From 5fda7520e017d2bc85c95f346bb684ed9d7da824 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Fri, 3 Nov 2017 09:44:47 -0500 Subject: Documentation for RTD. --- .gitignore | 1 + CHANGES.rst | 2 +- MANIFEST.in | 5 + README.rst | 24 ++-- doc-requirements.txt | 1 + docs/changelog.rst | 1 + docs/compiler.rst | 12 ++ docs/conf.py | 280 ++++++++++++++++++++++++++++++++++++++++++ docs/engine.rst | 17 +++ docs/expressions.rst | 7 ++ docs/index.rst | 26 ++++ docs/interfaces.rst | 5 + docs/other.rst | 5 + setup.py | 10 +- src/zope/tales/engine.py | 26 +++- src/zope/tales/expressions.py | 55 ++++++++- src/zope/tales/interfaces.py | 26 ++-- src/zope/tales/pythonexpr.py | 9 ++ src/zope/tales/tales.py | 108 +++++++++++----- tox.ini | 11 +- 20 files changed, 573 insertions(+), 58 deletions(-) create mode 100644 doc-requirements.txt create mode 100644 docs/changelog.rst create mode 100644 docs/compiler.rst create mode 100644 docs/conf.py create mode 100644 docs/engine.rst create mode 100644 docs/expressions.rst create mode 100644 docs/index.rst create mode 100644 docs/interfaces.rst create mode 100644 docs/other.rst diff --git a/.gitignore b/.gitignore index f323719..605e07a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ develop-eggs parts .coverage htmlcov/ +docs/_build/ diff --git a/CHANGES.rst b/CHANGES.rst index a304996..7e55b33 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ 4.2.1 (unreleased) ================== -- Nothing changed yet. +- Host documentation at https://zopetales.readthedocs.io 4.2.0 (2017-09-22) diff --git a/MANIFEST.in b/MANIFEST.in index f8ca167..7b9f311 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,4 +8,9 @@ include .coveragerc recursive-include src * +recursive-include docs *.rst +recursive-include docs *.py +recursive-include docs Makefile +recursive-include docs make.bat + global-exclude *.pyc diff --git a/README.rst b/README.rst index 2344718..522949d 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ -================ - ``zope.tales`` -================ +============ + zope.tales +============ .. image:: https://img.shields.io/pypi/v/zope.tales.svg :target: https://pypi.python.org/pypi/zope.tales/ @@ -16,9 +16,19 @@ .. image:: https://coveralls.io/repos/github/zopefoundation/zope.tales/badge.svg?branch=master :target: https://coveralls.io/github/zopefoundation/zope.tales?branch=master -Template Attribute Language - Expression Syntax +.. image:: https://readthedocs.org/projects/zopetales/badge/?version=latest + :target: https://zopetales.readthedocs.io/en/latest/ + :alt: Documentation Status -See -* http://web.archive.org/web/20120814103624/http://wiki.zope.org:80/ZPT/TALESSpecification13 -* https://docs.zope.org/zope2/zope2book/AppendixC.html#tales-overview +``zope.tales`` (Template Attribute Language - Expression Syntax) is an +expression language designed to work with `zope.tal +`_ (although it can be used +independently). The two are integrated to produce page templates in +`zope.pagetemplate `_. + +The specification for TAL and TALES can be found at +http://docs.zope.org/zope2/zope2book/AppendixC.html + +Documentation on this implementation and its API can be found at +https://zopetales.readthedocs.io/ diff --git a/doc-requirements.txt b/doc-requirements.txt new file mode 100644 index 0000000..e9704b8 --- /dev/null +++ b/doc-requirements.txt @@ -0,0 +1 @@ +.[docs] diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..d9e113e --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1 @@ +.. include:: ../CHANGES.rst diff --git a/docs/compiler.rst b/docs/compiler.rst new file mode 100644 index 0000000..a04a3b4 --- /dev/null +++ b/docs/compiler.rst @@ -0,0 +1,12 @@ +===================== + Expression Compiler +===================== + +.. autoclass:: zope.tales.tales.ExpressionEngine + +.. autofunction:: zope.tales.engine.DefaultEngine + +.. data:: zope.tales.engine.Engine + + An instance of the default engine (:func:`DefaultEngine`) that can + be used for simple shared cases diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..63b8d4f --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# zope.tales documentation build configuration file, created by +# sphinx-quickstart on Thu Jan 29 11:31:12 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import pkg_resources +sys.path.append(os.path.abspath('../src')) +rqmt = pkg_resources.require('zope.tales')[0] + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'sphinx.ext.viewcode', + 'repoze.sphinx.autointerface', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'zope.tales' +copyright = '2015-2017, Zope Foundation and Contributors' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '%s.%s' % tuple(map(int, rqmt.version.split('.')[:2])) +# The full version, including alpha/beta/rc tags. +release = rqmt.version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'zopetalesdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'zopetales.tex', 'zope.tales Documentation', + 'Zope Foundation and Contributors', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'zopetales', 'zope.tales Documentation', + ['Zope Foundation and Contributors'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'zopetales', 'zope.tales Documentation', + 'Zope Foundation and Contributors', 'zopetales', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = { + 'https://docs.python.org/': None, + 'https://zopetal.readthedocs.io/en/latest/': None, + 'https://zopepagetemplate.readthedocs.io/en/latest/': None, +} + +autodoc_default_flags = ['members', 'show-inheritance'] +autoclass_content = 'both' +autodoc_member_order = 'bysource' diff --git a/docs/engine.rst b/docs/engine.rst new file mode 100644 index 0000000..f3c95fd --- /dev/null +++ b/docs/engine.rst @@ -0,0 +1,17 @@ +=================== + Expression Engine +=================== + +.. currentmodule:: zope.tales.tales + +.. autoclass:: Context + +Exceptions +========== + +A number of exceptions are defined. + +.. autoclass:: TALESError +.. autoclass:: Undefined +.. autoclass:: CompilerError +.. autoclass:: RegistrationError diff --git a/docs/expressions.rst b/docs/expressions.rst new file mode 100644 index 0000000..4281468 --- /dev/null +++ b/docs/expressions.rst @@ -0,0 +1,7 @@ +============================ + Expression Implementations +============================ + +.. automodule:: zope.tales.expressions + +.. autoclass:: zope.tales.pythonexpr.PythonExpr diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..6ed5495 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,26 @@ +.. include:: ../README.rst + + +API Documentation: + +.. toctree:: + :maxdepth: 2 + + interfaces + expressions + compiler + engine + other + +.. toctree:: + :maxdepth: 1 + + changelog + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/interfaces.rst b/docs/interfaces.rst new file mode 100644 index 0000000..8411977 --- /dev/null +++ b/docs/interfaces.rst @@ -0,0 +1,5 @@ +============ + Interfaces +============ + +.. automodule:: zope.tales.interfaces diff --git a/docs/other.rst b/docs/other.rst new file mode 100644 index 0000000..98bbd29 --- /dev/null +++ b/docs/other.rst @@ -0,0 +1,5 @@ +=============== + Other Objects +=============== + +.. autoclass:: zope.tales.tales.Iterator diff --git a/setup.py b/setup.py index e1077bf..790bc09 100644 --- a/setup.py +++ b/setup.py @@ -60,8 +60,9 @@ setup(name='zope.tales', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', - 'Framework :: Zope3'], - url='http://github.com/zopefoundation/zope.tales', + 'Framework :: Zope3', + ], + url='https://github.com/zopefoundation/zope.tales', license='ZPL 2.1', packages=find_packages('src'), package_dir={'': 'src'}, @@ -71,6 +72,11 @@ setup(name='zope.tales', 'tal': [ 'zope.tal', ], + 'docs': [ + 'Sphinx', + 'repoze.sphinx.autointerface', + 'zope.tal', + ], }, install_requires=[ 'setuptools', diff --git a/src/zope/tales/engine.py b/src/zope/tales/engine.py index 14cfecc..7bab8f2 100644 --- a/src/zope/tales/engine.py +++ b/src/zope/tales/engine.py @@ -24,7 +24,29 @@ from zope.tales.expressions import LazyExpr from zope.tales.expressions import SimpleModuleImporter from zope.tales.pythonexpr import PythonExpr -def Engine(): +def DefaultEngine(): + """ + Create and return an instance of :class:`~.ExpressionEngine` (an + implementation of + :class:`zope.tal.interfaces.ITALExpressionCompiler`) with the + following expression types registered: + + ``string`` + :class:`.StringExpr` + ``python`` + :class:`.PythonExpr` + ``not`` + :class:`.NotExpr` + ``defer`` + :class:`.DeferExpr` + ``lazy`` + :class:`.LazyExpr` + ``modules`` + :class:`.SimpleModuleImporter` + + In addition, the default ``path`` expressions (``standard``, ``path``, ``exists`` + and ``nocall``), all implemented by :class:`.PathExpr`, are registered. + """ e = ExpressionEngine() reg = e.registerType for pt in PathExpr._default_type_names: @@ -37,4 +59,4 @@ def Engine(): e.registerBaseName('modules', SimpleModuleImporter()) return e -Engine = Engine() +Engine = DefaultEngine() diff --git a/src/zope/tales/expressions.py b/src/zope/tales/expressions.py index 6203cc7..48be495 100644 --- a/src/zope/tales/expressions.py +++ b/src/zope/tales/expressions.py @@ -11,13 +11,22 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Basic Page Template expression types. +""" +Basic Page Template expression types. + +Expression objects are created by the :class:`.ExpressionEngine` +(they must have previously been registered with +:func:`~zope.tales.tales.ExpressionEngine.registerType`). The expression object itself +is a callable object taking one argument, *econtext*, which is the local +expression namespace. + """ import re -import six import sys import types +import six + from zope.interface import implementer from zope.tales.tales import _valid_name, _parse_expr, NAME_RE, Undefined from zope.tales.interfaces import ITALESExpression, ITALESFunctionNamespace @@ -46,6 +55,9 @@ def simpleTraverse(object, path_items, econtext): class SubPathExpr(object): + """ + Implementation of a single path expression. + """ def __init__(self, path, traverser, engine): self._traverser = traverser @@ -144,7 +156,10 @@ class SubPathExpr(object): @implementer(ITALESExpression) class PathExpr(object): - """One or more subpath expressions, separated by '|'.""" + """ + One or more :class:`subpath expressions `, separated + by ``|``. + """ # _default_type_names contains the expression type names this # class is usually registered for. @@ -162,8 +177,8 @@ class PathExpr(object): paths = expr.split('|') self._subexprs = [] add = self._subexprs.append - for i in range(len(paths)): - path = paths[i].lstrip() + for i, path in enumerate(paths): + path = path.lstrip() if _parse_expr(path): # This part is the start of another expression type, # so glue it back together and compile it. @@ -232,6 +247,12 @@ _interp = re.compile( @implementer(ITALESExpression) class StringExpr(object): + """ + An expression that produces a string. + + Sub-sequences of the string that begin with ``$`` are + interpreted as path expressions to evaluate. + """ def __init__(self, name, expr, engine): self._s = expr @@ -275,6 +296,10 @@ class StringExpr(object): @implementer(ITALESExpression) class NotExpr(object): + """ + An expression that negates the boolean value + of its sub-expression. + """ def __init__(self, name, expr, engine): self._s = expr = expr.lstrip() @@ -301,6 +326,19 @@ class DeferWrapper(object): @implementer(ITALESExpression) class DeferExpr(object): + """ + An expression that will defer evaluation of the sub-expression + until necessary, preserving the execution context it was created + with. + + This is useful in ``tal:define`` expressions:: + +
+ ... + + +
+ """ def __init__(self, name, expr, compiler): self._s = expr = expr.lstrip() @@ -328,7 +366,12 @@ class LazyWrapper(DeferWrapper): return r class LazyExpr(DeferExpr): - """lazy: expression handler for lazy initialization of expressions + """ + An expression that will defer evaluation of its + sub-expression until the first time it is necessary. + + This is like :class:`DeferExpr`, but caches the result of + evaluating the expression. """ def __call__(self, econtext): return LazyWrapper(self._c, econtext) diff --git a/src/zope/tales/interfaces.py b/src/zope/tales/interfaces.py index cefae98..158b794 100644 --- a/src/zope/tales/interfaces.py +++ b/src/zope/tales/interfaces.py @@ -11,7 +11,8 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Interface that describes the 'macros' attribute of a PageTemplate. +""" +Interface that describes the TALES implementation. """ from zope.interface import Interface @@ -36,25 +37,28 @@ class ITALESExpression(Interface): """TALES expression These are expression handlers that handle a specific type of - expression in TALES, e.g. path or string expression. + expression in TALES, e.g. ``path`` or ``string`` expression. """ def __call__(econtext): - """Evaluate expression according to the given execution - context 'econtext' and return computed value. + """ + Evaluate expression according to the given execution context + *econtext* and return computed value. """ class ITALESIterator(ITALIterator): - """TAL Iterator provided by TALES + """TAL Iterator provided by TALES. - Values of this iterator are assigned to items in the repeat namespace. + Values of this iterator are assigned to items in the ``repeat`` + namespace. - For example, with a TAL statement like: tal:repeat="item items", - an iterator will be assigned to "repeat/item". The iterator - provides a number of handy methods useful in writing TAL loops. + For example, with a TAL statement like: ``tal:repeat="item + items"``, an iterator will be assigned to ``repeat/item`` (using a + path expression). The iterator provides a number of handy methods + useful in writing TAL loops. The results are undefined of calling any of the methods except - 'length' before the first iteration. + ``length`` before the first iteration. """ def index(): @@ -66,7 +70,7 @@ class ITALESIterator(ITALIterator): """ def even(): - """Return whether the current position is even + """Return whether the current position is even. """ def odd(): diff --git a/src/zope/tales/pythonexpr.py b/src/zope/tales/pythonexpr.py index ffa5c84..3f47bed 100644 --- a/src/zope/tales/pythonexpr.py +++ b/src/zope/tales/pythonexpr.py @@ -15,7 +15,16 @@ """ class PythonExpr(object): + """ + Evaluates a python expression by calling :func:`eval` after + compiling it with :func:`compile`. + """ def __init__(self, name, expr, engine): + """ + :param str expr: The Python expression. + :param ExpressionEngine engine: The expression compiler that + is creating us. + """ text = '\n'.join(expr.splitlines()) # normalize line endings text = '(' + text + ')' # Put text in parens so newlines don't matter self.text = text diff --git a/src/zope/tales/tales.py b/src/zope/tales/tales.py index 1256153..9765fc8 100644 --- a/src/zope/tales/tales.py +++ b/src/zope/tales/tales.py @@ -19,9 +19,11 @@ __docformat__ = "reStructuredText" import re from zope.interface import implementer +from zope.interface import Interface import six -from zope.interface import Interface +from zope.tales.interfaces import ITALESIterator + class ITALExpressionEngine(Interface): pass class ITALExpressionCompiler(Interface): @@ -37,7 +39,7 @@ try: except ImportError: pass -from zope.tales.interfaces import ITALESIterator + NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*" _parse_expr = re.compile(r"(%s):" % NAME_RE).match @@ -61,7 +63,11 @@ _default = object() @implementer(ITALESIterator) class Iterator(object): - """TALES Iterator + """ + TALES Iterator. + + Default implementation of :class:`zope.tales.interfaces.ITALESIterator`. + """ def __init__(self, name, seq, context): @@ -511,6 +517,7 @@ class Iterator(object): class ErrorInfo(object): """Information about an exception passed to an on-error handler.""" + # XXX: This is a duplicate of zope.tal.taldefs.ErrorInfo value = None def __init__(self, err, position=(None, None)): @@ -525,13 +532,23 @@ class ErrorInfo(object): @implementer(ITALExpressionCompiler) class ExpressionEngine(object): - '''Expression Engine - - An instance of this class keeps a mutable collection of expression - type handlers. It can compile expression strings by delegating to - these handlers. It can provide an expression Context, which is - capable of holding state and evaluating compiled expressions. - ''' + """ + Expression compiler, an implementation of + :class:`zope.tal.interfaces.ITALExpressionCompiler`. + + An instance of this class keeps :meth:`a mutable collection of + expression type handlers + `. It can compile + expression strings by delegating to these handlers. It can + :meth:`provide an expression engine + `, which is capable + of holding state and evaluating compiled expressions. + + By default, this object does not know how to compile any + expression types. See :data:`zope.tales.engine.Engine` and + :func:`zope.tales.engine.DefaultEngine` for pre-configured + instances supporting the standard expression types. + """ def __init__(self): self.types = {} @@ -540,23 +557,24 @@ class ExpressionEngine(object): self.iteratorFactory = Iterator def registerFunctionNamespace(self, namespacename, namespacecallable): - """Register a function namespace + """ + Register a function namespace - namespace - a string containing the name of the namespace to - be registered + :param str namespace: a string containing the name of the namespace to + be registered - namespacecallable - a callable object which takes the following - parameter: + :param callable namespacecallable: a callable object which takes the following + parameter: - context - the object on which the functions - provided by this namespace will - be called + :context: the object on which the functions + provided by this namespace will + be called - This callable should return an object which - can be traversed to get the functions provided - by the this namespace. + This callable should return an object which + can be traversed to get the functions provided + by the this namespace. - example: + For example:: class stringFuncs(object): @@ -569,7 +587,7 @@ class ExpressionEngine(object): def lower(self): return self.context.lower() - engine.registerFunctionNamespace('string',stringFuncs) + engine.registerFunctionNamespace('string', stringFuncs) """ self.namespaces[namespacename] = namespacecallable @@ -579,6 +597,12 @@ class ExpressionEngine(object): return self.namespaces[namespacename] def registerType(self, name, handler): + """ + Register an expression of *name* to be handled with *handler*. + + :raises RegistrationError: If this is a duplicate registration for *name*, + or if *name* is not a valid expression type name. + """ if not _valid_name(name): raise RegistrationError('Invalid expression type name "%s".' % name) types = self.types @@ -617,6 +641,16 @@ class ExpressionEngine(object): return handler(type, expr, self) def getContext(self, contexts=None, **kwcontexts): + """ + Return a new expression engine. + + The keyword arguments passed in *kwcantexts* become the default + variable context for the returned engine. If *contexts* is given, it + should be a mapping, and the values it contains will override + the keyword arguments. + + :rtype: Context + """ if contexts is not None: if kwcontexts: kwcontexts.update(contexts) @@ -630,15 +664,24 @@ class ExpressionEngine(object): @implementer(ITALExpressionEngine) class Context(object): - '''Expression Context + """ + Expression engine, an implementation of + :class:`zope.tal.interfaces.ITALExpressionEngine`. - An instance of this class holds context information that it can - use to evaluate compiled expressions. - ''' + This class is called ``Context`` because an instance of this class + holds context information (namespaces) that it uses when evaluating compiled + expressions. + """ position = (None, None) source_file = None def __init__(self, engine, contexts): + """ + :param engine: A :class:`ExpressionEngine` (a + :class:`zope.tal.interfaces.ITALExpressionCompiler`) + :param contexts: A mapping (namespace) of variables that forms the base + variable scope. + """ self._engine = engine self.contexts = contexts self.setContext('nothing', None) @@ -656,7 +699,7 @@ class Context(object): self._scope_stack = [] def setContext(self, name, value): - # Hook to allow subclasses to do things like adding security proxies + """Hook to allow subclasses to do things like adding security proxies.""" self.contexts[name] = value def beginScope(self): @@ -705,6 +748,12 @@ class Context(object): return it def evaluate(self, expression): + """ + Evaluate the *expression* by calling it, passing in this object, + and return the raw results. + + If *expression* is a string, it is first compiled. + """ if isinstance(expression, str): expression = self._engine.compile(expression) __traceback_supplement__ = ( @@ -714,6 +763,9 @@ class Context(object): evaluateValue = evaluate def evaluateBoolean(self, expr): + """ + Evaluate the expression and return the boolean value of its result. + """ # "not not", while frowned upon by linters might be faster # than bool() because it avoids a builtin lookup. Plus it can't be # reassigned. diff --git a/tox.ini b/tox.ini index 927f900..3d18895 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py27,py34,py35,py36,pypy,pypy3,coverage + py27,py34,py35,py36,pypy,pypy3,coverage,docs [testenv] commands = @@ -18,3 +18,12 @@ commands = deps = {[testenv]deps} coverage + +[testenv:docs] +basepython = + python2.7 +commands = + sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html +deps = + {[testenv]deps} + .[docs] -- cgit v1.2.1