##// END OF EJS Templates
hggettext: ensure correct Mercurial is imported
Martin Geisler -
r8626:1fc1c77d default
parent child Browse files
Show More
@@ -1,118 +1,123
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # hggettext - carefully extract docstrings for Mercurial
3 # hggettext - carefully extract docstrings for Mercurial
4 #
4 #
5 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
5 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2, incorporated herein by reference.
8 # GNU General Public License version 2, incorporated herein by reference.
9
9
10 # The normalize function is taken from pygettext which is distributed
10 # The normalize function is taken from pygettext which is distributed
11 # with Python under the Python License, which is GPL compatible.
11 # with Python under the Python License, which is GPL compatible.
12
12
13 """Extract docstrings from Mercurial commands.
13 """Extract docstrings from Mercurial commands.
14
14
15 Compared to pygettext, this script knows about the cmdtable and table
15 Compared to pygettext, this script knows about the cmdtable and table
16 dictionaries used by Mercurial, and will only extract docstrings from
16 dictionaries used by Mercurial, and will only extract docstrings from
17 functions mentioned therein.
17 functions mentioned therein.
18
18
19 Use xgettext like normal to extract strings marked as translatable and
19 Use xgettext like normal to extract strings marked as translatable and
20 join the message cataloges to get the final catalog.
20 join the message cataloges to get the final catalog.
21 """
21 """
22
22
23 from mercurial import demandimport; demandimport.enable()
23 import os, sys, inspect
24 import sys, inspect
25
24
26
25
27 def escape(s):
26 def escape(s):
28 # The order is important, the backslash must be escaped first
27 # The order is important, the backslash must be escaped first
29 # since the other replacements introduce new backslashes
28 # since the other replacements introduce new backslashes
30 # themselves.
29 # themselves.
31 s = s.replace('\\', '\\\\')
30 s = s.replace('\\', '\\\\')
32 s = s.replace('\n', '\\n')
31 s = s.replace('\n', '\\n')
33 s = s.replace('\r', '\\r')
32 s = s.replace('\r', '\\r')
34 s = s.replace('\t', '\\t')
33 s = s.replace('\t', '\\t')
35 s = s.replace('"', '\\"')
34 s = s.replace('"', '\\"')
36 return s
35 return s
37
36
38
37
39 def normalize(s):
38 def normalize(s):
40 # This converts the various Python string types into a format that
39 # This converts the various Python string types into a format that
41 # is appropriate for .po files, namely much closer to C style.
40 # is appropriate for .po files, namely much closer to C style.
42 lines = s.split('\n')
41 lines = s.split('\n')
43 if len(lines) == 1:
42 if len(lines) == 1:
44 s = '"' + escape(s) + '"'
43 s = '"' + escape(s) + '"'
45 else:
44 else:
46 if not lines[-1]:
45 if not lines[-1]:
47 del lines[-1]
46 del lines[-1]
48 lines[-1] = lines[-1] + '\n'
47 lines[-1] = lines[-1] + '\n'
49 lines = map(escape, lines)
48 lines = map(escape, lines)
50 lineterm = '\\n"\n"'
49 lineterm = '\\n"\n"'
51 s = '""\n"' + lineterm.join(lines) + '"'
50 s = '""\n"' + lineterm.join(lines) + '"'
52 return s
51 return s
53
52
54
53
55 def poentry(path, lineno, s):
54 def poentry(path, lineno, s):
56 return ('#: %s:%d\n' % (path, lineno) +
55 return ('#: %s:%d\n' % (path, lineno) +
57 'msgid %s\n' % normalize(s) +
56 'msgid %s\n' % normalize(s) +
58 'msgstr ""\n')
57 'msgstr ""\n')
59
58
60
59
61 def offset(src, doc, name, default):
60 def offset(src, doc, name, default):
62 """Compute offset or issue a warning on stdout."""
61 """Compute offset or issue a warning on stdout."""
63 # Backslashes in doc appear doubled in src.
62 # Backslashes in doc appear doubled in src.
64 end = src.find(doc.replace('\\', '\\\\'))
63 end = src.find(doc.replace('\\', '\\\\'))
65 if end == -1:
64 if end == -1:
66 # This can happen if the docstring contains unnecessary escape
65 # This can happen if the docstring contains unnecessary escape
67 # sequences such as \" in a triple-quoted string. The problem
66 # sequences such as \" in a triple-quoted string. The problem
68 # is that \" is turned into " and so doc wont appear in src.
67 # is that \" is turned into " and so doc wont appear in src.
69 sys.stderr.write("warning: unknown offset in %s, assuming %d lines\n"
68 sys.stderr.write("warning: unknown offset in %s, assuming %d lines\n"
70 % (name, default))
69 % (name, default))
71 return default
70 return default
72 else:
71 else:
73 return src.count('\n', 0, end)
72 return src.count('\n', 0, end)
74
73
75
74
76 def importpath(path):
75 def importpath(path):
77 """Import a path like foo/bar/baz.py and return the baz module."""
76 """Import a path like foo/bar/baz.py and return the baz module."""
78 if path.endswith('.py'):
77 if path.endswith('.py'):
79 path = path[:-3]
78 path = path[:-3]
80 if path.endswith('/__init__'):
79 if path.endswith('/__init__'):
81 path = path[:-9]
80 path = path[:-9]
82 path = path.replace('/', '.')
81 path = path.replace('/', '.')
83 mod = __import__(path)
82 mod = __import__(path)
84 for comp in path.split('.')[1:]:
83 for comp in path.split('.')[1:]:
85 mod = getattr(mod, comp)
84 mod = getattr(mod, comp)
86 return mod
85 return mod
87
86
88
87
89 def docstrings(path):
88 def docstrings(path):
90 """Extract docstrings from path.
89 """Extract docstrings from path.
91
90
92 This respects the Mercurial cmdtable/table convention and will
91 This respects the Mercurial cmdtable/table convention and will
93 only extract docstrings from functions mentioned in these tables.
92 only extract docstrings from functions mentioned in these tables.
94 """
93 """
95 mod = importpath(path)
94 mod = importpath(path)
96 if mod.__doc__:
95 if mod.__doc__:
97 src = open(path).read()
96 src = open(path).read()
98 lineno = 1 + offset(src, mod.__doc__, path, 7)
97 lineno = 1 + offset(src, mod.__doc__, path, 7)
99 print poentry(path, lineno, mod.__doc__)
98 print poentry(path, lineno, mod.__doc__)
100
99
101 cmdtable = getattr(mod, 'cmdtable', {})
100 cmdtable = getattr(mod, 'cmdtable', {})
102 if not cmdtable:
101 if not cmdtable:
103 # Maybe we are processing mercurial.commands?
102 # Maybe we are processing mercurial.commands?
104 cmdtable = getattr(mod, 'table', {})
103 cmdtable = getattr(mod, 'table', {})
105
104
106 for entry in cmdtable.itervalues():
105 for entry in cmdtable.itervalues():
107 func = entry[0]
106 func = entry[0]
108 if func.__doc__:
107 if func.__doc__:
109 src = inspect.getsource(func)
108 src = inspect.getsource(func)
110 name = "%s.%s" % (path, func.__name__)
109 name = "%s.%s" % (path, func.__name__)
111 lineno = func.func_code.co_firstlineno
110 lineno = func.func_code.co_firstlineno
112 lineno += offset(src, func.__doc__, name, 1)
111 lineno += offset(src, func.__doc__, name, 1)
113 print poentry(path, lineno, func.__doc__)
112 print poentry(path, lineno, func.__doc__)
114
113
115
114
116 if __name__ == "__main__":
115 if __name__ == "__main__":
116 # It is very important that we import the Mercurial modules from
117 # the source tree where hggettext is executed. Otherwise we might
118 # accidentally import and extract strings from a Mercurial
119 # installation mentioned in PYTHONPATH.
120 sys.path.insert(0, os.getcwd())
121 from mercurial import demandimport; demandimport.enable()
117 for path in sys.argv[1:]:
122 for path in sys.argv[1:]:
118 docstrings(path)
123 docstrings(path)
General Comments 0
You need to be logged in to leave comments. Login now