1
# Copyright (C) 2011 Canonical Ltd
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.
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.
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
17
# The normalize function is taken from pygettext which is distributed
18
# with Python under the Python License, which is GPL compatible.
20
"""Extract docstrings from Bazaar commands.
27
commands as _mod_commands,
33
from bzrlib.trace import (
37
from bzrlib.i18n import gettext
41
s = (s.replace('\\', '\\\\')
50
# This converts the various Python string types into a format that
51
# is appropriate for .po files, namely much closer to C style.
54
s = '"' + _escape(s) + '"'
58
lines[-1] = lines[-1] + '\n'
59
lines = map(_escape, lines)
61
s = '""\n"' + lineterm.join(lines) + '"'
65
_FOUND_MSGID = None # set by entry function.
67
def _poentry(outf, path, lineno, s, comment=None):
74
comment = "# %s\n" % comment
75
mutter("Exporting msg %r at line %d in %r", s[:20], lineno, path)
76
print >>outf, ('#: %s:%d\n' % (path, lineno) +
78
'msgid %s\n' % _normalize(s) +
81
def _poentry_per_paragraph(outf, path, lineno, msgid, filter=lambda x: False):
82
# TODO: How to split long help?
83
paragraphs = msgid.split('\n\n')
87
_poentry(outf, path, lineno, p)
88
lineno += p.count('\n') + 2
90
_LAST_CACHE = _LAST_CACHED_SRC = None
92
def _offsets_of_literal(src):
93
global _LAST_CACHE, _LAST_CACHED_SRC
94
if src == _LAST_CACHED_SRC:
95
return _LAST_CACHE.copy()
100
for node in ast.walk(root):
101
if not isinstance(node, ast.Str):
103
offsets[node.s] = node.lineno - node.s.count('\n')
105
_LAST_CACHED_SRC = src
106
_LAST_CACHE = offsets.copy()
109
def _standard_options(outf):
110
from bzrlib.option import Option
111
src = inspect.findsource(Option)[0]
113
path = 'bzrlib/option.py'
114
offsets = _offsets_of_literal(src)
116
for name in sorted(Option.OPTIONS.keys()):
117
opt = Option.OPTIONS[name]
118
if getattr(opt, 'hidden', False):
120
if getattr(opt, 'title', None):
121
lineno = offsets.get(opt.title, 9999)
123
note(gettext("%r is not found in bzrlib/option.py") % opt.title)
124
_poentry(outf, path, lineno, opt.title,
125
'title of %r option' % name)
126
if getattr(opt, 'help', None):
127
lineno = offsets.get(opt.help, 9999)
129
note(gettext("%r is not found in bzrlib/option.py") % opt.help)
130
_poentry(outf, path, lineno, opt.help,
131
'help of %r option' % name)
133
def _command_options(outf, path, cmd):
134
src, default_lineno = inspect.findsource(cmd.__class__)
135
offsets = _offsets_of_literal(''.join(src))
136
for opt in cmd.takes_options:
137
if isinstance(opt, str):
139
if getattr(opt, 'hidden', False):
142
if getattr(opt, 'title', None):
143
lineno = offsets.get(opt.title, default_lineno)
144
_poentry(outf, path, lineno, opt.title,
145
'title of %r option of %r command' % (name, cmd.name()))
146
if getattr(opt, 'help', None):
147
lineno = offsets.get(opt.help, default_lineno)
148
_poentry(outf, path, lineno, opt.help,
149
'help of %r option of %r command' % (name, cmd.name()))
152
def _write_command_help(outf, cmd):
153
path = inspect.getfile(cmd.__class__)
154
if path.endswith('.pyc'):
156
path = os.path.relpath(path)
157
src, lineno = inspect.findsource(cmd.__class__)
158
offsets = _offsets_of_literal(''.join(src))
159
lineno = offsets[cmd.__doc__]
160
doc = inspect.getdoc(cmd)
163
# ':Usage:' has special meaning in help topics.
164
# This is usage example of command and should not be translated.
165
if p.splitlines()[0] == ':Usage:':
168
_poentry_per_paragraph(outf, path, lineno, doc, filter)
169
_command_options(outf, path, cmd)
172
def _command_helps(outf, plugin_name=None):
173
"""Extract docstrings from path.
175
This respects the Bazaar cmdtable/table convention and will
176
only extract docstrings from functions mentioned in these tables.
178
from glob import glob
181
for cmd_name in _mod_commands.builtin_command_names():
182
command = _mod_commands.get_cmd_object(cmd_name, False)
185
if plugin_name is not None:
186
# only export builtins if we are not exporting plugin commands
188
note(gettext("Exporting messages from builtin command: %s"), cmd_name)
189
_write_command_help(outf, command)
191
plugin_path = plugin.get_core_plugin_path()
192
core_plugins = glob(plugin_path + '/*/__init__.py')
193
core_plugins = [os.path.basename(os.path.dirname(p))
194
for p in core_plugins]
196
for cmd_name in _mod_commands.plugin_command_names():
197
command = _mod_commands.get_cmd_object(cmd_name, False)
200
if plugin_name is not None and command.plugin_name() != plugin_name:
201
# if we are exporting plugin commands, skip plugins we have not specified.
203
if plugin_name is None and command.plugin_name() not in core_plugins:
204
# skip non-core plugins
205
# TODO: Support extracting from third party plugins.
207
note(gettext("Exporting messages from plugin command: {0} in {1}").format(
208
cmd_name, command.plugin_name() ))
209
_write_command_help(outf, command)
212
def _error_messages(outf):
213
"""Extract fmt string from bzrlib.errors."""
214
path = errors.__file__
215
if path.endswith('.pyc'):
217
offsets = _offsets_of_literal(open(path).read())
219
base_klass = errors.BzrError
220
for name in dir(errors):
221
klass = getattr(errors, name)
222
if not inspect.isclass(klass):
224
if not issubclass(klass, base_klass):
226
if klass is base_klass:
228
if klass.internal_error:
230
fmt = getattr(klass, "_fmt", None)
232
note(gettext("Exporting message from error: %s"), name)
233
_poentry(outf, 'bzrlib/errors.py',
234
offsets.get(fmt, 9999), fmt)
236
def _help_topics(outf):
237
topic_registry = help_topics.topic_registry
238
for key in topic_registry.keys():
239
doc = topic_registry.get(key)
240
if isinstance(doc, str):
241
_poentry_per_paragraph(
243
'dummy/help_topics/'+key+'/detail.txt',
245
elif callable(doc): # help topics from files
246
_poentry_per_paragraph(
248
'en/help_topics/'+key+'.txt',
250
summary = topic_registry.get_summary(key)
251
if summary is not None:
252
_poentry(outf, 'dummy/help_topics/'+key+'/summary.txt',
255
def export_pot(outf, plugin=None):
259
_standard_options(outf)
261
_error_messages(outf)
264
_command_helps(outf, plugin)