From 58f351da2c71873e5c41b98a871ac1af78624f65 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 28 May 2008 21:30:14 +0200 Subject: [PATCH] data files are optional now --HG-- branch : trunk --- docs/api.rst | 43 ++++++++++++++++++++++-------- jinja2/loaders.py | 2 +- jinja2/utils.py | 68 ++++++++++++++++++++++++++++++++++++++++------- setup.py | 22 +++++++-------- 4 files changed, 103 insertions(+), 32 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 0dce618..e4c7a50 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -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`. diff --git a/jinja2/loaders.py b/jinja2/loaders.py index 8b21fdc..74dee5e 100644 --- a/jinja2/loaders.py +++ b/jinja2/loaders.py @@ -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 diff --git a/jinja2/utils.py b/jinja2/utils.py index 6d1c958..f402704 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -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 World!") + Markup(u'Hello World!') + >>> class Foo(object): + ... def __html__(self): + ... return 'foo' + ... + >>> Markup(Foo()) + Markup(u'foo') + + 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 World!") + Markup(u'Hello <em>World</em>!') + + Operations on a markup string are markup aware which means that all + arguments are passed through the :func:`escape` function: + + >>> em = Markup("%s") + >>> em % "foo & bar" + Markup(u'foo & bar') + >>> strong = Markup("%(text)s") + >>> strong % {'text': 'hacker here'} + Markup(u'<blink>hacker here</blink>') + >>> Markup("Hello ") + "" + Markup(u'Hello <foo>') """ __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 » About").unescape() + u'Main \xbb About' + """ 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 » About").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 diff --git a/setup.py b/setup.py index 8cc724c..f90ca6d 100644 --- 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, -- 2.26.2