Permalink
Browse files

0.12.2dev: Moves get_l10n_js_cmdclass and get_l10n_cmdclass from `tra…

…c.util.dist` to `trac.dist`. It be able to import `*_cmdclasses` in setup.py without Genshi.

Closes #9961.


git-svn-id: http://trac.edgewall.org/intertrac/log:/branches/0.12-stable@10444 af82e41b-90c4-0310-8c96-b1721e28e2e2
  • Loading branch information...
1 parent b4b1bfa commit af534b697191306f9b143121e5bf3d9d6f7bae55 jomae committed Jan 20, 2011
Showing with 244 additions and 215 deletions.
  1. +2 −2 messages-js.cfg
  2. +12 −2 setup.py
  3. +225 −0 trac/dist.py
  4. +5 −211 trac/util/dist.py
View
@@ -3,6 +3,6 @@
[javascript: **.js]
[extractors]
-javascript_script = trac.util.dist:extract_javascript_script
+javascript_script = trac.dist:extract_javascript_script
-[javascript_script: **.html]
+[javascript_script: **.html]
View
@@ -40,12 +40,19 @@
'tracopt': extractors,
}
- from trac.util.dist import get_l10n_js_cmdclass
+ from trac.dist import get_l10n_js_cmdclass
extra['cmdclass'] = get_l10n_js_cmdclass()
-except ImportError, e:
+except ImportError:
pass
+try:
+ import genshi
+except ImportError:
+ print "Genshi is needed by Trac setup, pre-installing"
+ # give some context to the warnings we might get when installing Genshi
+
+
setup(
name = 'Trac',
version = '0.12.2',
@@ -85,6 +92,9 @@
test_suite = 'trac.test.suite',
zip_safe = True,
+ setup_requires = [
+ 'Genshi>=0.6',
+ ],
install_requires = [
'setuptools>=0.6b1',
'Genshi>=0.6',
View
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2010 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
+"""Extra commands for setup.py.
+
+In addition to providing a few extra command classes in `l10n_cmdclass`,
+we also modify the standard `distutils.command.build` and
+`setuptools.command.install_lib` classes so that the relevant l10n commands
+for compiling catalogs are issued upon install.
+"""
+
+from StringIO import StringIO
+import os
+import re
+
+from distutils import log
+from distutils.cmd import Command
+from distutils.command.build import build as _build
+from distutils.errors import DistutilsOptionError
+from setuptools.command.install_lib import install_lib as _install_lib
+
+try:
+ from babel.messages.extract import extract_javascript
+ from babel.messages.frontend import extract_messages, init_catalog, \
+ compile_catalog, update_catalog
+ from babel.support import Translations
+
+
+ def extract_javascript_script(fileobj, keywords, comment_tags, options):
+ """Extract messages from Javascript embedding in <script> tags.
+
+ Select <script type="javascript/text"> tags and delegate to
+ `extract_javascript`.
+ """
+ from genshi.core import Stream
+ from genshi.input import XMLParser
+
+ out = StringIO()
+ stream = Stream(XMLParser(fileobj))
+ stream.select('//script[@type="text/javascript"]').render(out=out)
+ out.seek(0)
+ return extract_javascript(out, keywords, comment_tags, options)
+
+
+ class generate_messages_js(Command):
+ """Generating message javascripts command for use ``setup.py`` scripts.
+ """
+
+ description = 'generate message javascript files from binary MO files'
+ user_options = [
+ ('domain=', 'D',
+ "domain of PO file (default 'messages')"),
+ ('input-dir=', 'I',
+ 'path to base directory containing the catalogs'),
+ ('input-file=', 'i',
+ 'name of the input file'),
+ ('output-dir=', 'O',
+ "name of the output directory"),
+ ('output-file=', 'o',
+ "name of the output file (default "
+ "'<output_dir>/<locale>.js')"),
+ ('locale=', 'l',
+ 'locale of the catalog to compile'),
+ ]
+
+ def initialize_options(self):
+ self.domain = 'messages'
+ self.input_dir = None
+ self.input_file = None
+ self.output_dir = None
+ self.output_file = None
+ self.locale = None
+
+ def finalize_options(self):
+ if not self.input_file and not self.input_dir:
+ raise DistutilsOptionError('you must specify either the input '
+ 'file or directory')
+ if not self.output_file and not self.output_dir:
+ raise DistutilsOptionError('you must specify either the '
+ 'output file or directory')
+
+ def run(self):
+ mo_files = []
+ js_files = []
+
+ def js_path(dir, locale):
+ return os.path.join(dir, locale + '.js')
+
+ if not self.input_file:
+ if self.locale:
+ mo_files.append((self.locale,
+ os.path.join(self.input_dir, self.locale,
+ 'LC_MESSAGES',
+ self.domain + '.mo')))
+ js_files.append(js_path(self.output_dir, self.locale))
+ else:
+ for locale in os.listdir(self.input_dir):
+ mo_file = os.path.join(self.input_dir, locale,
+ 'LC_MESSAGES',
+ self.domain + '.mo')
+ if os.path.exists(mo_file):
+ mo_files.append((locale, mo_file))
+ js_files.append(js_path(self.output_dir, locale))
+ else:
+ mo_files.append((self.locale, self.input_file))
+ if self.output_file:
+ js_files.append(self.output_file)
+ else:
+ js_files.append(js_path(self.output_dir, locale))
+
+ if not mo_files:
+ raise DistutilsOptionError('no compiled catalogs found')
+
+ if not os.path.isdir(self.output_dir):
+ os.mkdir(self.output_dir)
+
+ for idx, (locale, mo_file) in enumerate(mo_files):
+ js_file = js_files[idx]
+ log.info('generating messages javascript %r to %r',
+ mo_file, js_file)
+
+ infile = open(mo_file, 'rb')
+ try:
+ t = Translations(infile, self.domain)
+ catalog = t._catalog
+ finally:
+ infile.close()
+
+ outfile = open(js_file, 'w')
+ try:
+ write_js(outfile, catalog, self.domain, locale)
+ finally:
+ outfile.close()
+
+ def write_js(fileobj, catalog, domain, locale):
+ from trac.util.presentation import to_json
+ data = {'domain': domain, 'locale': locale}
+
+ messages = {}
+ for msgid, msgstr in catalog.iteritems():
+ if isinstance(msgid, (list, tuple)):
+ messages.setdefault(msgid[0], {})
+ messages[msgid[0]][msgid[1]] = msgstr
+ elif msgid:
+ messages[msgid] = msgstr
+ else:
+ for line in msgstr.splitlines():
+ line = line.strip()
+ if not line:
+ continue
+ if ':' not in line:
+ continue
+ name, val = line.split(':', 1)
+ name = name.strip().lower()
+ if name == 'plural-forms':
+ data['plural_expr'] = pluralexpr(val)
+ break
+ data['messages'] = messages
+
+ fileobj.write('// Generated messages javascript file '
+ 'from compiled MO file\n')
+ fileobj.write('babel.Translations.load(')
+ fileobj.write(to_json(data).encode('utf-8'))
+ fileobj.write(').install();\n')
+
+ def pluralexpr(forms):
+ match = re.search(r'\bplural\s*=\s*([^;]+)', forms)
+ if not match:
+ raise ValueError('Failed to parse plural_forms %r' % (forms,))
+ return match.group(1)
+
+
+ def get_command_overriders():
+ # 'bdist_wininst' runs a 'build', so make the latter
+ # run a 'compile_catalog' before 'build_py'
+ class build(_build):
+ sub_commands = [('compile_catalog', None)] + _build.sub_commands
+
+ # 'bdist_egg' isn't that nice, all it does is an 'install_lib'
+ class install_lib(_install_lib): # playing setuptools' own tricks ;-)
+ def l10n_run(self):
+ self.run_command('compile_catalog')
+ def run(self):
+ self.l10n_run()
+ _install_lib.run(self)
+ return build, install_lib
+
+ def get_l10n_cmdclass():
+ build, install_lib = get_command_overriders()
+ return {'build': build, 'install_lib': install_lib}
+
+ def get_l10n_js_cmdclass():
+ build, _install_lib = get_command_overriders()
+ build.sub_commands.insert(0, ('generate_messages_js', None))
+ build.sub_commands.insert(0, ('compile_catalog_js', None))
+ class install_lib(_install_lib):
+ def l10n_run(self):
+ self.run_command('compile_catalog_js')
+ self.run_command('generate_messages_js')
+ self.run_command('compile_catalog')
+ return {
+ 'build': build, 'install_lib': install_lib,
+ 'extract_messages_js': extract_messages,
+ 'init_catalog_js': init_catalog,
+ 'compile_catalog_js': compile_catalog,
+ 'update_catalog_js': update_catalog,
+ 'generate_messages_js': generate_messages_js,
+ }
+
+
+except ImportError:
+ def get_l10n_cmdclass():
+ return
+ def get_l10n_js_cmdclass():
+ return
Oops, something went wrong.

0 comments on commit af534b6

Please sign in to comment.