diff options
author | Jason Madden <jamadden@gmail.com> | 2018-09-27 07:11:23 -0500 |
---|---|---|
committer | Jason Madden <jamadden@gmail.com> | 2018-09-27 07:11:23 -0500 |
commit | e93f66b15cc0ebfe667da0d7e08e1621c931f437 (patch) | |
tree | 9f5ef0362adffffe6d82b7bd51d81be91f1b4021 | |
parent | ee3c202b62ca09fda23a5d02ab7ae8118b8f6635 (diff) | |
download | zope-configuration-e93f66b15cc0ebfe667da0d7e08e1621c931f437.tar.gz |
Unify docs for fields.rst/py back into .py
-rw-r--r-- | docs/api/fields.rst | 352 | ||||
-rw-r--r-- | docs/conf.py | 7 | ||||
-rw-r--r-- | src/zope/configuration/fields.py | 361 | ||||
-rw-r--r-- | src/zope/configuration/tests/test_docs.py | 34 |
4 files changed, 377 insertions, 377 deletions
diff --git a/docs/api/fields.rst b/docs/api/fields.rst index c44dcf1..b640a30 100644 --- a/docs/api/fields.rst +++ b/docs/api/fields.rst @@ -1,349 +1,5 @@ -:mod:`zope.configuration.fields` -================================ +=========================== + zope.configuration.fields +=========================== -.. module:: zope.configuration.fields - -.. autoclass:: PythonIdentifier - :members: - :member-order: bysource - - Let's look at an example: - - .. doctest:: - - >>> from zope.configuration.fields import PythonIdentifier - >>> class FauxContext(object): - ... pass - >>> context = FauxContext() - >>> field = PythonIdentifier().bind(context) - - Let's test the fromUnicode method: - - .. doctest:: - - >>> field.fromUnicode(u'foo') - 'foo' - >>> field.fromUnicode(u'foo3') - 'foo3' - >>> field.fromUnicode(u'_foo3') - '_foo3' - - Now let's see whether validation works alright - - .. doctest:: - - >>> for value in (u'foo', u'foo3', u'foo_', u'_foo3', u'foo_3', u'foo3_'): - ... _ = field.fromUnicode(value) - >>> from zope.schema import ValidationError - >>> for value in (u'3foo', u'foo:', u'\\', u''): - ... try: - ... field.fromUnicode(value) - ... except ValidationError: - ... print('Validation Error ' + repr(value)) - Validation Error '3foo' - Validation Error 'foo:' - Validation Error '\\' - Validation Error '' - -.. autoclass:: GlobalObject - :members: - :member-order: bysource - - Let's look at an example: - - .. doctest:: - - >>> d = {'x': 1, 'y': 42, 'z': 'zope'} - >>> class fakeresolver(dict): - ... def resolve(self, n): - ... return self[n] - >>> fake = fakeresolver(d) - - >>> from zope.schema import Int - >>> from zope.configuration.fields import GlobalObject - >>> g = GlobalObject(value_type=Int()) - >>> gg = g.bind(fake) - >>> gg.fromUnicode("x") - 1 - >>> gg.fromUnicode(" x \n ") - 1 - >>> gg.fromUnicode("y") - 42 - >>> gg.fromUnicode("z") - Traceback (most recent call last): - ... - WrongType: ('zope', (<type 'int'>, <type 'long'>), '') - - >>> g = GlobalObject(constraint=lambda x: x%2 == 0) - >>> gg = g.bind(fake) - >>> gg.fromUnicode("x") - Traceback (most recent call last): - ... - ConstraintNotSatisfied: 1 - >>> gg.fromUnicode("y") - 42 - >>> g = GlobalObject() - >>> gg = g.bind(fake) - >>> print(gg.fromUnicode('*')) - None - -.. autoclass:: GlobalInterface - :members: - :member-order: bysource - - Example: - - First, we need to set up a stub name resolver: - - .. doctest:: - - >>> from zope.interface import Interface - >>> class IFoo(Interface): - ... pass - >>> class Foo(object): - ... pass - >>> d = {'Foo': Foo, 'IFoo': IFoo} - >>> class fakeresolver(dict): - ... def resolve(self, n): - ... return self[n] - >>> fake = fakeresolver(d) - - Now verify constraints are checked correctly: - - .. doctest:: - - >>> from zope.configuration.fields import GlobalInterface - >>> g = GlobalInterface() - >>> gg = g.bind(fake) - >>> gg.fromUnicode('IFoo') is IFoo - True - >>> gg.fromUnicode(' IFoo ') is IFoo - True - >>> gg.fromUnicode('Foo') - Traceback (most recent call last): - ... - NotAnInterface: (<class 'Foo'>, ... - -.. autoclass:: Tokens - :members: - :member-order: bysource - - Consider GlobalObject tokens: - - First, we need to set up a stub name resolver: - - .. doctest:: - - >>> d = {'x': 1, 'y': 42, 'z': 'zope', 'x.y.x': 'foo'} - >>> class fakeresolver(dict): - ... def resolve(self, n): - ... return self[n] - >>> fake = fakeresolver(d) - - >>> from zope.configuration.fields import Tokens - >>> from zope.configuration.fields import GlobalObject - >>> g = Tokens(value_type=GlobalObject()) - >>> gg = g.bind(fake) - >>> gg.fromUnicode(" \n x y z \n") - [1, 42, 'zope'] - - >>> from zope.schema import Int - >>> g = Tokens(value_type= - ... GlobalObject(value_type= - ... Int(constraint=lambda x: x%2 == 0))) - >>> gg = g.bind(fake) - >>> gg.fromUnicode("x y") - Traceback (most recent call last): - ... - InvalidToken: 1 in x y - - >>> gg.fromUnicode("z y") - Traceback (most recent call last): - ... - InvalidToken: ('zope', (<type 'int'>, <type 'long'>), '') in z y - >>> gg.fromUnicode("y y") - [42, 42] - -.. autoclass:: Path - :members: - :member-order: bysource - - Let's look at an example: - - First, we need a "context" for the field that has a path - function for converting relative path to an absolute path. - - We'll be careful to do this in an os-independent fashion. - - .. doctest:: - - >>> from zope.configuration.fields import Path - >>> class FauxContext(object): - ... def path(self, p): - ... return os.path.join(os.sep, 'faux', 'context', p) - >>> context = FauxContext() - >>> field = Path().bind(context) - - Lets try an absolute path first: - - .. doctest:: - - >>> import os - >>> p = os.path.join(os.sep, u'a', u'b') - >>> n = field.fromUnicode(p) - >>> n.split(os.sep) - ['', 'a', 'b'] - - This should also work with extra spaces around the path: - - .. doctest:: - - >>> p = " \n %s \n\n " % p - >>> n = field.fromUnicode(p) - >>> n.split(os.sep) - ['', 'a', 'b'] - - Environment variables are expanded: - - .. doctest:: - - >>> os.environ['path-test'] = '42' - >>> with_env = os.path.join(os.sep, u'a', u'${path-test}') - >>> n = field.fromUnicode(with_env) - >>> n.split(os.sep) - ['', 'a', '42'] - - Now try a relative path: - - .. doctest:: - - >>> p = os.path.join(u'a', u'b') - >>> n = field.fromUnicode(p) - >>> n.split(os.sep) - ['', 'faux', 'context', 'a', 'b'] - - The current user is expanded (these are implicitly relative paths): - - .. doctest:: - - >>> old_home = os.environ.get('HOME') - >>> os.environ['HOME'] = os.path.join(os.sep, 'HOME') - >>> n = field.fromUnicode('~') - >>> n.split(os.sep) - ['', 'HOME'] - >>> if old_home: - ... os.environ['HOME'] = old_home - ... else: - ... del os.environ['HOME'] - - -.. autoclass:: Bool - :members: - :member-order: bysource - - .. doctest:: - - >>> from zope.configuration.fields import Bool - >>> Bool().fromUnicode(u"yes") - True - >>> Bool().fromUnicode(u"y") - True - >>> Bool().fromUnicode(u"true") - True - >>> Bool().fromUnicode(u"no") - False - -.. autoclass:: MessageID - :members: - :member-order: bysource - - .. doctest:: - - >>> from zope.configuration.fields import MessageID - >>> class Info(object): - ... file = 'file location' - ... line = 8 - >>> class FauxContext(object): - ... i18n_strings = {} - ... info = Info() - >>> context = FauxContext() - >>> field = MessageID().bind(context) - - There is a fallback domain when no domain has been specified. - - Exchange the warn function so we can make test whether the warning - has been issued - - .. doctest:: - - >>> warned = None - >>> def fakewarn(*args, **kw): #* syntax highlighting - ... global warned - ... warned = args - - >>> import warnings - >>> realwarn = warnings.warn - >>> warnings.warn = fakewarn - - >>> i = field.fromUnicode(u"Hello world!") - >>> i - 'Hello world!' - >>> i.domain - 'untranslated' - >>> warned - ("You did not specify an i18n translation domain for the '' field in file location",) - - >>> warnings.warn = realwarn - - With the domain specified: - - .. doctest:: - - >>> context.i18n_strings = {} - >>> context.i18n_domain = 'testing' - - We can get a message id: - - .. doctest:: - - >>> i = field.fromUnicode(u"Hello world!") - >>> i - 'Hello world!' - >>> i.domain - 'testing' - - In addition, the string has been registered with the context: - - .. doctest:: - - >>> context.i18n_strings - {'testing': {'Hello world!': [('file location', 8)]}} - - >>> i = field.fromUnicode(u"Foo Bar") - >>> i = field.fromUnicode(u"Hello world!") - >>> from pprint import PrettyPrinter - >>> pprint=PrettyPrinter(width=70).pprint - >>> pprint(context.i18n_strings) - {'testing': {'Foo Bar': [('file location', 8)], - 'Hello world!': [('file location', 8), - ('file location', 8)]}} - - >>> from zope.i18nmessageid import Message - >>> isinstance(list(context.i18n_strings['testing'].keys())[0], Message) - True - - Explicit Message IDs - - .. doctest:: - - >>> i = field.fromUnicode(u'[View-Permission] View') - >>> i - 'View-Permission' - >>> i.default - 'View' - - >>> i = field.fromUnicode(u'[] [Some] text') - >>> i - '[Some] text' - >>> i.default is None - True +.. automodule:: zope.configuration.fields diff --git a/docs/conf.py b/docs/conf.py index 9fffd20..ac65d4b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -257,6 +257,7 @@ intersphinx_mapping = { 'https://zopeschema.readthedocs.io/en/latest': None, } -autodoc_default_flags = ['members', 'show-inheritance'] -autoclass_content = 'both' -autodoc_member_order = 'bysource' +autodoc_default_flags = [ + 'members', + 'show-inheritance', +] diff --git a/src/zope/configuration/fields.py b/src/zope/configuration/fields.py index 668c4a3..85a5ff7 100644 --- a/src/zope/configuration/fields.py +++ b/src/zope/configuration/fields.py @@ -33,13 +33,13 @@ from zope.configuration.exceptions import ConfigurationError from zope.configuration.interfaces import InvalidToken __all__ = [ - 'PythonIdentifier', + 'Bool', 'GlobalObject', 'GlobalInterface', - 'Tokens', - 'Path', - 'Bool', 'MessageID', + 'Path', + 'PythonIdentifier', + 'Tokens', ] @@ -47,6 +47,39 @@ class PythonIdentifier(schema_PythonIdentifier): """ This class is like `zope.schema.PythonIdentifier`. + + Let's look at an example: + + >>> from zope.configuration.fields import PythonIdentifier + >>> class FauxContext(object): + ... pass + >>> context = FauxContext() + >>> field = PythonIdentifier().bind(context) + + Let's test the fromUnicode method: + + >>> field.fromUnicode(u'foo') + 'foo' + >>> field.fromUnicode(u'foo3') + 'foo3' + >>> field.fromUnicode(u'_foo3') + '_foo3' + + Now let's see whether validation works alright + + >>> for value in (u'foo', u'foo3', u'foo_', u'_foo3', u'foo_3', u'foo3_'): + ... _ = field.fromUnicode(value) + >>> from zope.schema import ValidationError + >>> for value in (u'3foo', u'foo:', u'\\\\', u''): + ... try: + ... field.fromUnicode(value) + ... except ValidationError: + ... print('Validation Error ' + repr(value)) + Validation Error '3foo' + Validation Error 'foo:' + Validation Error '\\\\' + Validation Error '' + .. versionchanged:: 4.2.0 Extend `zope.schema.PythonIdentifier`, which implies that `fromUnicode` validates the strings. @@ -79,6 +112,43 @@ class GlobalObject(Field): self.value_type.validate(value) def fromUnicode(self, value): + """ + Find and return the module global at the path *value*. + + >>> d = {'x': 1, 'y': 42, 'z': 'zope'} + >>> class fakeresolver(dict): + ... def resolve(self, n): + ... return self[n] + >>> fake = fakeresolver(d) + + >>> from zope.schema import Int + >>> from zope.configuration.fields import GlobalObject + >>> g = GlobalObject(value_type=Int()) + >>> gg = g.bind(fake) + >>> gg.fromUnicode("x") + 1 + >>> gg.fromUnicode(" x \\n ") + 1 + >>> gg.fromUnicode("y") + 42 + >>> gg.fromUnicode("z") + Traceback (most recent call last): + ... + WrongType: ('zope', (<type 'int'>, <type 'long'>), '') + + >>> g = GlobalObject(constraint=lambda x: x%2 == 0) + >>> gg = g.bind(fake) + >>> gg.fromUnicode("x") + Traceback (most recent call last): + ... + ConstraintNotSatisfied: 1 + >>> gg.fromUnicode("y") + 42 + >>> g = GlobalObject() + >>> gg = g.bind(fake) + >>> print(gg.fromUnicode('*')) + None + """ name = str(value.strip()) # special case, mostly for interfaces @@ -108,7 +178,38 @@ class GlobalObject(Field): @implementer(IFromUnicode) class GlobalInterface(GlobalObject): - """An interface that can be accessed from a module. + """ + An interface that can be accessed from a module. + + Example: + + First, we need to set up a stub name resolver: + + >>> from zope.interface import Interface + >>> class IFoo(Interface): + ... pass + >>> class Foo(object): + ... pass + >>> d = {'Foo': Foo, 'IFoo': IFoo} + >>> class fakeresolver(dict): + ... def resolve(self, n): + ... return self[n] + >>> fake = fakeresolver(d) + + Now verify constraints are checked correctly: + + >>> from zope.configuration.fields import GlobalInterface + >>> g = GlobalInterface() + >>> gg = g.bind(fake) + >>> gg.fromUnicode('IFoo') is IFoo + True + >>> gg.fromUnicode(' IFoo ') is IFoo + True + >>> gg.fromUnicode('Foo') + Traceback (most recent call last): + ... + NotAnInterface: (<class 'Foo'>, ... + """ def __init__(self, **kw): super(GlobalInterface, self).__init__(InterfaceField(), **kw) @@ -116,18 +217,57 @@ class GlobalInterface(GlobalObject): @implementer(IFromUnicode) class Tokens(List): - """A list that can be read from a space-separated string. """ - def fromUnicode(self, u): - u = u.strip() - if u: + A list that can be read from a space-separated string. + """ + + def fromUnicode(self, value): + """ + Split the input string and convert it to *value_type*. + + Consider GlobalObject tokens: + + First, we need to set up a stub name resolver: + + >>> d = {'x': 1, 'y': 42, 'z': 'zope', 'x.y.x': 'foo'} + >>> class fakeresolver(dict): + ... def resolve(self, n): + ... return self[n] + >>> fake = fakeresolver(d) + + >>> from zope.configuration.fields import Tokens + >>> from zope.configuration.fields import GlobalObject + >>> g = Tokens(value_type=GlobalObject()) + >>> gg = g.bind(fake) + >>> gg.fromUnicode(" \\n x y z \\n") + [1, 42, 'zope'] + + >>> from zope.schema import Int + >>> g = Tokens(value_type= + ... GlobalObject(value_type= + ... Int(constraint=lambda x: x%2 == 0))) + >>> gg = g.bind(fake) + >>> gg.fromUnicode("x y") + Traceback (most recent call last): + ... + InvalidToken: 1 in x y + + >>> gg.fromUnicode("z y") + Traceback (most recent call last): + ... + InvalidToken: ('zope', (<type 'int'>, <type 'long'>), '') in z y + >>> gg.fromUnicode("y y") + [42, 42] + """ + value = value.strip() + if value: vt = self.value_type.bind(self.context) values = [] - for s in u.split(): + for s in value.split(): try: v = vt.fromUnicode(s) - except ValidationError as v: - raise InvalidToken("%s in %s" % (v, u)).with_field_and_value(self, s) + except ValidationError as ex: + raise InvalidToken("%s in %r" % (ex, value)).with_field_and_value(self, s) else: values.append(v) else: @@ -159,15 +299,76 @@ class PathProcessor(object): @implementer(IFromUnicode) class Path(Text): - """A file path name, which may be input as a relative path + """ + A file path name, which may be input as a relative path Input paths are converted to absolute paths and normalized. - - .. versionchanged:: 4.2.0 - Start expanding home directories and environment variables. """ def fromUnicode(self, value): + """ + Convert the input path to a normalized, absolute path. + + Let's look at an example: + + First, we need a "context" for the field that has a path + function for converting relative path to an absolute path. + + We'll be careful to do this in an os-independent fashion. + + >>> from zope.configuration.fields import Path + >>> class FauxContext(object): + ... def path(self, p): + ... return os.path.join(os.sep, 'faux', 'context', p) + >>> context = FauxContext() + >>> field = Path().bind(context) + + Lets try an absolute path first: + + >>> import os + >>> p = os.path.join(os.sep, u'a', u'b') + >>> n = field.fromUnicode(p) + >>> n.split(os.sep) + ['', 'a', 'b'] + + This should also work with extra spaces around the path: + + >>> p = " \\n %s \\n\\n " % p + >>> n = field.fromUnicode(p) + >>> n.split(os.sep) + ['', 'a', 'b'] + + Environment variables are expanded: + + >>> os.environ['path-test'] = '42' + >>> with_env = os.path.join(os.sep, u'a', u'${path-test}') + >>> n = field.fromUnicode(with_env) + >>> n.split(os.sep) + ['', 'a', '42'] + + Now try a relative path: + + >>> p = os.path.join(u'a', u'b') + >>> n = field.fromUnicode(p) + >>> n.split(os.sep) + ['', 'faux', 'context', 'a', 'b'] + + The current user is expanded (these are implicitly relative paths): + + >>> old_home = os.environ.get('HOME') + >>> os.environ['HOME'] = os.path.join(os.sep, 'HOME') + >>> n = field.fromUnicode('~') + >>> n.split(os.sep) + ['', 'HOME'] + >>> if old_home: + ... os.environ['HOME'] = old_home + ... else: + ... del os.environ['HOME'] + + + .. versionchanged:: 4.2.0 + Start expanding home directories and environment variables. + """ filename, needs_processing = PathProcessor.expand(value) if needs_processing: filename = self.context.path(filename) @@ -177,12 +378,46 @@ class Path(Text): @implementer(IFromUnicode) class Bool(schema_Bool): - """A boolean value + """ + A boolean value. Values may be input (in upper or lower case) as any of: - yes, no, y, n, true, false, t, or f. + + - yes / no + - y / n + - true / false + - t / f + + .. caution:: + + Do not confuse this with :class:`zope.schema.Bool`. + That class will only parse ``"True"`` and ``"true"`` as + `True` values. Any other value will silently be accepted as + `False`. This class raises a validation error for unrecognized + input. + """ + def fromUnicode(self, value): + """ + Convert the input string to a boolean. + + Example: + + >>> from zope.configuration.fields import Bool + >>> Bool().fromUnicode(u"yes") + True + >>> Bool().fromUnicode(u"y") + True + >>> Bool().fromUnicode(u"true") + True + >>> Bool().fromUnicode(u"no") + False + >>> Bool().fromUnicode(u"surprise") + Traceback (most recent call last): + ... + zope.schema._bootstrapinterfaces.InvalidValue + """ value = value.lower() if value in ('1', 'true', 'yes', 't', 'y'): return True @@ -195,15 +430,99 @@ class Bool(schema_Bool): @implementer(IFromUnicode) class MessageID(Text): - """Text string that should be translated. + """ + Text string that should be translated. - When a string is converted to a message ID, it is also - recorded in the context. + When a string is converted to a message ID, it is also recorded in + the context. """ __factories = {} def fromUnicode(self, u): + """ + Translate a string to a MessageID. + + >>> from zope.configuration.fields import MessageID + >>> class Info(object): + ... file = 'file location' + ... line = 8 + >>> class FauxContext(object): + ... i18n_strings = {} + ... info = Info() + >>> context = FauxContext() + >>> field = MessageID().bind(context) + + There is a fallback domain when no domain has been specified. + + Exchange the warn function so we can make test whether the warning + has been issued + + >>> warned = None + >>> def fakewarn(*args, **kw): #* syntax highlighting + ... global warned + ... warned = args + + >>> import warnings + >>> realwarn = warnings.warn + >>> warnings.warn = fakewarn + + >>> i = field.fromUnicode(u"Hello world!") + >>> i + 'Hello world!' + >>> i.domain + 'untranslated' + >>> warned + ("You did not specify an i18n translation domain for the '' field in file location",) + + >>> warnings.warn = realwarn + + With the domain specified: + + >>> context.i18n_strings = {} + >>> context.i18n_domain = 'testing' + + We can get a message id: + + >>> i = field.fromUnicode(u"Hello world!") + >>> i + 'Hello world!' + >>> i.domain + 'testing' + + In addition, the string has been registered with the context: + + >>> context.i18n_strings + {'testing': {'Hello world!': [('file location', 8)]}} + + >>> i = field.fromUnicode(u"Foo Bar") + >>> i = field.fromUnicode(u"Hello world!") + >>> from pprint import PrettyPrinter + >>> pprint=PrettyPrinter(width=70).pprint + >>> pprint(context.i18n_strings) + {'testing': {'Foo Bar': [('file location', 8)], + 'Hello world!': [('file location', 8), + ('file location', 8)]}} + + >>> from zope.i18nmessageid import Message + >>> isinstance(list(context.i18n_strings['testing'].keys())[0], Message) + True + + Explicit Message IDs + + >>> i = field.fromUnicode(u'[View-Permission] View') + >>> i + 'View-Permission' + >>> i.default + 'View' + + >>> i = field.fromUnicode(u'[] [Some] text') + >>> i + '[Some] text' + >>> i.default is None + True + + """ context = self.context domain = getattr(context, 'i18n_domain', '') if not domain: diff --git a/src/zope/configuration/tests/test_docs.py b/src/zope/configuration/tests/test_docs.py index 915d2f0..e51a095 100644 --- a/src/zope/configuration/tests/test_docs.py +++ b/src/zope/configuration/tests/test_docs.py @@ -20,6 +20,7 @@ from __future__ import division from __future__ import print_function import re +import sys import os.path import unittest import doctest @@ -41,6 +42,11 @@ checker = renormalizing.RENormalizing([ (re.compile('b(".*?")'), r"\1"), ]) +optionflags = ( + doctest.NORMALIZE_WHITESPACE + | doctest.ELLIPSIS + | doctest.IGNORE_EXCEPTION_DETAIL +) def test_suite(): here = os.path.dirname(os.path.abspath(__file__)) @@ -64,15 +70,22 @@ def test_suite(): 'xmlconfig.rst', ) + # Plain doctest suites + api_to_test = ( + 'config', + 'docutils', + 'fields', + 'interfaces', + 'name', + 'xmlconfig', + 'zopeconfigure', + ) + paths = [os.path.join(docs, f) for f in doc_files_to_test] paths += [os.path.join(api_docs, f) for f in api_files_to_test] m = manuel.ignore.Manuel() - m += manuel.doctest.Manuel(checker=checker, optionflags=( - doctest.NORMALIZE_WHITESPACE - | doctest.ELLIPSIS - | doctest.IGNORE_EXCEPTION_DETAIL - )) + m += manuel.doctest.Manuel(checker=checker, optionflags=optionflags) m += manuel.codeblock.Manuel() m += manuel.capture.Manuel() @@ -84,4 +97,15 @@ def test_suite(): ) ) + for mod_name in api_to_test: + mod_name = 'zope.configuration.' + mod_name + __import__(mod_name) + suite.addTest( + doctest.DocTestSuite( + sys.modules[mod_name], + checker=checker, + optionflags=optionflags + ) + ) + return suite |