Added non-babel output mode to extract_from_ast, integreated jinja2 doctests directly...
authorArmin Ronacher <armin.ronacher@active-4.com>
Fri, 27 Jun 2008 06:45:19 +0000 (08:45 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Fri, 27 Jun 2008 06:45:19 +0000 (08:45 +0200)
--HG--
branch : trunk

docs/Makefile
jinja2/ext.py
jinja2/loaders.py
jinja2/runtime.py
tests/conftest.py

index e9c11d0676164a4290f8ac58255ac0322448be76..5e24ec12ec99ec3d4cc907f2b2fa5049cfcbc3ba 100644 (file)
@@ -16,7 +16,8 @@ ALLSPHINXOPTS   = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 help:
        @echo "Please use \`make <target>' where <target> is one of"
        @echo "  html      to make standalone HTML files"
-       @echo "  pickle    to make pickle files (usable by e.g. sphinx-web)"
+       @echo "  pickle    to make pickle files"
+       @echo "  json      to make JSON files"
        @echo "  htmlhelp  to make HTML files and a HTML help project"
        @echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
        @echo "  changes   to make an overview over all changed/added/deprecated items"
@@ -35,9 +36,13 @@ pickle:
        mkdir -p _build/pickle _build/doctrees
        $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
        @echo
-       @echo "Build finished; now you can process the pickle files or run"
-       @echo "  sphinx-web _build/pickle"
-       @echo "to start the sphinx-web server."
+       @echo "Build finished; now you can process the pickle files"
+
+json:
+       mkdir -p _build/json _build/doctrees
+       $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json
+       @echo
+       @echo "Build finished; now you can process the json files"
 
 web: pickle
 
index f60aade429a4a46dea2605f3e27324f04979dfdd..4d68983a517f9d8dca6176fa9238416f7e6a8af1 100644 (file)
@@ -321,8 +321,28 @@ class LoopControlExtension(Extension):
         return nodes.Continue(lineno=token.lineno)
 
 
-def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS):
-    """Extract localizable strings from the given template node.
+def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
+                     babel_style=True):
+    """Extract localizable strings from the given template node.  Per
+    default this function returns matches in babel style that means non string
+    parameters as well as keyword arguments are returned as `None`.  This
+    allows Babel to figure out what you really meant if you are using
+    gettext functions that allow keyword arguments for placeholder expansion.
+    If you don't want that behavior set the `babel_style` parameter to `False`
+    which causes only strings to be returned and parameters are always stored
+    in tuples.  As a consequence invalid gettext calls (calls without a single
+    string parameter or string parameters after non-string parameters) are
+    skipped.
+
+    This example explains the behavior:
+
+    >>> from jinja2 import Environment
+    >>> env = Environment()
+    >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}')
+    >>> list(extract_from_ast(node))
+    [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))]
+    >>> list(extract_from_ast(node, babel_style=False))
+    [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))]
 
     For every string found this function yields a ``(lineno, function,
     message)`` tuple, where:
@@ -346,10 +366,22 @@ def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS):
             else:
                 strings.append(None)
 
-        if len(strings) == 1:
-            strings = strings[0]
+        for arg in node.kwargs:
+            strings.append(None)
+        if node.dyn_args is not None:
+            strings.append(None)
+        if node.dyn_kwargs is not None:
+            strings.append(None)
+
+        if not babel_style:
+            strings = tuple(x for x in strings if x is not None)
+            if not strings:
+                continue
         else:
-            strings = tuple(strings)
+            if len(strings) == 1:
+                strings = strings[0]
+            else:
+                strings = tuple(strings)
         yield node.lineno, node.node.name, strings
 
 
index ccd6ec183a2af664c4fc8a1e2fae7915892a7d9b..e964fdcf7964092480d19fc85bfc4427c9b3723e 100644 (file)
@@ -257,7 +257,7 @@ class ChoiceLoader(BaseLoader):
 
     >>> loader = ChoiceLoader([
     ...     FileSystemLoader('/path/to/user/templates'),
-    ...     PackageLoader('myapplication')
+    ...     PackageLoader('mypackage')
     ... ])
 
     This is useful if you want to allow users to override builtin templates
