Mercurial > babel > old > mirror
changeset 54:cc3c6cfe909d
Support sub-commands in command-line interface, and renamed the generated script wrapper to `babel`. See #9.
author | cmlenz |
---|---|
date | Thu, 07 Jun 2007 22:59:25 +0000 |
parents | 52dbebdd3789 |
children | b298e583d326 |
files | babel/catalog/frontend.py doc/cmdline.txt setup.py |
diffstat | 3 files changed, 190 insertions(+), 133 deletions(-) [+] |
line wrap: on
line diff
--- a/babel/catalog/frontend.py +++ b/babel/catalog/frontend.py @@ -31,24 +31,25 @@ from babel.catalog.pofile import write_po, write_pot from babel.catalog.plurals import PLURALS -__all__ = ['extract_messages', 'check_message_extractors', 'main'] +__all__ = ['CommandLineInterface', 'extract_messages', + 'check_message_extractors', 'main'] __docformat__ = 'restructuredtext en' class extract_messages(Command): """Message extraction command for use in ``setup.py`` scripts. - + If correctly installed, this command is available to Setuptools-using setup scripts automatically. For projects using plain old ``distutils``, the command needs to be registered explicitly in ``setup.py``:: - + from babel.catalog.frontend import extract_messages - + setup( ... cmdclass = {'extract_messages': extract_messages} ) - + :see: `Integrating new distutils commands <http://docs.python.org/dist/node32.html>`_ :see: `setuptools <http://peak.telecommunity.com/DevCenter/setuptools>`_ """ @@ -158,95 +159,24 @@ finally: outfile.close() -def extract_cmdline(argv=sys.argv): - """Command-line interface. - - This function provides a simple command-line interface to the message - extraction and PO file generation functionality. - - :param argv: list of arguments passed on the command-line - """ - parser = OptionParser(usage='%prog [options] dirname1 <dirname2> ...', - version='%%prog %s' % VERSION) - parser.add_option('--charset', dest='charset', default='utf-8', - help='charset to use in the output') - parser.add_option('-k', '--keyword', dest='keywords', - default=[], action='append', - help='keywords to look for in addition to the defaults. ' - 'You can specify multiple -k flags on the command ' - 'line.') - parser.add_option('--no-default-keywords', dest='no_default_keywords', - action='store_true', default=False, - help="do not include the default keywords") - parser.add_option('--mapping', '-F', dest='mapping_file', - help='path to the extraction mapping file') - parser.add_option('--no-location', dest='no_location', default=False, - action='store_true', - help='do not include location comments with filename and ' - 'line number') - parser.add_option('--omit-header', dest='omit_header', default=False, - action='store_true', - help='do not include msgid "" entry in header') - parser.add_option('-o', '--output', dest='output', - help='path to the output POT file') - parser.add_option('-w', '--width', dest='width', type='int', - help="set output line width (default 76)") - parser.add_option('--no-wrap', dest='no_wrap', default=False, - action = 'store_true', help='do not break long message ' - 'lines, longer than the output line width, into several ' - 'lines') - options, args = parser.parse_args(argv[1:]) - if not args: - parser.error('incorrect number of arguments') - if options.output not in (None, '-'): - outfile = open(options.output, 'w') - else: - outfile = sys.stdout - - keywords = DEFAULT_KEYWORDS.copy() - if options.no_default_keywords: - if not options.keywords: - parser.error('you must specify new keywords if you disable the ' - 'default ones') - keywords = {} - if options.keywords: - keywords.update(parse_keywords(options.keywords)) - - if options.mapping_file: - fileobj = open(options.mapping_file, 'U') - try: - method_map, options_map = parse_mapping(fileobj) - finally: - fileobj.close() - else: - method_map = DEFAULT_MAPPING - options_map = {} +def check_message_extractors(dist, name, value): + """Validate the ``message_extractors`` keyword argument to ``setup()``. - if options.width and options.no_wrap: - parser.error("'--no-wrap' and '--width' are mutually exclusive.") - elif not options.width and not options.no_wrap: - options.width = 76 - elif not options.width and options.no_wrap: - options.width = 0 + :param dist: the distutils/setuptools ``Distribution`` object + :param name: the name of the keyword argument (should always be + "message_extractors") + :param value: the value of the keyword argument + :raise `DistutilsSetupError`: if the value is not valid + :see: `Adding setup() arguments + <http://peak.telecommunity.com/DevCenter/setuptools#adding-setup-arguments>`_ + """ + assert name == 'message_extractors' + if not isinstance(value, (basestring, dict)): + raise DistutilsSetupError('the value of the "extract_messages" ' + 'parameter must be a string or dictionary') - try: - messages = [] - for dirname in args: - if not os.path.isdir(dirname): - parser.error('%r is not a directory' % dirname) - extracted = extract_from_dir(dirname, method_map, options_map, - keywords) - for filename, lineno, funcname, message in extracted: - filepath = os.path.normpath(os.path.join(dirname, filename)) - messages.append((filepath, lineno, funcname, message, None)) - write_po(outfile, messages, width=options.width, - charset=options.charset, no_location=options.no_location, - omit_header=options.omit_header) - finally: - if options.output: - outfile.close() - + class new_catalog(Command): """New catalog command for use in ``setup.py`` scripts. @@ -293,7 +223,7 @@ def finalize_options(self): if not self.input_file: raise DistutilsOptionError('you must specify the input file') - + if not self.locale: raise DistutilsOptionError('you must provide a locale for the ' 'new catalog') @@ -303,7 +233,7 @@ except UnknownLocaleError, error: log.error(error) sys.exit(1) - + self._locale_parts = self.locale.split('_') self._language = None self._country = None @@ -319,18 +249,17 @@ else: locale = self._locale_parts[0] self._language = _locale.languages[locale] - + if not self.output_file and not self.output_dir: raise DistutilsOptionError('you must specify the output directory') - + if not self.output_file and self.output_dir: self.output_file = os.path.join(self.output_dir, locale + '.po') - def run(self): outfile = open(self.output_file, 'w') infile = open(self.input_file, 'r') - + if PLURALS.has_key(self.locale): # Try <language>_<COUNTRY> plurals = PLURALS[self.locale] @@ -339,14 +268,14 @@ plurals = PLURALS[self._locale_parts[0]] else: plurals = ('INTEGER', 'EXPRESSION') - + if self._country: logline = 'Creating %%s (%s) %%r PO from %%r' % self._country + \ - ' PO template' + ' PO template' else: logline = 'Creating %s %r PO from %r PO template' log.info(logline, self._language, self.output_file, self.input_file) - + write_po(outfile, infile, self._language, country=self._country, project=self.distribution.get_name(), version=self.distribution.get_version(), @@ -356,33 +285,141 @@ infile.close() outfile.close() - -def new_catalog_cmdline(argv=sys.argv): - pass -def check_message_extractors(dist, name, value): - """Validate the ``message_extractors`` keyword argument to ``setup()``. - - :param dist: the distutils/setuptools ``Distribution`` object - :param name: the name of the keyword argument (should always be - "message_extractors") - :param value: the value of the keyword argument - :raise `DistutilsSetupError`: if the value is not valid - :see: `Adding setup() arguments - <http://peak.telecommunity.com/DevCenter/setuptools#adding-setup-arguments>`_ +class CommandLineInterface(object): + """Command-line interface. + + This class provides a simple command-line interface to the message + extraction and PO file generation functionality. """ - assert name == 'message_extractors' - if not isinstance(value, (basestring, dict)): - raise DistutilsSetupError('the value of the "extract_messages" ' - 'parameter must be a string or dictionary') + + usage = '%%prog %s [options] %s' + version = '%%prog %s' % VERSION + commands = ['extract', 'init'] + + def run(self, argv=sys.argv): + """Main entry point of the command-line interface. + + :param argv: list of arguments passed on the command-line + """ + parser = OptionParser(usage=self.usage % ('subcommand', '[args]'), + version=self.version) + parser.disable_interspersed_args() + options, args = parser.parse_args(argv[1:]) + if not args: + parser.error('incorrect number of arguments') + + cmdname = args[0] + if cmdname not in self.commands: + parser.error('unknown subcommand "%s"' % cmdname) + + getattr(self, cmdname)(args[1:]) + + def extract(self, argv): + """Subcommand for extracting messages from source files and generating + a POT file. + + :param argv: the command arguments + """ + parser = OptionParser(usage=self.usage % ('extract', 'dir1 <dir2> ...')) + parser.add_option('--charset', dest='charset', + help='charset to use in the output') + parser.add_option('-k', '--keyword', dest='keywords', action='append', + help='keywords to look for in addition to the ' + 'defaults. You can specify multiple -k flags on ' + 'the command line.') + parser.add_option('--no-default-keywords', dest='no_default_keywords', + action='store_true', + help="do not include the default keywords") + parser.add_option('--mapping', '-F', dest='mapping_file', + help='path to the extraction mapping file') + parser.add_option('--no-location', dest='no_location', + action='store_true', + help='do not include location comments with filename ' + 'and line number') + parser.add_option('--omit-header', dest='omit_header', + action='store_true', + help='do not include msgid "" entry in header') + parser.add_option('-o', '--output', dest='output', + help='path to the output POT file') + parser.add_option('-w', '--width', dest='width', type='int', + help="set output line width (default %default)") + parser.add_option('--no-wrap', dest='no_wrap', action = 'store_true', + help='do not break long message lines, longer than ' + 'the output line width, into several lines') + + parser.set_defaults(charset='utf-8', keywords=[], + no_default_keywords=False, no_location=False, + omit_header = False, width=76, no_wrap=False) + options, args = parser.parse_args(argv) + if not args: + parser.error('incorrect number of arguments') + + if options.output not in (None, '-'): + outfile = open(options.output, 'w') + else: + outfile = sys.stdout + + keywords = DEFAULT_KEYWORDS.copy() + if options.no_default_keywords: + if not options.keywords: + parser.error('you must specify new keywords if you disable the ' + 'default ones') + keywords = {} + if options.keywords: + keywords.update(parse_keywords(options.keywords)) + + if options.mapping_file: + fileobj = open(options.mapping_file, 'U') + try: + method_map, options_map = parse_mapping(fileobj) + finally: + fileobj.close() + else: + method_map = DEFAULT_MAPPING + options_map = {} + + if options.width and options.no_wrap: + parser.error("'--no-wrap' and '--width' are mutually exclusive.") + elif not options.width and not options.no_wrap: + options.width = 76 + elif not options.width and options.no_wrap: + options.width = 0 + + try: + messages = [] + for dirname in args: + if not os.path.isdir(dirname): + parser.error('%r is not a directory' % dirname) + extracted = extract_from_dir(dirname, method_map, options_map, + keywords) + for filename, lineno, funcname, message in extracted: + filepath = os.path.normpath(os.path.join(dirname, filename)) + messages.append((filepath, lineno, funcname, message, None)) + write_po(outfile, messages, width=options.width, + charset=options.charset, no_location=options.no_location, + omit_header=options.omit_header) + finally: + if options.output: + outfile.close() + + def init(self, argv): + """Subcommand for creating new message catalogs from a template. + + :param argv: the command arguments + """ + raise NotImplementedError + +def main(): + CommandLineInterface().run(sys.argv) def parse_mapping(fileobj, filename=None): """Parse an extraction method mapping from a file-like object. - + >>> buf = StringIO(''' ... # Python source files ... [python: foobar/**.py] - ... + ... ... # Genshi templates ... [genshi: foobar/**/templates/**.html] ... include_attrs = @@ -390,9 +427,9 @@ ... template_class = genshi.template.text.TextTemplate ... encoding = latin-1 ... ''') - + >>> method_map, options_map = parse_mapping(buf) - + >>> method_map['foobar/**.py'] 'python' >>> options_map['foobar/**.py'] @@ -407,7 +444,7 @@ 'genshi.template.text.TextTemplate' >>> options_map['foobar/**/templates/**.txt']['encoding'] 'latin-1' - + :param fileobj: a readable file-like object containing the configuration text to parse :return: a `(method_map, options_map)` tuple @@ -428,7 +465,7 @@ def parse_keywords(strings=[]): """Parse keywords specifications from the given list of strings. - + >>> kw = parse_keywords(['_', 'dgettext:2', 'dngettext:2,3']) >>> for keyword, indices in sorted(kw.items()): ... print (keyword, indices) @@ -448,5 +485,6 @@ keywords[funcname] = indices return keywords + if __name__ == '__main__': extract_cmdline()
--- a/doc/cmdline.txt +++ b/doc/cmdline.txt @@ -5,7 +5,7 @@ ====================== Babel includes a command-line interface for working with message catalogs, -similar to the GNU ``xgettext`` program commonly available on Linux/Unix +similar to the various GNU ``gettext`` tools commonly available on Linux/Unix systems. @@ -14,24 +14,44 @@ .. sectnum:: -pygettext -========= +When properly installed, Babel provides a script called ``babel``:: -When properly installed, Babel provides a script called ``pygettext``, which can -be used to extract localized messages from a variety of files:: - - $ pygettext --help - usage: pygettext [options] dirname1 <dirname2> ... + $ babel --help + usage: babel subcommand [options] [args] options: --version show program's version number and exit -h, --help show this help message and exit + +The ``babel`` script provides a number of sub-commands that do the actual work. +Those sub-commands are described below. + + +extract +======= + +The ``extract`` sub-command can be used to extract localizable messages from +a collection of source files:: + + $ babel extract --help + usage: babel extract [options] dir1 <dir2> ... + + options: + -h, --help show this help message and exit --charset=CHARSET charset to use in the output -k KEYWORDS, --keyword=KEYWORDS keywords to look for in addition to the defaults. You can specify multiple -k flags on the command line. + --no-default-keywords + do not include the default keywords + -F MAPPING_FILE, --mapping=MAPPING_FILE + path to the extraction mapping file --no-location do not include location comments with filename and line number --omit-header do not include msgid "" entry in header -o OUTPUT, --output=OUTPUT path to the output POT file + -w WIDTH, --width=WIDTH + set output line width (default 76) + --no-wrap do not break long message lines, longer than the + output line width, into several lines
--- a/setup.py +++ b/setup.py @@ -127,8 +127,7 @@ entry_points = """ [console_scripts] - pygettext = babel.catalog.frontend:extract_cmdline - pymsginit = babel.catalog.frontend:new_catalog_cmdline + babel = babel.catalog.frontend:main [distutils.commands] extract_messages = babel.catalog.frontend:extract_messages