~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/export_pot.py

  • Committer: INADA Naoki
  • Date: 2011-05-08 17:58:08 UTC
  • mto: (5830.3.4 i18n-msgfmt)
  • mto: This revision was merged to the branch mainline in revision 5873.
  • Revision ID: songofacandy@gmail.com-20110508175808-rzcv5h10vwwolr8j
Make "export-pot" hidden command.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
#
3
 
# bzrgettext - extract docstrings for Bazaar commands
4
 
#
5
 
# Copyright 2009 Matt Mackall <mpm@selenic.com> and others
6
 
# Copyright 2011 Canonical Ltd
 
1
# Copyright (C) 2011 Canonical Ltd
7
2
#
8
3
# This program is free software; you can redistribute it and/or modify
9
4
# it under the terms of the GNU General Public License as published by
19
14
# along with this program; if not, write to the Free Software
20
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
16
 
22
 
# This script is copied from mercurial/i18n/hggettext and modified
23
 
# for Bazaar.
24
 
 
25
17
# The normalize function is taken from pygettext which is distributed
26
18
# with Python under the Python License, which is GPL compatible.
27
19
 
28
 
from __future__ import with_statement
29
 
 
30
20
"""Extract docstrings from Bazaar commands.
31
21
"""
32
22
 
33
 
import os, sys, inspect
34
 
 
35
 
 
36
 
def escape(s):
 
23
import inspect
 
24
import os
 
25
import sys
 
26
 
 
27
from bzrlib import (
 
28
    commands as _mod_commands,
 
29
    errors,
 
30
    help_topics,
 
31
    osutils,
 
32
    plugin,
 
33
    )
 
34
 
 
35
 
 
36
def _escape(s):
37
37
    s = (s.replace('\\', '\\\\')
38
38
        .replace('\n', '\\n')
39
39
        .replace('\r', '\\r')
42
42
        )
43
43
    return s
44
44
 
45
 
 
46
 
def normalize(s):
 
45
def _normalize(s):
47
46
    # This converts the various Python string types into a format that
48
47
    # is appropriate for .po files, namely much closer to C style.
49
48
    lines = s.split('\n')
50
49
    if len(lines) == 1:
51
 
        s = '"' + escape(s) + '"'
 
50
        s = '"' + _escape(s) + '"'
52
51
    else:
53
52
        if not lines[-1]:
54
53
            del lines[-1]
55
54
            lines[-1] = lines[-1] + '\n'
56
 
        lines = map(escape, lines)
 
55
        lines = map(_escape, lines)
57
56
        lineterm = '\\n"\n"'
58
57
        s = '""\n"' + lineterm.join(lines) + '"'
59
58
    return s
60
59
 
61
60
 
62
 
MSGID_FOUND = set()
 
61
_FOUND_MSGID = None # set by entry function.
63
62
 
64
 
def poentry(path, lineno, s, comment=None):
65
 
    if s in MSGID_FOUND:
 
63
def _poentry(outf, path, lineno, s, comment=None):
 
64
    if s in _FOUND_MSGID:
66
65
        return
67
 
    MSGID_FOUND.add(s)
 
66
    _FOUND_MSGID.add(s)
68
67
    if comment is None:
69
68
        comment = ''
70
69
    else:
71
70
        comment = "# %s\n" % comment
