/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to brzlib/export_pot.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-21 12:41:27 UTC
  • mto: This revision was merged to the branch mainline in revision 6623.
  • Revision ID: jelmer@jelmer.uk-20170521124127-iv8etg0vwymyai6y
s/bzr/brz/ in apport config.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
"""Extract docstrings from Bazaar commands.
21
21
 
22
 
This module only handles breezy objects that use strings not directly wrapped
 
22
This module only handles brzlib objects that use strings not directly wrapped
23
23
by a gettext() call. To generate a complete translation template file, this
24
24
output needs to be combined with that of xgettext or a similar command for
25
25
extracting those strings, as is done in the bzr Makefile. Sorting the output
26
26
is also left to that stage of the process.
27
27
"""
28
28
 
 
29
from __future__ import absolute_import
 
30
 
29
31
import inspect
30
32
import os
31
 
import sys
32
33
 
33
 
import breezy
34
 
from . import (
 
34
from brzlib import (
35
35
    commands as _mod_commands,
36
36
    errors,
37
37
    help_topics,
38
38
    option,
39
 
    plugin as _mod_plugin,
 
39
    plugin,
 
40
    help,
40
41
    )
41
 
from .trace import (
 
42
from brzlib.trace import (
42
43
    mutter,
43
44
    note,
44
45
    )
45
 
from .i18n import gettext
 
46
from brzlib.i18n import gettext
46
47
 
47
48
 
48
49
def _escape(s):
49
50
    s = (s.replace('\\', '\\\\')
50
 
         .replace('\n', '\\n')
51
 
         .replace('\r', '\\r')
52
 
         .replace('\t', '\\t')
53
 
         .replace('"', '\\"')
54
 
         )
 
51
        .replace('\n', '\\n')
 
52
        .replace('\r', '\\r')
 
53
        .replace('\t', '\\t')
 
54
        .replace('"', '\\"')
 
55
        )
55
56
    return s
56
57
 
57
 
 
58
58
def _normalize(s):
59
59
    # This converts the various Python string types into a format that
60
60
    # is appropriate for .po files, namely much closer to C style.
65
65
        if not lines[-1]:
66
66
            del lines[-1]
67
67
            lines[-1] = lines[-1] + '\n'
 
68
        lines = map(_escape, lines)
68
69
        lineterm = '\\n"\n"'
69
 
        s = '""\n"' + lineterm.join(map(_escape, lines)) + '"'
 
70
        s = '""\n"' + lineterm.join(lines) + '"'
70
71
    return s
71
72
 
72
73
 
73
 
def _parse_source(source_text, filename='<unknown>'):
 
74
def _parse_source(source_text):
74
75
    """Get object to lineno mappings from given source_text"""
75
76
    import ast
76
77
    cls_to_lineno = {}
77
78
    str_to_lineno = {}
78
 
    for node in ast.walk(ast.parse(source_text, filename)):
 
79
    for node in ast.walk(ast.parse(source_text)):
79
80
        # TODO: worry about duplicates?
80
81
        if isinstance(node, ast.ClassDef):
81
82
            # TODO: worry about nesting?
85
86
            # string terminates on. It's more useful to have the line the
86
87
            # string begins on. Unfortunately, counting back newlines is
87
88
            # only an approximation as the AST is ignorant of escaping.
88
 
            str_to_lineno[node.s] = node.lineno - (0 if sys.version_info >= (3, 8) else node.s.count('\n'))
 
89
            str_to_lineno[node.s] = node.lineno - node.s.count('\n')
89
90
    return cls_to_lineno, str_to_lineno
90
91
 
91
92
 
105
106
        # TODO: fix this to do the right thing rather than rely on cwd
106
107
        relpath = os.path.relpath(sourcepath)
107
108
        return cls(relpath,
108
 
                   _source_info=_parse_source("".join(inspect.findsource(module)[0]), module.__file__))
 
109
            _source_info=_parse_source("".join(inspect.findsource(module)[0])))
109
110
 
110
111
    def from_class(self, cls):
111
112
        """Get new context with same details but lineno of class in source"""
115
116
            mutter("Definition of %r not found in %r", cls, self.path)
116
117
            return self
117
118
        return self.__class__(self.path, lineno,
118
 
                              (self._cls_to_lineno, self._str_to_lineno))
 
119
            (self._cls_to_lineno, self._str_to_lineno))
119
120
 
120
121
    def from_string(self, string):
121
122
        """Get new context with same details but lineno of string in source"""
125
126
            mutter("String %r not found in %r", string[:20], self.path)
126
127
            return self
127
128
        return self.__class__(self.path, lineno,
128
 
                              (self._cls_to_lineno, self._str_to_lineno))
 
129
            (self._cls_to_lineno, self._str_to_lineno))
129
130
 
130
131
 
131
132
class _PotExporter(object):
149
150
        else:
150
151
            comment = "# %s\n" % comment
151
152
        mutter("Exporting msg %r at line %d in %r", s[:20], lineno, path)
