~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/export_pot.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-03-13 23:45:11 UTC
  • mfrom: (3272.1.1 ianc-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080313234511-fkj5oa8gm3nrfcro
(Neil Martinsen-Burrell) Explain version-info --custom in the User
        Guide

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2011 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
# The normalize function is taken from pygettext which is distributed
18
 
# with Python under the Python License, which is GPL compatible.
19
 
 
20
 
"""Extract docstrings from Bazaar commands.
21
 
"""
22
 
 
23
 
import inspect
24
 
import os
25
 
 
26
 
from bzrlib import (
27
 
    commands as _mod_commands,
28
 
    errors,
29
 
    help_topics,
30
 
    plugin,
31
 
    help,
32
 
    )
33
 
from bzrlib.trace import (
34
 
    mutter,
35
 
    note,
36
 
    )
37
 
 
38
 
 
39
 
def _escape(s):
40
 
    s = (s.replace('\\', '\\\\')
41
 
        .replace('\n', '\\n')
42
 
        .replace('\r', '\\r')
43
 
        .replace('\t', '\\t')
44
 
        .replace('"', '\\"')
45
 
        )
46
 
    return s
47
 
 
48
 
def _normalize(s):
49
 
    # This converts the various Python string types into a format that
50
 
    # is appropriate for .po files, namely much closer to C style.
51
 
    lines = s.split('\n')
52
 
    if len(lines) == 1:
53
 
        s = '"' + _escape(s) + '"'
54
 
    else:
55
 
        if not lines[-1]:
56
 
            del lines[-1]
57
 
            lines[-1] = lines[-1] + '\n'
58
 
        lines = map(_escape, lines)
59
 
        lineterm = '\\n"\n"'
60
 
        s = '""\n"' + lineterm.join(lines) + '"'
61
 
    return s
62
 
 
63
 
 
64
 
_FOUND_MSGID = None # set by entry function.
65
 
 
66
 
def _poentry(outf, path, lineno, s, comment=None):
67
 
    if s in _FOUND_MSGID:
68
 
        return
69
 
    _FOUND_MSGID.add(s)
70
 
    if comment is None:
71
 
        comment = ''
72
 
    else:
73
 
        comment = "# %s\n" % comment
74
 
    mutter("Exporting msg %r at line %d in %r", s[:20], lineno, path)
75
 
    print >>outf, ('#: %s:%d\n' % (path, lineno) +
76
 
           comment+
77
 
           'msgid %s\n' % _normalize(s) +
78
 
           'msgstr ""\n')
79
 
 
80
 
def _poentry_per_paragraph(outf, path, lineno, msgid, filter=lambda x: False):
81
 
    # TODO: How to split long help?
82
 
    paragraphs = msgid.split('\n\n')
83
 
    for p in paragraphs:
84
 
        if filter(p):
85
 
            continue
86
 
        _poentry(outf, path, lineno, p)
87
 
        lineno += p.count('\n') + 2
88
 
 
89
 
_LAST_CACHE = _LAST_CACHED_SRC = None
90
 
 
91
 
def _offsets_of_literal(src):
92
 
    global _LAST_CACHE, _LAST_CACHED_SRC
93
 
    if src == _LAST_CACHED_SRC:
94
 
        return _LAST_CACHE.copy()
95
 
 
96
 
    import ast
97
 
    root = ast.parse(src)
98
 
    offsets = {}
99
 
    for node in ast.walk(root):
100
 
        if not isinstance(node, ast.Str):
101
 
            continue
102
 
        offsets[node.s] = node.lineno - node.s.count('\n')
103
 
 
104
 
    _LAST_CACHED_SRC = src
105
 
    _LAST_CACHE = offsets.copy()
106
 
    return offsets
107
 
 
108
 
def _standard_options(outf):
109
 
    from bzrlib.option import Option
110
 
    src = inspect.findsource(Option)[0]
111
 
    src = ''.join(src)
112
 
    path = 'bzrlib/option.py'
113
 
    offsets = _offsets_of_literal(src)
114
 
 
115
 
    for name in sorted(Option.OPTIONS.keys()):
116
 
        opt = Option.OPTIONS[name]
117
 
        if getattr(opt, 'hidden', False):
118
 
            continue
119
 
        if getattr(opt, 'title', None):
120
 
            lineno = offsets.get(opt.title, 9999)
121
 
            if lineno == 9999:
122
 
                note("%r is not found in bzrlib/option.py" % opt.title)
123
 
            _poentry(outf, path, lineno, opt.title,
124
 
                     'title of %r option' % name)
125
 
        if getattr(opt, 'help', None):
126
 
            lineno = offsets.get(opt.help, 9999)
127
 
            if lineno == 9999:
128
 
                note("%r is not found in bzrlib/option.py" % opt.help)
129
 
            _poentry(outf, path, lineno, opt.help,
130
 
                     'help of %r option' % name)
131
 
 
132
 
def _command_options(outf, path, cmd):
133
 
    src, default_lineno = inspect.findsource(cmd.__class__)
134
 
    offsets = _offsets_of_literal(''.join(src))
135
 
    for opt in cmd.takes_options:
136
 
        if isinstance(opt, str):
137
 
            continue
138
 
        if getattr(opt, 'hidden', False):
139
 
            continue
140
 
        name = opt.name
141
 
        if getattr(opt, 'title', None):
142
 
            lineno = offsets.get(opt.title, default_lineno)
143
 
            _poentry(outf, path, lineno, opt.title,
144
 
                     'title of %r option of %r command' % (name, cmd.name()))
145
 
        if getattr(opt, 'help', None):
146
 
            lineno = offsets.get(opt.help, default_lineno)
147
 
            _poentry(outf, path, lineno, opt.help,
148
 
                     'help of %r option of %r command' % (name, cmd.name()))
149
 
 
150
 
 
151
 
def _write_command_help(outf, cmd):
152
 
    path = inspect.getfile(cmd.__class__)
153
 
    if path.endswith('.pyc'):
154
 
        path = path[:-1]
155
 
    path = os.path.relpath(path)
156
 
    src, lineno = inspect.findsource(cmd.__class__)
157
 
    offsets = _offsets_of_literal(''.join(src))
158
 
    lineno = offsets[cmd.__doc__]
159
 
    doc = inspect.getdoc(cmd)
160
 
 
161
 
    def filter(p):
162
 
        # ':Usage:' has special meaning in help topics.
163
 
        # This is usage example of command and should not be translated.
164
 
        if p.splitlines()[0] == ':Usage:':
165
 
            return True
166
 
 
167
 
    _poentry_per_paragraph(outf, path, lineno, doc, filter)
168
 
    _command_options(outf, path, cmd)
169
 
 
170
 
 
171
 
def _command_helps(outf):
172
 
    """Extract docstrings from path.
173
 
 
174
 
    This respects the Bazaar cmdtable/table convention and will
175
 
    only extract docstrings from functions mentioned in these tables.
176
 
    """
177
 
    from glob import glob
178
 
 
179
 
    # builtin commands
180
 
    for cmd_name in _mod_commands.builtin_command_names():
181
 
        command = _mod_commands.get_cmd_object(cmd_name, False)
182
 
        if command.hidden:
183
 
            continue
184
 
        note("Exporting messages from builtin command: %s", cmd_name)
185
 
        _write_command_help(outf, command)
186
 
 
187
 
    plugin_path = plugin.get_core_plugin_path()
188
 
    core_plugins = glob(plugin_path + '/*/__init__.py')
189
 
    core_plugins = [os.path.basename(os.path.dirname(p))
190
 
                        for p in core_plugins]
191
 
    # core plugins
192
 
    for cmd_name in _mod_commands.plugin_command_names():
193
 
        command = _mod_commands.get_cmd_object(cmd_name, False)
194
 
        if command.hidden:
195
 
            continue
196
 
        if command.plugin_name() not in core_plugins:
197
 
            # skip non-core plugins
198
 
            # TODO: Support extracting from third party plugins.
199
 
            continue
200
 
        note("Exporting messages from plugin command: %s in %s",
201
 
             cmd_name, command.plugin_name())
202
 
        _write_command_help(outf, command)
203
 
 
204
 
 
205
 
def _error_messages(outf):
206
 
    """Extract fmt string from bzrlib.errors."""
207
 
    path = errors.__file__
208
 
    if path.endswith('.pyc'):
209
 
        path = path[:-1]
210
 
    offsets = _offsets_of_literal(open(path).read())
211
 
 
212
 
    base_klass = errors.BzrError
213
 
    for name in dir(errors):
214
 
        klass = getattr(errors, name)
215
 
        if not inspect.isclass(klass):
216
 
            continue
217
 
        if not issubclass(klass, base_klass):
218
 
            continue
219
 
        if klass is base_klass:
220
 
            continue
221
 
        if klass.internal_error:
222
 
            continue
223
 
        fmt = getattr(klass, "_fmt", None)
224
 
        if fmt:
225
 
            note("Exporting message from error: %s", name)
226
 
            _poentry(outf, 'bzrlib/errors.py',
227
 
                     offsets.get(fmt, 9999), fmt)
228
 
 
229
 
def _help_topics(outf):
230
 
    topic_registry = help_topics.topic_registry
231
 
    for key in topic_registry.keys():
232
 
        doc = topic_registry.get(key)
233
 
        if isinstance(doc, str):
234
 
            _poentry_per_paragraph(
235
 
                    outf,
236
 
                    'dummy/help_topics/'+key+'/detail.txt',
237
 
                    1, doc)
238
 
        elif callable(doc): # help topics from files
239
 
            _poentry_per_paragraph(
240
 
                    outf,
241
 
                    'en/help_topics/'+key+'.txt',
242
 
                    1, doc(key))
243
 
        summary = topic_registry.get_summary(key)
244
 
        if summary is not None:
245
 
            _poentry(outf, 'dummy/help_topics/'+key+'/summary.txt',
246
 
                     1, summary)
247
 
 
248
 
def export_pot(outf):
249
 
    global _FOUND_MSGID
250
 
    _FOUND_MSGID = set()
251
 
    _standard_options(outf)
252
 
    _command_helps(outf)
253
 
    _error_messages(outf)
254
 
    _help_topics(outf)