/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
1
# Copyright (C) 2011 Canonical Ltd
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
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.
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
21
22
This module only handles bzrlib objects that use strings not directly wrapped
23
by a gettext() call. To generate a complete translation template file, this
24
output needs to be combined with that of xgettext or a similar command for
25
extracting those strings, as is done in the bzr Makefile. Sorting the output
26
is also left to that stage of the process.
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
27
"""
28
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
29
import inspect
30
import os
31
32
from bzrlib import (
33
    commands as _mod_commands,
34
    errors,
35
    help_topics,
36
    plugin,
6110.7.1 by Jonathan Riddell
add topic help to translations
37
    help,
5830.2.15 by INADA Naoki
Add debug trace.
38
    )
39
from bzrlib.trace import (
40
    mutter,
41
    note,
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
42
    )
6138.4.4 by Jonathan Riddell
update bzr.pot
43
from bzrlib.i18n import gettext
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
44
45
46
def _escape(s):
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
47
    s = (s.replace('\\', '\\\\')
48
        .replace('\n', '\\n')
49
        .replace('\r', '\\r')
50
        .replace('\t', '\\t')
51
        .replace('"', '\\"')
52
        )
53
    return s
54
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
55
def _normalize(s):
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
56
    # This converts the various Python string types into a format that
57
    # is appropriate for .po files, namely much closer to C style.
58
    lines = s.split('\n')
59
    if len(lines) == 1:
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
60
        s = '"' + _escape(s) + '"'
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
61
    else:
62
        if not lines[-1]:
63
            del lines[-1]
64
            lines[-1] = lines[-1] + '\n'
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
65
        lines = map(_escape, lines)
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
66
        lineterm = '\\n"\n"'
67
        s = '""\n"' + lineterm.join(lines) + '"'
68
    return s
69
70
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
71
class _PotExporter(object):
72
    """Write message details to output stream in .pot file format"""
73
74
    def __init__(self, outf):
75
        self.outf = outf
76
        self._msgids = set()
77
78
    def poentry(self, path, lineno, s, comment=None):
79
        if s in self._msgids:
80
            return
81
        self._msgids.add(s)
82
        if comment is None:
83
            comment = ''
84
        else:
85
            comment = "# %s\n" % comment
86
        mutter("Exporting msg %r at line %d in %r", s[:20], lineno, path)
87
        self.outf.write(
88
            "#: {path}:{lineno}\n"
89
            "{comment}"
90
            "msgid {msg}\n"
91
            "msgstr \"\"\n"
92
            "\n".format(
93
                path=path, lineno=lineno, comment=comment, msg=_normalize(s)))
94
95
    def poentry_per_paragraph(self, path, lineno, msgid, include=None):
96
        # TODO: How to split long help?
97
        paragraphs = msgid.split('\n\n')
98
        if include is not None:
99
            paragraphs = filter(include, paragraphs)
100
        for p in paragraphs:
101
            self.poentry(path, lineno, p)
102
            lineno += p.count('\n') + 2
103
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
104
5830.2.19 by INADA Naoki
Implement ast based offset detection.
105
_LAST_CACHE = _LAST_CACHED_SRC = None
106
107
def _offsets_of_literal(src):
108
    global _LAST_CACHE, _LAST_CACHED_SRC
109
    if src == _LAST_CACHED_SRC:
110
        return _LAST_CACHE.copy()
111
112
    import ast
113
    root = ast.parse(src)
114
    offsets = {}
115
    for node in ast.walk(root):
116
        if not isinstance(node, ast.Str):
117
            continue
5830.2.21 by INADA Naoki
Fix lineno of command help in pot
118
        offsets[node.s] = node.lineno - node.s.count('\n')
5830.2.19 by INADA Naoki
Implement ast based offset detection.
119
120
    _LAST_CACHED_SRC = src
121
    _LAST_CACHE = offsets.copy()
122
    return offsets
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
123
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
124
def _standard_options(exporter):
5830.2.17 by INADA Naoki
Split command specific options and standard options.
125
    from bzrlib.option import Option
126
    src = inspect.findsource(Option)[0]
127
    src = ''.join(src)
128
    path = 'bzrlib/option.py'
5830.2.19 by INADA Naoki
Implement ast based offset detection.
129
    offsets = _offsets_of_literal(src)
5830.2.17 by INADA Naoki
Split command specific options and standard options.
130
131
    for name in sorted(Option.OPTIONS.keys()):
132
        opt = Option.OPTIONS[name]
133
        if getattr(opt, 'hidden', False):
134
            continue
135
        if getattr(opt, 'title', None):
5830.2.19 by INADA Naoki
Implement ast based offset detection.
136
            lineno = offsets.get(opt.title, 9999)
137
            if lineno == 9999:
6138.3.4 by Jonathan Riddell
add gettext() to uses of trace.note()
138
                note(gettext("%r is not found in bzrlib/option.py") % opt.title)
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
139
            exporter.poentry(path, lineno, opt.title,
5830.2.17 by INADA Naoki
Split command specific options and standard options.
140
                     'title of %r option' % name)
141
        if getattr(opt, 'help', None):
5830.2.19 by INADA Naoki
Implement ast based offset detection.
142
            lineno = offsets.get(opt.help, 9999)
143
            if lineno == 9999:
6138.3.4 by Jonathan Riddell
add gettext() to uses of trace.note()
144
                note(gettext("%r is not found in bzrlib/option.py") % opt.help)
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
145
            exporter.poentry(path, lineno, opt.help,
5830.2.17 by INADA Naoki
Split command specific options and standard options.
146
                     'help of %r option' % name)
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
147
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
148
def _command_options(exporter, path, cmd):
5830.2.17 by INADA Naoki
Split command specific options and standard options.
149
    src, default_lineno = inspect.findsource(cmd.__class__)
5830.2.19 by INADA Naoki
Implement ast based offset detection.
150
    offsets = _offsets_of_literal(''.join(src))
5830.2.17 by INADA Naoki
Split command specific options and standard options.
151
    for opt in cmd.takes_options:
152
        if isinstance(opt, str):
153
            continue
154
        if getattr(opt, 'hidden', False):
155
            continue
156
        name = opt.name
157
        if getattr(opt, 'title', None):
5830.2.19 by INADA Naoki
Implement ast based offset detection.
158
            lineno = offsets.get(opt.title, default_lineno)
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
159
            exporter.poentry(path, lineno, opt.title,
5830.2.17 by INADA Naoki
Split command specific options and standard options.
160
                     'title of %r option of %r command' % (name, cmd.name()))
161
        if getattr(opt, 'help', None):
5830.2.19 by INADA Naoki
Implement ast based offset detection.
162
            lineno = offsets.get(opt.help, default_lineno)
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
163
            exporter.poentry(path, lineno, opt.help,
5830.2.17 by INADA Naoki
Split command specific options and standard options.
164
                     'help of %r option of %r command' % (name, cmd.name()))
5830.2.3 by INADA Naoki
bzrgettext extracts help message of command option too.
165
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
166
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
167
def _write_command_help(exporter, cmd):
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
168
    path = inspect.getfile(cmd.__class__)
169
    if path.endswith('.pyc'):
170
        path = path[:-1]
171
    path = os.path.relpath(path)
5830.2.21 by INADA Naoki
Fix lineno of command help in pot
172
    src, lineno = inspect.findsource(cmd.__class__)
173
    offsets = _offsets_of_literal(''.join(src))
174
    lineno = offsets[cmd.__doc__]
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
175
    doc = inspect.getdoc(cmd)
176
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
177
    def exclude_usage(p):
5875.3.20 by INADA Naoki
Add test for exporting command help.
178
        # ':Usage:' has special meaning in help topics.
179
        # This is usage example of command and should not be translated.
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
180
        if p.splitlines()[0] != ':Usage:':
5875.3.20 by INADA Naoki
Add test for exporting command help.
181
            return True
182
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
183
    exporter.poentry_per_paragraph(path, lineno, doc, exclude_usage)
184
    _command_options(exporter, path, cmd)
185
186
187
def _command_helps(exporter, plugin_name=None):
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
188
    """Extract docstrings from path.
