data files are optional now
authorArmin Ronacher <armin.ronacher@active-4.com>
Wed, 28 May 2008 19:30:14 +0000 (21:30 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Wed, 28 May 2008 19:30:14 +0000 (21:30 +0200)
--HG--
branch : trunk

docs/api.rst
jinja2/loaders.py
jinja2/utils.py
setup.py

index 0dce61876d7b4706cd6e163f4afa89fbd473b205..e4c7a50202d4e1053bdda029acd4d156cd62a85c 100644 (file)
@@ -89,6 +89,20 @@ unicode string.
 For more details about unicode in Python have a look at the excellent
 `Unicode documentation`_.
 
+Another important thing is how Jinja2 is handling string literals in
+templates.  A naive implementation would be using unicode strings for
+all string literals but it turned out in the past that this is problematic
+as some libraries are typechecking against `str` explicitly.  For example
+`datetime.strftime` does not accept unicode arguments.  To not break it
+completely Jinja2 is returning `str` for strings that fit into ASCII and
+for everything else `unicode`:
+
+>>> m = Template(u"{% set a, b = 'foo', 'föö' %}").module
+>>> m.a
+'foo'
+>>> m.b
+u'f\xf6\xf6'
+
 
 .. _Unicode documentation: http://docs.python.org/dev/howto/unicode.html
 
@@ -140,7 +154,7 @@ useful if you want to dig deeper into Jinja2 or :ref:`develop extensions
 
     .. automethod:: overlay([options])
 
-    .. method:: undefined([hint,] [obj,] name[, exc])
+    .. method:: undefined([hint, obj, name, exc])
 
         Creates a new :class:`Undefined` object for `name`.  This is useful
         for filters or functions that may return undefined objects for
@@ -285,11 +299,11 @@ Undefined objects are created by calling :attr:`undefined`.
                 return 0.0
 
     To disallow a method, just override it and raise
-    :attr:`_undefined_exception`.  Because this is a very common idom in
-    undefined objects there is the helper method
-    :meth:`_fail_with_undefined_error`.  That does that automatically.  Here
-    a class that works like the regular :class:`UndefinedError` but chokes
-    on iteration::
+    :attr:`~Undefined._undefined_exception`.  Because this is a very common
+    idom in undefined objects there is the helper method
+    :meth:`~Undefined._fail_with_undefined_error` that does the error raising
+    automatically.  Here a class that works like the regular :class:`Undefined`
+    but chokes on iteration::
 
         class NonIterableUndefined(Undefined):
             __iter__ = Undefined._fail_with_undefined_error
@@ -410,7 +424,14 @@ functions to a Jinja2 environment.
 
 .. autofunction:: jinja2.utils.is_undefined
 
-.. autoclass:: jinja2.utils.Markup
+.. autoclass:: jinja2.utils.Markup([string])
+    :members: escape, unescape, striptags
+
+.. admonition:: Note
+
+    The Jinja2 :class:`Markup` class is compatible with at least Pylons and
+    Genshi.  It's expected that more template engines and framework will pick
+    up the `__html__` concept soon.
 
 
 Exceptions
@@ -596,7 +617,7 @@ don't recommend using any of those.
 
 .. admonition:: Note
 
-    The low-level API is fragile.  Future Jinja2 versions will not change it
-    in a backwards incompatible way but modifications in the Jinja core may
-    shine through.  For example if Jinja2 introduces a new AST node in later
-    versions that may be returned by :meth:`~Environment.parse`.
+    The low-level API is fragile.  Future Jinja2 versions will try not to
+    change it in a backwards incompatible way but modifications in the Jinja2
+    core may shine through.  For example if Jinja2 introduces a new AST node
+    in later versions that may be returned by :meth:`~Environment.parse`.
index 8b21fdcf679860f2722ee8f20baa660c3fd0a834..74dee5e05f146536660ded7ee8e0c6b18c0b1892 100644 (file)
@@ -23,7 +23,7 @@ def split_template_path(template):
            or (path.altsep and path.altsep in piece) or \
            piece == path.pardir:
             raise TemplateNotFound(template)
-        elif piece != '.':
+        elif piece and piece != '.':
             pieces.append(piece)
     return pieces
 
index 6d1c9580ac66f8aa18ac2d9c57ea66bd7ca66f74..f402704661bf8ff9e3d95e00babe695767a88895 100644 (file)
@@ -70,7 +70,7 @@ def contextfunction(f):
     a function that returns a sorted list of template variables the current
     template exports could look like this::
 
-        @contextcallable
+        @contextfunction
         def get_exported_names(context):
             return sorted(context.exported_vars)
     """
@@ -248,13 +248,48 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
 
 
 class Markup(unicode):
-    """Marks a string as being safe for inclusion in HTML/XML output without
+    r"""Marks a string as being safe for inclusion in HTML/XML output without
     needing to be escaped.  This implements the `__html__` interface a couple
-    of frameworks and web applications use.
+    of frameworks and web applications use.  :class:`Markup` is a direct
+    subclass of `unicode` and provides all the methods of `unicode` just that
+    it escapes arguments passed and always returns `Markup`.
 
     The `escape` function returns markup objects so that double escaping can't
     happen.  If you want to use autoescaping in Jinja just set the finalizer
     of the environment to `escape`.
+
+    The constructor of the :class:`Markup` class can be used for three
+    different things:  When passed an unicode object it's assumed to be safe,
+    when passed an object with an HTML representation (has an `__html__`
+    method) that representation is used, otherwise the object passed is
+    converted into a unicode string and then assumed to be safe:
+
+    >>> Markup("Hello <em>World</em>!")
+    Markup(u'Hello <em>World</em>!')
+    >>> class Foo(object):
+    ...  def __html__(self):
+    ...   return '<a href="#">foo</a>'
+    ... 
+    >>> Markup(Foo())
+    Markup(u'<a href="#">foo</a>')
+
+    If you want object passed being always treated as unsafe you can use the
+    :meth:`escape` classmethod to create a :class:`Markup` object:
+
+    >>> Markup.escape("Hello <em>World</em>!")
+    Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
+
+    Operations on a markup string are markup aware which means that all
+    arguments are passed through the :func:`escape` function:
+
+    >>> em = Markup("<em>%s</em>")
+    >>> em % "foo & bar"
+    Markup(u'<em>foo &amp; bar</em>')
+    >>> strong = Markup("<strong>%(text)s</strong>")
+    >>> strong % {'text': '<blink>hacker here</blink>'}
+    Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
+    >>> Markup("<em>Hello</em> ") + "<foo>"
+    Markup(u'<em>Hello</em> &lt;foo&gt;')
     """
     __slots__ = ()
 
@@ -312,7 +347,12 @@ class Markup(unicode):
     splitlines.__doc__ = unicode.splitlines.__doc__
 
     def unescape(self):
-        """Unescape markup."""
+        r"""Unescape markup again into an unicode string.  This also resolves
+        known HTML4 and XHTML entities:
+
+        >>> Markup("Main &raquo; <em>About</em>").unescape()
+        u'Main \xbb <em>About</em>'
+        """
         def handle_match(m):
             name = m.group(1)
             if name in _entities:
@@ -328,13 +368,22 @@ class Markup(unicode):
         return _entity_re.sub(handle_match, unicode(self))
 
     def striptags(self):
-        """Strip tags and resolve enities."""
+        r"""Unescape markup into an unicode string and strip all tags.  This
+        also resolves known HTML4 and XHTML entities.  Whitespace is
+        normalized to one:
+
+        >>> Markup("Main &raquo;  <em>About</em>").striptags()
+        u'Main \xbb About'
+        """
         stripped = u' '.join(_striptags_re.sub('', self).split())
         return Markup(stripped).unescape()
 
     @classmethod
     def escape(cls, s):
-        """Escape the string.  Works like :func:`escape`."""
+        """Escape the string.  Works like :func:`escape` with the difference
+        that for subclasses of :class:`Markup` this function would return the
+        correct subclass.
+        """
         rv = escape(s)
         if rv.__class__ is not cls:
             return cls(rv)
@@ -392,9 +441,10 @@ class _MarkupEscapeHelper(object):
 
 class LRUCache(object):
     """A simple LRU Cache implementation."""
-    # this is fast for small capacities (something around 200) but doesn't
-    # scale.  But as long as it's only used for the database connections in
-    # a non request fallback it's fine.
+
+    # this is fast for small capacities (something below 1000) but doesn't
+    # scale.  But as long as it's only used as storage for templates this
+    # won't do any harm.
 
     def __init__(self, capacity):
         self.capacity = capacity
index 8cc724cf878789d57f4dced011f815fd83f7348d..f90ca6d2ac75ee49f686a3eb7806416c22a92304 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -49,13 +49,16 @@ from distutils.command.build_ext import build_ext
 from distutils.errors import CCompilerError, DistutilsPlatformError
 
 
-def list_files(path):
-    for fn in os.listdir(path):
-        if fn.startswith('.'):
-            continue
-        fn = os.path.join(path, fn)
-        if os.path.isfile(fn):
-            yield fn
+data_files = []
+documentation_path = 'docs/_build/html'
+if os.path.exists(documentation_path):
+    documentation_files = []
+    for fn in os.listdir(documentation_path):
+        if not fn.startswith('.'):
+            fn = os.path.join(documentation_path, fn)
+            if os.path.isfile(fn):
+                documentation_files.append(fn)
+    data_files.append(('docs', documentation_files))
 
 
 def get_terminal_width():
@@ -91,7 +94,6 @@ class optional_build_ext(build_ext):
         print """WARNING:
 An optional C extension could not be compiled, speedups will not be
 available."""
-        print '*' * width
 
 
 setup(
@@ -119,9 +121,7 @@ setup(
         'Topic :: Text Processing :: Markup :: HTML'
     ],
     packages=['jinja2'],
-    data_files=[
-        ('docs', list(list_files('docs/_build/html')))
-    ],
+    data_files=data_files,
     features={
         'speedups': Feature("optional C speed-enhancements",
             standard=True,