Mercurial > babel > mirror
comparison babel/catalog/frontend.py @ 47:f8469ab4b257 trunk
Support passing extraction method mapping and options from the frontends (see #4). No distutils/setuptools keyword supported yet, but the rest seems to be working okay.
author | cmlenz |
---|---|
date | Wed, 06 Jun 2007 21:03:24 +0000 |
parents | b09e90803d1b |
children | 22b90b3b161a |
comparison
equal
deleted
inserted
replaced
46:29b88754e13a | 47:f8469ab4b257 |
---|---|
16 from distutils import log | 16 from distutils import log |
17 from distutils.cmd import Command | 17 from distutils.cmd import Command |
18 from distutils.errors import DistutilsOptionError | 18 from distutils.errors import DistutilsOptionError |
19 from optparse import OptionParser | 19 from optparse import OptionParser |
20 import os | 20 import os |
21 import re | |
21 import sys | 22 import sys |
22 | 23 |
23 from babel import __version__ as VERSION | 24 from babel import __version__ as VERSION |
24 from babel.catalog.extract import extract_from_dir, DEFAULT_KEYWORDS | 25 from babel.catalog.extract import extract_from_dir, DEFAULT_KEYWORDS, \ |
26 DEFAULT_MAPPING | |
25 from babel.catalog.pofile import write_po | 27 from babel.catalog.pofile import write_po |
26 | 28 |
27 __all__ = ['extract_messages', 'main'] | 29 __all__ = ['extract_messages', 'main'] |
28 __docformat__ = 'restructuredtext en' | 30 __docformat__ = 'restructuredtext en' |
29 | 31 |
53 ('keywords=', 'k', | 55 ('keywords=', 'k', |
54 'space-separated list of keywords to look for in addition to the ' | 56 'space-separated list of keywords to look for in addition to the ' |
55 'defaults'), | 57 'defaults'), |
56 ('no-default-keywords', None, | 58 ('no-default-keywords', None, |
57 'do not include the default keywords'), | 59 'do not include the default keywords'), |
60 ('mapping-file=', 'F', | |
61 'path to the mapping configuration file'), | |
58 ('no-location', None, | 62 ('no-location', None, |
59 'do not include location comments with filename and line number'), | 63 'do not include location comments with filename and line number'), |
60 ('omit-header', None, | 64 ('omit-header', None, |
61 'do not include msgid "" entry in header'), | 65 'do not include msgid "" entry in header'), |
62 ('output-file=', 'o', | 66 ('output-file=', 'o', |
71 'no-default-keywords', 'no-location', 'omit-header', 'no-wrap' | 75 'no-default-keywords', 'no-location', 'omit-header', 'no-wrap' |
72 ] | 76 ] |
73 | 77 |
74 def initialize_options(self): | 78 def initialize_options(self): |
75 self.charset = 'utf-8' | 79 self.charset = 'utf-8' |
76 self.width = 76 | |
77 self.no_wrap = False | |
78 self.keywords = self._keywords = DEFAULT_KEYWORDS.copy() | 80 self.keywords = self._keywords = DEFAULT_KEYWORDS.copy() |
79 self.no_default_keywords = False | 81 self.no_default_keywords = False |
82 self.mapping_file = None | |
80 self.no_location = False | 83 self.no_location = False |
81 self.omit_header = False | 84 self.omit_header = False |
82 self.output_file = None | 85 self.output_file = None |
83 self.input_dirs = None | 86 self.width = 76 |
87 self.no_wrap = False | |
84 | 88 |
85 def finalize_options(self): | 89 def finalize_options(self): |
86 if not self.input_dirs: | |
87 self.input_dirs = dict.fromkeys([k.split('.',1)[0] | |
88 for k in self.distribution.packages | |
89 ]).keys() | |
90 | |
91 if self.no_default_keywords and not self.keywords: | 90 if self.no_default_keywords and not self.keywords: |
92 raise DistutilsOptionError('you must specify new keywords if you ' | 91 raise DistutilsOptionError('you must specify new keywords if you ' |
93 'disable the default ones') | 92 'disable the default ones') |
94 if self.no_default_keywords: | 93 if self.no_default_keywords: |
95 self._keywords = {} | 94 self._keywords = {} |
104 self.width = None | 103 self.width = None |
105 else: | 104 else: |
106 self.width = int(self.width) | 105 self.width = int(self.width) |
107 | 106 |
108 def run(self): | 107 def run(self): |
108 if self.mapping_file: | |
109 fileobj = open(self.mapping_file, 'U') | |
110 try: | |
111 method_map, options_map = parse_mapping(fileobj) | |
112 finally: | |
113 fileobj.close() | |
114 else: | |
115 method_map = DEFAULT_MAPPING | |
116 options_map = {} | |
117 | |
109 outfile = open(self.output_file, 'w') | 118 outfile = open(self.output_file, 'w') |
110 try: | 119 try: |
120 def callback(filename, options): | |
121 optstr = '' | |
122 if options: | |
123 optstr = ' (%s)' % ', '.join(['%s="%s"' % (k, v) for k, v | |
124 in options.items()]) | |
125 log.info('extracting messages from %s%s' % (filename, optstr)) | |
126 | |
111 messages = [] | 127 messages = [] |
112 for dirname in self.input_dirs: | 128 extracted = extract_from_dir(method_map=method_map, |
113 log.info('extracting messages from %r' % dirname) | 129 options_map=options_map, |
114 extracted = extract_from_dir(dirname, keywords=self.keywords) | 130 keywords=self.keywords, |
115 for filename, lineno, funcname, message in extracted: | 131 callback=callback) |
116 messages.append((os.path.join(dirname, filename), lineno, | 132 for filename, lineno, funcname, message in extracted: |
117 funcname, message, None)) | 133 filepath = os.path.normpath(filename) |
134 messages.append((filepath, lineno, funcname, message, None)) | |
118 | 135 |
119 log.info('writing PO file to %s' % self.output_file) | 136 log.info('writing PO file to %s' % self.output_file) |
120 write_po(outfile, messages, project=self.distribution.get_name(), | 137 write_po(outfile, messages, project=self.distribution.get_name(), |
121 version=self.distribution.get_version(), width=self.width, | 138 version=self.distribution.get_version(), width=self.width, |
122 charset=self.charset, no_location=self.no_location, | 139 charset=self.charset, no_location=self.no_location, |
141 help='keywords to look for in addition to the defaults. ' | 158 help='keywords to look for in addition to the defaults. ' |
142 'You can specify multiple -k flags on the command ' | 159 'You can specify multiple -k flags on the command ' |
143 'line.') | 160 'line.') |
144 parser.add_option('--no-default-keywords', dest='no_default_keywords', | 161 parser.add_option('--no-default-keywords', dest='no_default_keywords', |
145 action='store_true', default=False, | 162 action='store_true', default=False, |
146 help="do not include the default keywords defined by " | 163 help="do not include the default keywords") |
147 "Babel") | 164 parser.add_option('--mapping', '-F', dest='mapping_file', |
165 help='path to the extraction mapping file') | |
148 parser.add_option('--no-location', dest='no_location', default=False, | 166 parser.add_option('--no-location', dest='no_location', default=False, |
149 action='store_true', | 167 action='store_true', |
150 help='do not include location comments with filename and ' | 168 help='do not include location comments with filename and ' |
151 'line number') | 169 'line number') |
152 parser.add_option('--omit-header', dest='omit_header', default=False, | 170 parser.add_option('--omit-header', dest='omit_header', default=False, |
175 parser.error('you must specify new keywords if you disable the ' | 193 parser.error('you must specify new keywords if you disable the ' |
176 'default ones') | 194 'default ones') |
177 keywords = {} | 195 keywords = {} |
178 if options.keywords: | 196 if options.keywords: |
179 keywords.update(parse_keywords(options.keywords)) | 197 keywords.update(parse_keywords(options.keywords)) |
180 | 198 |
199 if options.mapping_file: | |
200 fileobj = open(options.mapping_file, 'U') | |
201 try: | |
202 method_map, options_map = parse_mapping(fileobj) | |
203 finally: | |
204 fileobj.close() | |
205 else: | |
206 method_map = DEFAULT_MAPPING | |
207 options_map = {} | |
208 | |
181 if options.width and options.no_wrap: | 209 if options.width and options.no_wrap: |
182 parser.error("'--no-wrap' and '--width' are mutually exclusive.") | 210 parser.error("'--no-wrap' and '--width' are mutually exclusive.") |
183 elif not options.width and not options.no_wrap: | 211 elif not options.width and not options.no_wrap: |
184 options.width = 76 | 212 options.width = 76 |
185 elif not options.width and options.no_wrap: | 213 elif not options.width and options.no_wrap: |
188 try: | 216 try: |
189 messages = [] | 217 messages = [] |
190 for dirname in args: | 218 for dirname in args: |
191 if not os.path.isdir(dirname): | 219 if not os.path.isdir(dirname): |
192 parser.error('%r is not a directory' % dirname) | 220 parser.error('%r is not a directory' % dirname) |
193 extracted = extract_from_dir(dirname, keywords=keywords) | 221 extracted = extract_from_dir(dirname, method_map, options_map, |
222 keywords) | |
194 for filename, lineno, funcname, message in extracted: | 223 for filename, lineno, funcname, message in extracted: |
195 messages.append((os.path.join(dirname, filename), lineno, | 224 filepath = os.path.normpath(os.path.join(dirname, filename)) |
196 funcname, message, None)) | 225 messages.append((filepath, lineno, funcname, message, None)) |
197 write_po(outfile, messages, width=options.width, | 226 write_po(outfile, messages, width=options.width, |
198 charset=options.charset, no_location=options.no_location, | 227 charset=options.charset, no_location=options.no_location, |
199 omit_header=options.omit_header) | 228 omit_header=options.omit_header) |
200 finally: | 229 finally: |
201 if options.output: | 230 if options.output: |
202 outfile.close() | 231 outfile.close() |
232 | |
233 def parse_mapping(fileobj): | |
234 """Parse an extraction method mapping from a file-like object. | |
235 | |
236 >>> from StringIO import StringIO | |
237 >>> buf = StringIO(''' | |
238 ... # Python source files | |
239 ... python: foobar/**.py | |
240 ... | |
241 ... # Genshi templates | |
242 ... genshi: foobar/**/templates/**.html | |
243 ... include_attrs = | |
244 ... genshi: foobar/**/templates/**.txt | |
245 ... template_class = genshi.template.text.TextTemplate | |
246 ... encoding = latin-1 | |
247 ... ''') | |
248 | |
249 >>> method_map, options_map = parse_mapping(buf) | |
250 | |
251 >>> method_map['foobar/**.py'] | |
252 'python' | |
253 >>> options_map['foobar/**.py'] | |
254 {} | |
255 >>> method_map['foobar/**/templates/**.html'] | |
256 'genshi' | |
257 >>> options_map['foobar/**/templates/**.html']['include_attrs'] | |
258 '' | |
259 >>> method_map['foobar/**/templates/**.txt'] | |
260 'genshi' | |
261 >>> options_map['foobar/**/templates/**.txt']['template_class'] | |
262 'genshi.template.text.TextTemplate' | |
263 >>> options_map['foobar/**/templates/**.txt']['encoding'] | |
264 'latin-1' | |
265 | |
266 :param fileobj: a readable file-like object containing the configuration | |
267 text to parse | |
268 :return: a `(method_map, options_map)` tuple | |
269 :rtype: `tuple` | |
270 :see: `extract_from_directory` | |
271 """ | |
272 method_map = {} | |
273 options_map = {} | |
274 | |
275 method = None | |
276 for line in fileobj.readlines(): | |
277 if line.startswith('#'): # comment | |
278 continue | |
279 match = re.match('(\w+): (.+)', line) | |
280 if match: | |
281 method, pattern = match.group(1, 2) | |
282 method_map[pattern] = method | |
283 options_map[pattern] = {} | |
284 elif method: | |
285 match = re.match('\s+(\w+)\s*=\s*(.*)', line) | |
286 if match: | |
287 option, value = match.group(1, 2) | |
288 options_map[pattern][option] = value.strip() | |
289 | |
290 return (method_map, options_map) | |
203 | 291 |
204 def parse_keywords(strings=[]): | 292 def parse_keywords(strings=[]): |
205 """Parse keywords specifications from the given list of strings. | 293 """Parse keywords specifications from the given list of strings. |
206 | 294 |
207 >>> kw = parse_keywords(['_', 'dgettext:2', 'dngettext:2,3']) | 295 >>> kw = parse_keywords(['_', 'dgettext:2', 'dngettext:2,3']) |