152
 
        line = (
 
153
        self.outf.write(
153
154
            "#: {path}:{lineno}\n"
154
155
            "{comment}"
155
156
            "msgid {msg}\n"
156
157
            "msgstr \"\"\n"
157
158
            "\n".format(
158
159
                path=path, lineno=lineno, comment=comment, msg=_normalize(s)))
159
 
        self.outf.write(line)
160
160
 
161
161
    def poentry_in_context(self, context, string, comment=None):
162
162
        context = context.from_string(string)
185
185
 
186
186
def _write_option(exporter, context, opt, note):
187
187
    if getattr(opt, 'hidden', False):
188
 
        return
 
188
        return   
189
189
    optname = opt.name
190
190
    if getattr(opt, 'title', None):
191
191
        exporter.poentry_in_context(context, opt.title,
192
 
                                    "title of {name!r} {what}".format(name=optname, what=note))
 
192
            "title of {name!r} {what}".format(name=optname, what=note))
193
193
    for name, _, _, helptxt in opt.iter_switches():
194
194
        if name != optname:
195
195
            if opt.is_hidden(name):
197
197
            name = "=".join([optname, name])
198
198
        if helptxt:
199
199
            exporter.poentry_in_context(context, helptxt,
200
 
                                        "help of {name!r} {what}".format(name=name, what=note))
 
200
                "help of {name!r} {what}".format(name=name, what=note))
201
201
 
202
202
 
203
203
def _standard_options(exporter):
204
204
    OPTIONS = option.Option.OPTIONS
205
205
    context = exporter.get_context(option)
206
 
    for name in sorted(OPTIONS):
 
206
    for name in sorted(OPTIONS.keys()):
207
207
        opt = OPTIONS[name]
208
208
        _write_option(exporter, context.from_string(name), opt, "option")
209
209
 
229
229
            return True
230
230
 
231
231
    exporter.poentry_per_paragraph(dcontext.path, dcontext.lineno, doc,
232
 
                                   exclude_usage)
 
232
        exclude_usage)
233
233
    _command_options(exporter, context, cmd)
234
234
 
235
235
 
239
239
    This respects the Bazaar cmdtable/table convention and will
240
240
    only extract docstrings from functions mentioned in these tables.
241
241
    """
 
242
    from glob import glob
242
243
 
243
244
    # builtin commands
244
245
    for cmd_name in _mod_commands.builtin_command_names():
251
252
        note(gettext("Exporting messages from builtin command: %s"), cmd_name)
252
253
        _write_command_help(exporter, command)
253
254
 
254
 
    plugins = _mod_plugin.plugins()
255
 
    if plugin_name is not None and plugin_name not in plugins:
256
 
        raise errors.BzrError(gettext('Plugin %s is not loaded' % plugin_name))
257
 
    core_plugins = set(
258
 
        name for name in plugins
259
 
        if plugins[name].path().startswith(breezy.__path__[0]))
 
255
    plugin_path = plugin.get_core_plugin_path()
 
256
    core_plugins = glob(plugin_path + '/*/__init__.py')
 
257
    core_plugins = [os.path.basename(os.path.dirname(p))
 
258
                        for p in core_plugins]
260
259
    # plugins
261
260
    for cmd_name in _mod_commands.plugin_command_names():
262
261
        command = _mod_commands.get_cmd_object(cmd_name, False)
263
262
        if command.hidden:
264
263
            continue
265
264
        if plugin_name is not None and command.plugin_name() != plugin_name:
266
 
            # if we are exporting plugin commands, skip plugins we have not
267
 
            # specified.
 
265
            # if we are exporting plugin commands, skip plugins we have not specified.
268
266
            continue
269
267
        if plugin_name is None and command.plugin_name() not in core_plugins:
270
268
            # skip non-core plugins
271
269
            # TODO: Support extracting from third party plugins.
272
270
            continue
273
271
        note(gettext("Exporting messages from plugin command: {0} in {1}").format(
274
 
             cmd_name, command.plugin_name()))
 
272
             cmd_name, command.plugin_name() ))
275
273
        _write_command_help(exporter, command)
276
274
 
277
275
 
278
276
def _error_messages(exporter):
279
 
    """Extract fmt string from breezy.errors."""
 
277
    """Extract fmt string from brzlib.errors."""
280
278
    context = exporter.get_context(errors)
281
279
    base_klass = errors.BzrError
282
280
    for name in dir(errors):
301
299
        doc = topic_registry.get(key)
302
300
        if isinstance(doc, str):
303
301
            exporter.poentry_per_paragraph(
304
 
                'dummy/help_topics/' + key + '/detail.txt',
305
 
                1, doc)
306
 
        elif callable(doc):  # help topics from files
 
302
                    'dummy/help_topics/'+key+'/detail.txt',
 
303
                    1, doc)
 
304
        elif callable(doc): # help topics from files
307
305
            exporter.poentry_per_paragraph(
308
 
                'en/help_topics/' + key + '.txt',
309
 
                1, doc(key))
 
306
                    'en/help_topics/'+key+'.txt',
 
307
                    1, doc(key))
310
308
        summary = topic_registry.get_summary(key)
311
309
        if summary is not None:
312
 
            exporter.poentry('dummy/help_topics/' + key + '/summary.txt',
313
 
                             1, summary)
 
310
            exporter.poentry('dummy/help_topics/'+key+'/summary.txt',
 
311
                     1, summary)
314
312
 
315
313
 
316
314
def export_pot(outf, plugin=None, include_duplicates=False):