~bzr-pqm/bzr/bzr.dev

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.
21
"""
22
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
23
import inspect
24
import os
25
26
from bzrlib import (
27
    commands as _mod_commands,
28
    errors,
29
    help_topics,
30
    plugin,
6110.7.1 by Jonathan Riddell
add topic help to translations
31
    help,
5830.2.15 by INADA Naoki
Add debug trace.
32
    )
33
from bzrlib.trace import (
34
    mutter,
35
    note,
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
36
    )
37
38
39
def _escape(s):
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
40
    s = (s.replace('\\', '\\\\')
41
        .replace('\n', '\\n')
42
        .replace('\r', '\\r')
43
        .replace('\t', '\\t')
44
        .replace('"', '\\"')
45
        )
46
    return s
47
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
48
def _normalize(s):
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
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:
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
53
        s = '"' + _escape(s) + '"'
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
54
    else:
55
        if not lines[-1]:
56
            del lines[-1]
57
            lines[-1] = lines[-1] + '\n'
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
58
        lines = map(_escape, lines)
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
59
        lineterm = '\\n"\n"'
60
        s = '""\n"' + lineterm.join(lines) + '"'
61
    return s
62
63
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
64
_FOUND_MSGID = None # set by entry function.
5830.2.3 by INADA Naoki
bzrgettext extracts help message of command option too.
65
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
66
def _poentry(outf, path, lineno, s, comment=None):
67
    if s in _FOUND_MSGID:
5830.2.3 by INADA Naoki
bzrgettext extracts help message of command option too.
68
        return
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
69
    _FOUND_MSGID.add(s)
5830.2.3 by INADA Naoki
bzrgettext extracts help message of command option too.
70
    if comment is None:
71
        comment = ''
72
    else:
73
        comment = "# %s\n" % comment
5830.2.15 by INADA Naoki
Add debug trace.
74
    mutter("Exporting msg %r at line %d in %r", s[:20], lineno, path)
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
75
    print >>outf, ('#: %s:%d\n' % (path, lineno) +
5830.2.3 by INADA Naoki
bzrgettext extracts help message of command option too.
76
           comment+
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
77
           'msgid %s\n' % _normalize(s) +
5830.2.3 by INADA Naoki
bzrgettext extracts help message of command option too.
78
           'msgstr ""\n')
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
79
5875.3.20 by INADA Naoki
Add test for exporting command help.
80
def _poentry_per_paragraph(outf, path, lineno, msgid, filter=lambda x: False):
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
81
    # TODO: How to split long help?
5830.2.2 by INADA Naoki
Split command's docstring per paragraph.
82
    paragraphs = msgid.split('\n\n')
83
    for p in paragraphs:
5875.3.20 by INADA Naoki
Add test for exporting command help.
84
        if filter(p):
5875.3.4 by INADA Naoki
Don't translate :Usage: sections.
85
            continue
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
86
        _poentry(outf, path, lineno, p)
5830.2.2 by INADA Naoki
Split command's docstring per paragraph.
87
        lineno += p.count('\n') + 2
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
88
5830.2.19 by INADA Naoki
Implement ast based offset detection.
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
5830.2.21 by INADA Naoki
Fix lineno of command help in pot
102
        offsets[node.s] = node.lineno - node.s.count('\n')
5830.2.19 by INADA Naoki
Implement ast based offset detection.
103
104
    _LAST_CACHED_SRC = src
105
    _LAST_CACHE = offsets.copy()
106
    return offsets
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
107
5830.2.17 by INADA Naoki
Split command specific options and standard options.
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'
5830.2.19 by INADA Naoki
Implement ast based offset detection.
113
    offsets = _offsets_of_literal(src)
5830.2.17 by INADA Naoki
Split command specific options and standard options.
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):
5830.2.19 by INADA Naoki
Implement ast based offset detection.
120
            lineno = offsets.get(opt.title, 9999)
121
            if lineno == 9999:
122
                note("%r is not found in bzrlib/option.py" % opt.title)
5830.2.17 by INADA Naoki
Split command specific options and standard options.
123
            _poentry(outf, path, lineno, opt.title,
124
                     'title of %r option' % name)
125
        if getattr(opt, 'help', None):
5830.2.19 by INADA Naoki
Implement ast based offset detection.
126
            lineno = offsets.get(opt.help, 9999)
127
            if lineno == 9999:
128
                note("%r is not found in bzrlib/option.py" % opt.help)
5830.2.17 by INADA Naoki
Split command specific options and standard options.
129
            _poentry(outf, path, lineno, opt.help,
130
                     'help of %r option' % name)
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
131
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
132
def _command_options(outf, path, cmd):
5830.2.17 by INADA Naoki
Split command specific options and standard options.
133
    src, default_lineno = inspect.findsource(cmd.__class__)
5830.2.19 by INADA Naoki
Implement ast based offset detection.
134
    offsets = _offsets_of_literal(''.join(src))
5830.2.17 by INADA Naoki
Split command specific options and standard options.
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):
5830.2.19 by INADA Naoki
Implement ast based offset detection.
142
            lineno = offsets.get(opt.title, default_lineno)
5830.2.17 by INADA Naoki
Split command specific options and standard options.
143
            _poentry(outf, path, lineno, opt.title,
144
                     'title of %r option of %r command' % (name, cmd.name()))
145
        if getattr(opt, 'help', None):
5830.2.19 by INADA Naoki
Implement ast based offset detection.
146
            lineno = offsets.get(opt.help, default_lineno)
5830.2.17 by INADA Naoki
Split command specific options and standard options.
147
            _poentry(outf, path, lineno, opt.help,
148
                     'help of %r option of %r command' % (name, cmd.name()))
5830.2.3 by INADA Naoki
bzrgettext extracts help message of command option too.
149
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
150
5875.3.20 by INADA Naoki
Add test for exporting command help.
151
def _write_command_help(outf, cmd):
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
152
    path = inspect.getfile(cmd.__class__)
153
    if path.endswith('.pyc'):
154
        path = path[:-1]
155
    path = os.path.relpath(path)
5830.2.21 by INADA Naoki
Fix lineno of command help in pot
156
    src, lineno = inspect.findsource(cmd.__class__)
157
    offsets = _offsets_of_literal(''.join(src))
158
    lineno = offsets[cmd.__doc__]
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
159
    doc = inspect.getdoc(cmd)
160
5875.3.20 by INADA Naoki
Add test for exporting command help.
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)
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
168
    _command_options(outf, path, cmd)
169
5875.3.20 by INADA Naoki
Add test for exporting command help.
170
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
171
def _command_helps(outf):
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
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
    """
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
177
    from glob import glob