72
 
    print ('#: %s:%d\n' % (path, lineno) +
 
71
    print >>outf, ('#: %s:%d\n' % (path, lineno) +
73
72
           comment+
74
 
           'msgid %s\n' % normalize(s) +
 
73
           'msgid %s\n' % _normalize(s) +
75
74
           'msgstr ""\n')
76
75
 
77
 
def poentry_per_paragraph(path, lineno, msgid):
 
76
def _poentry_per_paragraph(outf, path, lineno, msgid):
78
77
    paragraphs = msgid.split('\n\n')
79
78
    for p in paragraphs:
80
 
        poentry(path, lineno, p)
 
79
        _poentry(outf, path, lineno, p)
81
80
        lineno += p.count('\n') + 2
82
81
 
83
 
def offset(src, doc, name, default):
 
82
def _offset(src, doc, default):
84
83
    """Compute offset or issue a warning on stdout."""
85
84
    # Backslashes in doc appear doubled in src.
86
85
    end = src.find(doc.replace('\\', '\\\\'))
87
86
    if end == -1:
88
 
        # This can happen if the docstring contains unnecessary escape
89
 
        # sequences such as \" in a triple-quoted string. The problem
90
 
        # is that \" is turned into " and so doc wont appear in src.
91
 
        sys.stderr.write("warning: unknown offset in %s, assuming %d lines\n"
92
 
                         % (name, default))
93
87
        return default
94
88
    else:
95
89
        return src.count('\n', 0, end)
96
90
 
97
91
 
98
 
def importpath(path):
99
 
    """Import a path like foo/bar/baz.py and return the baz module."""
100
 
    if path.endswith('.py'):
101
 
        path = path[:-3]
102
 
    if path.endswith('/__init__'):
103
 
        path = path[:-9]
104
 
    path = path.replace('/', '.')
105
 
    mod = __import__(path)
106
 
    for comp in path.split('.')[1:]:
107
 
        mod = getattr(mod, comp)
108
 
    return mod
109
 
 
110
 
def options(path, lineno, cmdklass):
111
 
    cmd = cmdklass()
 
92
def _command_options(outf, path, cmd):
112
93
    for name, opt in cmd.options().iteritems():
113
 
        poentry(path, lineno, opt.help,
 
94
        src, lineno = inspect.findsource(cmd.__class__)
 
95
        lineno = _offset(''.join(src), opt.help, lineno)
 
96
        _poentry(outf, path, lineno, opt.help,
114
97
                "help of '%s' option of '%s' command" % (name, cmd.name()))
115
98
 
116
99
 
117
 
def docstrings(path):
 
100
def _write_command_help(outf, cmd_name, cmd):
 
101
    path = inspect.getfile(cmd.__class__)
 
102
    if path.endswith('.pyc'):
 
103
        path = path[:-1]
 
104
    path = os.path.relpath(path)
 
105
    lineno = inspect.findsource(cmd.__class__)[1]
 
106
    doc = inspect.getdoc(cmd)
 
107
 
 
108
    _poentry_per_paragraph(outf, path, lineno, doc)
 
109
    _command_options(outf, path, cmd)
 
110
 
 
111
def _command_helps(outf):
118
112
    """Extract docstrings from path.
119
113
 
120
114
    This respects the Bazaar cmdtable/table convention and will
121
115
    only extract docstrings from functions mentioned in these tables.
122
116
    """
123
 
    from bzrlib.commands import Command as cmd_klass
124
 
    try:
125
 
        mod = importpath(path)
126
 
    except Exception as e:
127
 
        # some module raises exception (ex. bzrlib.transport.ftp._gssapi
128
 
        print >>sys.stderr, "Can't import %r: %s" % (path, e)
129
 
        return
130
 
    for name in dir(mod):
131
 
        if not name.startswith('cmd_'):
132
 
            continue
133
 
        obj = getattr(mod, name)
134
 
        try:
135
 
            doc = obj.__doc__
136
 
            if doc:
137
 
                doc = inspect.cleandoc(doc)
138
 
            else:
139
 
                continue
140
 
        except AttributeError:
141
 
            continue
142
 
        if (inspect.isclass(obj) and issubclass(obj, cmd_klass)
143
 
                and not obj is cmd_klass):
144
 
            lineno = inspect.findsource(obj)[1]
145
 
            poentry_per_paragraph(path, lineno, doc)
146
 
            options(path, lineno, obj)
147
 
 
148
 
def bzrerrors():
 
117
    import bzrlib.plugin
 
118
    from glob import glob
 
119
 
 
120
    # builtin commands
 
121
    for cmd_name in _mod_commands.builtin_command_names():
 
122
        command = _mod_commands.get_cmd_object(cmd_name, False)
 
123
        _write_command_help(outf, cmd_name, command)
 
124
 
 
125
    plugin_path = bzrlib.plugin.get_core_plugin_path()
 
126
    core_plugins = glob(plugin_path + '/*/__init__.py')
 
127
    core_plugins = [os.path.basename(os.path.dirname(p))
 
128
                        for p in core_plugins]
 
129
    # core plugins
 
130
    for cmd_name in _mod_commands.plugin_command_names():
 
131
        command = _mod_commands.get_cmd_object(cmd_name, False)
 
132
        if command.plugin_name() not in core_plugins:
 
133
            # skip non-core plugins
 
134
            # TODO: Support extracting from third party plugins.
 
135
            continue
 
136
        _write_command_help(outf, cmd_name, command)
 
137
 
 
138
 
 
139
def _error_messages(outf):
149
140
    """Extract fmt string from bzrlib.errors."""
150
141
    from bzrlib import errors
151
142
    base_klass = errors.BzrError
161
152
            continue
162
153
        fmt = getattr(klass, "_fmt", None)
163
154
        if fmt:
164
 
            poentry('bzrlib/erros.py', inspect.findsource(klass)[1], fmt)
 
155
            _poentry(outf, 'bzrlib/errors.py', inspect.findsource(klass)[1], fmt)
165
156
 
166
 
def bzr_helptopics():
 
157
def _help_topics(outf):
167
158
    from bzrlib.help_topics import topic_registry
168
159
    for key in topic_registry.keys():
169
160
        doc = topic_registry.get(key)
170
161
        if isinstance(doc, str):
171
 
            poentry_per_paragraph(
 
162
            _poentry_per_paragraph(
 
163
                    outf,
172
164
                    'dummy/help_topics/'+key+'/detail.txt',
173
165
                    1, doc)
174
166
 
175
167
        summary = topic_registry.get_summary(key)
176
168
        if summary is not None:
177
 
            poentry('dummy/help_topics/'+key+'/summary.txt',
178
 
                    1, summary)
179
 
 
180
 
 
181
 
def rawtext(path):
182
 
    src = open(path).read()
183
 
    poentry_per_paragraph(path, 1, src)
184
 
 
185
 
 
186
 
if __name__ == "__main__":
187
 
    # It is very important that we import the Bazaar modules from
188
 
    # the source tree where bzrgettext is executed. Otherwise we might
189
 
    # accidentally import and extract strings from a Bazaar
190
 
    # installation mentioned in PYTHONPATH.
191
 
    sys.path.insert(0, os.getcwd())
192
 
    import bzrlib
193
 
 
194
 
    with bzrlib.initialize():
195
 
        for path in sys.argv[1:]:
196
 
            if path.endswith('.txt'):
197
 
                rawtext(path)
198
 
            else:
199
 
                docstrings(path)
200
 
        bzrerrors()
201
 
        bzr_helptopics()
 
169
            _poentry(outf, 'dummy/help_topics/'+key+'/summary.txt',
 
170
                     1, summary)
 
171
 
 
172
def export_pot(outf):
 
173
    global _FOUND_MSGID
 
174
    _FOUND_MSGID = set()
 
175
    _command_helps(outf)
 
176
    _error_messages(outf)
 
177
    _help_topics(outf)