189
190
    This respects the Bazaar cmdtable/table convention and will
191
    only extract docstrings from functions mentioned in these tables.
192
    """
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
193
    from glob import glob
194
195
    # builtin commands
196
    for cmd_name in _mod_commands.builtin_command_names():
5830.2.16 by INADA Naoki
Skip hidden commands to focus important commands.
197
        command = _mod_commands.get_cmd_object(cmd_name, False)
198
        if command.hidden:
199
            continue
6162.4.5 by Jonathan Riddell
change export-pot --plugins option to --plugin which takes a plugin name rather than command name
200
        if plugin_name is not None:
6162.4.3 by Jonathan Riddell
add new option 'plugins' to 'export-pot' to export strings from given plugin commands help
201
            # only export builtins if we are not exporting plugin commands
202
            continue
6138.3.4 by Jonathan Riddell
add gettext() to uses of trace.note()
203
        note(gettext("Exporting messages from builtin command: %s"), cmd_name)
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
204
        _write_command_help(exporter, command)
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
205
5830.2.14 by INADA Naoki
Cleanup import
206
    plugin_path = plugin.get_core_plugin_path()
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
207
    core_plugins = glob(plugin_path + '/*/__init__.py')
208
    core_plugins = [os.path.basename(os.path.dirname(p))
209
                        for p in core_plugins]
6162.4.3 by Jonathan Riddell
add new option 'plugins' to 'export-pot' to export strings from given plugin commands help
210
    # plugins
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
211
    for cmd_name in _mod_commands.plugin_command_names():
212
        command = _mod_commands.get_cmd_object(cmd_name, False)
5830.2.16 by INADA Naoki
Skip hidden commands to focus important commands.
213
        if command.hidden:
214
            continue
6162.4.5 by Jonathan Riddell
change export-pot --plugins option to --plugin which takes a plugin name rather than command name
215
        if plugin_name is not None and command.plugin_name() != plugin_name:
6162.4.3 by Jonathan Riddell
add new option 'plugins' to 'export-pot' to export strings from given plugin commands help
216
            # if we are exporting plugin commands, skip plugins we have not specified.
217
            continue
6162.4.5 by Jonathan Riddell
change export-pot --plugins option to --plugin which takes a plugin name rather than command name
218
        if plugin_name is None and command.plugin_name() not in core_plugins:
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
219
            # skip non-core plugins
220
            # TODO: Support extracting from third party plugins.
221
            continue
6138.3.4 by Jonathan Riddell
add gettext() to uses of trace.note()
222
        note(gettext("Exporting messages from plugin command: {0} in {1}").format(
223
             cmd_name, command.plugin_name() ))
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
224
        _write_command_help(exporter, command)
225
226
227
def _error_messages(exporter):
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
228
    """Extract fmt string from bzrlib.errors."""
5830.2.22 by INADA Naoki
Fix line no of error formats in bzr.pot
229
    path = errors.__file__
230
    if path.endswith('.pyc'):
231
        path = path[:-1]
232
    offsets = _offsets_of_literal(open(path).read())
233
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
234
    base_klass = errors.BzrError
235
    for name in dir(errors):
236
        klass = getattr(errors, name)
237
        if not inspect.isclass(klass):
238
            continue
239
        if not issubclass(klass, base_klass):
240
            continue
241
        if klass is base_klass:
242
            continue
243
        if klass.internal_error:
244
            continue
245
        fmt = getattr(klass, "_fmt", None)
246
        if fmt:
6138.3.4 by Jonathan Riddell
add gettext() to uses of trace.note()
247
            note(gettext("Exporting message from error: %s"), name)
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
248
            exporter.poentry('bzrlib/errors.py',
5830.2.22 by INADA Naoki
Fix line no of error formats in bzr.pot
249
                     offsets.get(fmt, 9999), fmt)
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
250
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
251
def _help_topics(exporter):
5830.2.14 by INADA Naoki
Cleanup import
252
    topic_registry = help_topics.topic_registry
5830.2.9 by INADA Naoki
Export from help_topics that is directly registered into
253
    for key in topic_registry.keys():
254
        doc = topic_registry.get(key)
255
        if isinstance(doc, str):
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
256
            exporter.poentry_per_paragraph(
5830.2.11 by INADA Naoki
Change dummy file path for help topics.
257
                    'dummy/help_topics/'+key+'/detail.txt',
258
                    1, doc)
6110.7.2 by Jonathan Riddell
check for callable
259
        elif callable(doc): # help topics from files
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
260
            exporter.poentry_per_paragraph(
6110.7.1 by Jonathan Riddell
add topic help to translations
261
                    'en/help_topics/'+key+'.txt',
262
                    1, doc(key))
5830.2.9 by INADA Naoki
Export from help_topics that is directly registered into
263
        summary = topic_registry.get_summary(key)
264
        if summary is not None:
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
265
            exporter.poentry('dummy/help_topics/'+key+'/summary.txt',
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
266
                     1, summary)
267
6162.4.5 by Jonathan Riddell
change export-pot --plugins option to --plugin which takes a plugin name rather than command name
268
def export_pot(outf, plugin=None):
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
269
    exporter = _PotExporter(outf)
6162.4.5 by Jonathan Riddell
change export-pot --plugins option to --plugin which takes a plugin name rather than command name
270
    if plugin is None:
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
271
        _standard_options(exporter)
272
        _command_helps(exporter)
273
        _error_messages(exporter)
274
        _help_topics(exporter)
6162.4.3 by Jonathan Riddell
add new option 'plugins' to 'export-pot' to export strings from given plugin commands help
275
    else:
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
276
        _command_helps(exporter, plugin)