178
179
    # builtin commands
180
    for cmd_name in _mod_commands.builtin_command_names():
5830.2.16 by INADA Naoki
Skip hidden commands to focus important commands.
181
        command = _mod_commands.get_cmd_object(cmd_name, False)
182
        if command.hidden:
183
            continue
5830.2.15 by INADA Naoki
Add debug trace.
184
        note("Exporting messages from builtin command: %s", cmd_name)
5875.3.20 by INADA Naoki
Add test for exporting command help.
185
        _write_command_help(outf, command)
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
186
5830.2.14 by INADA Naoki
Cleanup import
187
    plugin_path = plugin.get_core_plugin_path()
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
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)
5830.2.16 by INADA Naoki
Skip hidden commands to focus important commands.
194
        if command.hidden:
195
            continue
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
196
        if command.plugin_name() not in core_plugins:
197
            # skip non-core plugins
198
            # TODO: Support extracting from third party plugins.
199
            continue
5830.2.15 by INADA Naoki
Add debug trace.
200
        note("Exporting messages from plugin command: %s in %s",
201
             cmd_name, command.plugin_name())
5875.3.20 by INADA Naoki
Add test for exporting command help.
202
        _write_command_help(outf, command)
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
203
204
205
def _error_messages(outf):
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
206
    """Extract fmt string from bzrlib.errors."""
5830.2.22 by INADA Naoki
Fix line no of error formats in bzr.pot
207
    path = errors.__file__
208
    if path.endswith('.pyc'):
209
        path = path[:-1]
210
    offsets = _offsets_of_literal(open(path).read())
211
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
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:
5830.2.15 by INADA Naoki
Add debug trace.
225
            note("Exporting message from error: %s", name)
226
            _poentry(outf, 'bzrlib/errors.py',
5830.2.22 by INADA Naoki
Fix line no of error formats in bzr.pot
227
                     offsets.get(fmt, 9999), fmt)
5830.2.1 by INADA Naoki
Add update-pot command to Makefile and tools/bzrgettext script that
228
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
229
def _help_topics(outf):
5830.2.14 by INADA Naoki
Cleanup import
230
    topic_registry = help_topics.topic_registry
5830.2.9 by INADA Naoki
Export from help_topics that is directly registered into
231
    for key in topic_registry.keys():
232
        doc = topic_registry.get(key)
233
        if isinstance(doc, str):
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
234
            _poentry_per_paragraph(
235
                    outf,
5830.2.11 by INADA Naoki
Change dummy file path for help topics.
236
                    'dummy/help_topics/'+key+'/detail.txt',
237
                    1, doc)
6110.7.2 by Jonathan Riddell
check for callable
238
        elif callable(doc): # help topics from files
6110.7.1 by Jonathan Riddell
add topic help to translations
239
            _poentry_per_paragraph(
240
                    outf,
241
                    'en/help_topics/'+key+'.txt',
242
                    1, doc(key))
5830.2.9 by INADA Naoki
Export from help_topics that is directly registered into
243
        summary = topic_registry.get_summary(key)
244
        if summary is not None:
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
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()
5830.2.17 by INADA Naoki
Split command specific options and standard options.
251
    _standard_options(outf)
5830.2.12 by INADA Naoki
Make "export-pot" hidden command.
252
    _command_helps(outf)
253
    _error_messages(outf)
6110.7.1 by Jonathan Riddell
add topic help to translations
254
    _help_topics(outf)