index 2dbd5693edd6d204110dd71ec131c41f2a1c6b3b..0417f616b13e78aa7f0eacc7051caeaff4203b7a 100644 (file)
@@ -346,7 +346,7 @@ class Undefined(object):
     >>> foo + 42
     Traceback (most recent call last):
       ...
-    jinja2.exceptions.UndefinedError: 'foo' is undefined
+    UndefinedError: 'foo' is undefined
     """
     __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
                  '_undefined_exception')
@@ -415,7 +415,7 @@ class DebugUndefined(Undefined):
     >>> foo + 42
     Traceback (most recent call last):
       ...
-    jinja2.exceptions.UndefinedError: 'foo' is undefined
+    UndefinedError: 'foo' is undefined
     """
     __slots__ = ()
 
@@ -439,15 +439,15 @@ class StrictUndefined(Undefined):
     >>> str(foo)
     Traceback (most recent call last):
       ...
-    jinja2.exceptions.UndefinedError: 'foo' is undefined
+    UndefinedError: 'foo' is undefined
     >>> not foo
     Traceback (most recent call last):
       ...
-    jinja2.exceptions.UndefinedError: 'foo' is undefined
+    UndefinedError: 'foo' is undefined
     >>> foo + 42
     Traceback (most recent call last):
       ...
-    jinja2.exceptions.UndefinedError: 'foo' is undefined
+    UndefinedError: 'foo' is undefined
     """
     __slots__ = ()
     __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
index 8a24673c627deb27c59cdd6ec0faab0c7cd0dd9a..fa60acd1da3dd8c2babda59a45d276b3e01b39f5 100644 (file)
@@ -18,6 +18,7 @@ from jinja2 import Environment
 from jinja2.loaders import BaseLoader
 from jinja2.exceptions import TemplateNotFound
 
+
 try:
     # This code adds support for coverage.py (see
     # http://nedbatchelder.com/code/modules/coverage.html).
@@ -67,6 +68,20 @@ loader = GlobalLoader()
 simple_env = Environment(trim_blocks=True, loader=loader, cache_size=0)
 
 
+class Directory(py.test.collect.Directory):
+
+    def run(self):
+        rv = super(Directory, self).run()
+        if self.fspath.basename == 'tests':
+            rv.append('doctests')
+        return rv
+
+    def join(self, name):
+        if name == 'doctests':
+            return JinjaDocTestModule(name, parent=self)
+        return super(Directory, self).join(name)
+
+
 class Module(py.test.collect.Module):
 
     def __init__(self, *args, **kwargs):
@@ -94,15 +109,41 @@ class JinjaTestFunction(py.test.collect.Function):
 
 class JinjaDocTest(py.test.collect.Item):
 
+    def __init__(self, *args, **kwargs):
+        realmod = kwargs.pop('realmod', False)
+        super(JinjaDocTest, self).__init__(*args, **kwargs)
+        self.realmod = realmod
+
     def run(self):
-        mod = py.std.types.ModuleType(self.name)
-        mod.__doc__ = self.obj
+        if self.realmod:
+            mod = __import__(self.name, None, None, [''])
+        else:
+            mod = py.std.types.ModuleType(self.name)
+            mod.__doc__ = self.obj
+            mod.env = self.parent.env
+            mod.MODULE = self.parent.obj
         self.execute(mod)
 
     def execute(self, mod):
-        mod.env = self.parent.env
-        mod.MODULE = self.parent.obj
         failed, tot = py.compat.doctest.testmod(mod, verbose=True)
         if failed:
             py.test.fail('doctest %s: %s failed out of %s' % (
                          self.fspath, failed, tot))
+
+
+class JinjaDocTestModule(py.test.collect.Module):
+
+    def __init__(self, *args, **kwargs):
+        super(JinjaDocTestModule, self).__init__(*args, **kwargs)
+        self.doctest_modules = [
+            'jinja2.environment', 'jinja2.compiler', 'jinja2.parser',
+            'jinja2.lexer', 'jinja2.ext', 'jinja2.sandbox',
+            'jinja2.filters', 'jinja2.tests', 'jinja2.utils',
+            'jinja2.runtime'
+        ]
+
+    def run(self):
+        return self.doctest_modules
+
+    def join(self, name):
+        return JinjaDocTest(name, parent=self, realmod=True)