diff --git a/.travis.yml b/.travis.yml index 702504e..8271e8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,6 @@ language: python sudo: false matrix: include: - - python: 2.7 - env: DOCS=1 - # The doctests only run on Python 2.7 - python: 2.7 env: MINIMAL="-t !persistentregistry -t !security" python: @@ -13,12 +10,11 @@ python: - 3.5 - 3.6 - pypy-5.6.0 - - pypy3.3-5.5-alpha script: - coverage run -m zope.testrunner --test-path=src $MINIMAL - - if [[ -n "$DOCS" ]]; then sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html; fi - - if [[ -n "$DOCS" ]]; then coverage run `which sphinx-build` -b doctest -d docs/_build/doctrees docs docs/_build/doctest; fi + - if [[ -z "$MINIMAL" ]]; then sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html; fi + - if [[ -z "$MINIMAL" ]]; then coverage run -a `which sphinx-build` -b doctest -d docs/_build/doctrees docs docs/_build/doctest; fi after_success: - coveralls diff --git a/docs/api/adapter.rst b/docs/api/adapter.rst index 992d9e5..33e8491 100644 --- a/docs/api/adapter.rst +++ b/docs/api/adapter.rst @@ -358,14 +358,21 @@ Let's register some adapters first: >>> gsm.registerAdapter(Comp, [None], I5, 'foo') Now we get all the adapters that are registered for ``ob`` that provide -``I5``: +``I5`` (note that the names are always text strings, meaning that on +Python 2 the names will be ``unicode``): .. doctest:: >>> from zope.component import getAdapters >>> adapters = sorted(getAdapters((ob,), I5)) - >>> [(name, adapter.__class__.__name__) for name, adapter in adapters] - [(u'', 'Comp'), (u'foo', 'Comp')] + >>> [(str(name), adapter.__class__.__name__) for name, adapter in adapters] + [('', 'Comp'), ('foo', 'Comp')] + >>> try: + ... text = unicode + ... except NameError: + ... text = str # Python 3 + >>> [isinstance(name, text) for name, _ in adapters] + [True, True] Note that the output doesn't include None values. If an adapter factory returns None, it is as if it wasn't present. @@ -374,8 +381,8 @@ factory returns None, it is as if it wasn't present. >>> gsm.registerAdapter(lambda context: None, [I1], I5, 'nah') >>> adapters = sorted(getAdapters((ob,), I5)) - >>> [(name, adapter.__class__.__name__) for name, adapter in adapters] - [(u'', 'Comp'), (u'foo', 'Comp')] + >>> [(str(name), adapter.__class__.__name__) for name, adapter in adapters] + [('', 'Comp'), ('foo', 'Comp')] Subscription Adapters @@ -402,4 +409,3 @@ Helpers for Declaring / Testing Adapters from zope.component.testing import tearDown tearDown() - diff --git a/docs/api/interface.rst b/docs/api/interface.rst index fbaf57f..ff456f7 100644 --- a/docs/api/interface.rst +++ b/docs/api/interface.rst @@ -115,7 +115,7 @@ We can register ``IDemo`` as providing more than one interface: False >>> queryInterface('zope.component.tests.examples.IQI') is None True - + >>> provideInterface('', IQI, ITestType) >>> ITestType.providedBy(IQI) True @@ -175,8 +175,8 @@ We can register ``IDemo`` as providing more than one interface: >>> provideInterface('', ISII, ITestType) >>> ITestType.providedBy(ISII) True - >>> searchInterfaceIds(None, 'zope.component.tests.examples.ISII') - [u'zope.component.tests.examples.ISII'] + >>> [str(x) for x in searchInterfaceIds(None, 'zope.component.tests.examples.ISII')] + ['zope.component.tests.examples.ISII'] .. testcleanup:: diff --git a/docs/api/persistent.rst b/docs/api/persistent.rst index 69c9e4b..eaad08f 100644 --- a/docs/api/persistent.rst +++ b/docs/api/persistent.rst @@ -167,11 +167,11 @@ We want to make sure that we see updates corrextly. >>> import persistent >>> import transaction >>> from zope.interface import Interface - >>> from zope.interface import implements + >>> from zope.interface import implementer >>> class IFoo(Interface): ... pass - >>> class Foo(persistent.Persistent): - ... implements(IFoo) + >>> @implementer(IFoo) + ... class Foo(persistent.Persistent): ... name = '' ... def __init__(self, name=''): ... self.name = name diff --git a/docs/event.rst b/docs/event.rst index bc10e8d..bf87472 100644 --- a/docs/event.rst +++ b/docs/event.rst @@ -76,9 +76,10 @@ First create an object class: >>> class IUseless(zope.interface.Interface): ... """Useless object""" - >>> class UselessObject(object): + >>> @zope.interface.implementer(IUseless) + ... class UselessObject(object): ... """Useless object""" - ... zope.interface.implements(IUseless) + Then create an event class: @@ -87,9 +88,9 @@ Then create an event class: >>> class IObjectThrownEvent(zope.component.interfaces.IObjectEvent): ... """An object has been thrown away""" - >>> class ObjectThrownEvent(zope.component.interfaces.ObjectEvent): + >>> @zope.interface.implementer(IObjectThrownEvent) + ... class ObjectThrownEvent(zope.component.interfaces.ObjectEvent): ... """An object has been thrown away""" - ... zope.interface.implements(IObjectThrownEvent) Create an object and an event: diff --git a/docs/factory.rst b/docs/factory.rst index 3e90856..d957da3 100644 --- a/docs/factory.rst +++ b/docs/factory.rst @@ -14,10 +14,10 @@ The Factory Class >>> class IKlass(Interface): ... pass - >>> from zope.interface import implements - >>> class Klass(object): - ... implements(IKlass) - ... + >>> from zope.interface import implementer + >>> @implementer(IKlass) + ... class Klass(object): + ... ... def __init__(self, *args, **kw): #* ... self.args = args ... self.kw = kw @@ -53,14 +53,14 @@ and make sure that the correct class was used to create the object: Since we passed in a couple positional and a keyword argument - + .. doctest:: >>> kl.args (1, 2) >>> kl.kw {'foo': 3} - + >>> factory2(3) 3 >>> factory3(3) @@ -94,16 +94,16 @@ Provided Interfaces >>> implemented = factory.getInterfaces() >>> implemented.isOrExtends(IKlass) True - >>> list(implemented) - [] - + >>> list(implemented) == [IKlass] + True + >>> implemented2 = factory2.getInterfaces() >>> list(implemented2) [] - + >>> implemented3 = factory3.getInterfaces() - >>> list(implemented3) - [] + >>> list(implemented3) == [IFunction] + True The Component Architecture Factory API @@ -113,7 +113,7 @@ The Component Architecture Factory API >>> import zope.component >>> factory = Factory(Klass, 'Klass', 'Klassier') - >>> gsm = zope.component.getGlobalSiteManager() + >>> gsm = zope.component.getGlobalSiteManager() >>> from zope.component.interfaces import IFactory >>> gsm.registerUtility(factory, IFactory, 'klass') @@ -139,15 +139,14 @@ Accessing Provided Interfaces >>> implemented = zope.component.getFactoryInterfaces('klass') >>> implemented.isOrExtends(IKlass) True - >>> [iface for iface in implemented] - [] + >>> [iface for iface in implemented] == [IKlass] + True List of All Factories ~~~~~~~~~~~~~~~~~~~~~ .. doctest:: - >>> [(name, fac.__class__) for name, fac in + >>> [(str(name), fac.__class__) for name, fac in ... zope.component.getFactoriesFor(IKlass)] - [(u'klass', )] - + [('klass', )] diff --git a/docs/hooks.rst b/docs/hooks.rst index 76fc6fd..713d12f 100644 --- a/docs/hooks.rst +++ b/docs/hooks.rst @@ -14,7 +14,7 @@ As long as we haven't set a site, none is being considered current: .. doctest:: >>> from zope.component.hooks import getSite - >>> print getSite() + >>> print(getSite()) None We can also ask for the current component registry (aka site manager @@ -71,7 +71,7 @@ Finally we can unset the site and the global component registry is used again: .. doctest:: >>> setSite() - >>> print getSite() + >>> print(getSite()) None >>> getSiteManager() @@ -86,12 +86,12 @@ useful when writing tests: .. doctest:: >>> import zope.component.hooks - >>> print getSite() + >>> print(getSite()) None >>> with zope.component.hooks.site(site2): ... getSite() is site2 True - >>> print getSite() + >>> print(getSite()) None The site is properly restored even if the body of the with statement @@ -99,7 +99,7 @@ raises an exception: .. doctest:: - >>> print getSite() + >>> print(getSite()) None >>> with zope.component.hooks.site(site2): ... getSite() is site2 @@ -107,6 +107,5 @@ raises an exception: Traceback (most recent call last): ... ValueError: An error in the body - >>> print getSite() + >>> print(getSite()) None - diff --git a/docs/narr.rst b/docs/narr.rst index ddc1de9..a1b7469 100644 --- a/docs/narr.rst +++ b/docs/narr.rst @@ -1,6 +1,8 @@ Zope Component Architecture =========================== +.. currentmodule:: zope.component + This package, together with `zope.interface`, provides facilities for defining, registering and looking up components. There are two basic kinds of components: adapters and utilities. @@ -14,22 +16,23 @@ definition: .. doctest:: + >>> from __future__ import print_function >>> from zope import interface >>> class IGreeter(interface.Interface): ... def greet(): ... "say hello" - >>> class Greeter: - ... interface.implements(IGreeter) + >>> @interface.implementer(IGreeter) + ... class Greeter(object): ... ... def __init__(self, other="world"): ... self.other = other ... ... def greet(self): - ... print "Hello", self.other + ... print(("Hello %s" % (self.other))) -We can register an instance this class using `provideUtility` [1]_: +We can register an instance this class using :func:`provideUtility` [1]_: .. doctest:: @@ -37,9 +40,9 @@ We can register an instance this class using `provideUtility` [1]_: >>> greet = Greeter('bob') >>> component.provideUtility(greet, IGreeter, 'robert') -In this example we registered the utility as providing the `IGreeter` +In this example we registered the utility as providing the ``IGreeter`` interface with a name of 'bob'. We can look the interface up with -either `queryUtility` or `getUtility`: +either :func:`queryUtility` or :func:`getUtility`: .. doctest:: @@ -49,7 +52,7 @@ either `queryUtility` or `getUtility`: >>> component.getUtility(IGreeter, 'robert').greet() Hello bob -`queryUtility` and `getUtility` differ in how failed lookups are handled: +:func:`queryUtility` and :func:`getUtility` differ in how failed lookups are handled: .. doctest:: @@ -63,7 +66,7 @@ either `queryUtility` or `getUtility`: ComponentLookupError: (, 'ted') If a component provides only one interface, as in the example above, -then we can omit the provided interface from the call to `provideUtility`: +then we can omit the provided interface from the call to :func:`provideUtility`: .. doctest:: @@ -95,21 +98,20 @@ for different people: >>> class IPerson(interface.Interface): ... name = interface.Attribute("Name") - >>> class PersonGreeter: - ... - ... component.adapts(IPerson) - ... interface.implements(IGreeter) + >>> @component.adapter(IPerson) + ... @interface.implementer(IGreeter) + ... class PersonGreeter(object): ... ... def __init__(self, person): ... self.person = person ... ... def greet(self): - ... print "Hello", self.person.name + ... print("Hello", self.person.name) The class defines a constructor that takes an argument for every object adapted. -We used `component.adapts` to declare what we adapt. We can find +We used :func:`adapter` to declare what we adapt. We can find out if an object declares that it adapts anything using adaptedBy: .. doctest:: @@ -139,8 +141,8 @@ interface: .. doctest:: - >>> class Person: - ... interface.implements(IPerson) + >>> @interface.implementer(IPerson) + ... class Person(object): ... ... def __init__(self, name): ... self.name = name @@ -156,7 +158,7 @@ how to register the adapter. >>> class BobPersonGreeter(PersonGreeter): ... name = 'Bob' ... def greet(self): - ... print "Hello", self.person.name, "my name is", self.name + ... print("Hello", self.person.name, "my name is", self.name) >>> component.provideAdapter( ... BobPersonGreeter, [IPerson], IGreeter, 'bob') @@ -172,7 +174,7 @@ The arguments can also be provided as keyword arguments: ... factory=TedPersonGreeter, adapts=[IPerson], ... provides=IGreeter, name='ted') -For named adapters, use `queryAdapter`, or `getAdapter`: +For named adapters, use :func:`queryAdapter`, or :func:`getAdapter`: .. doctest:: @@ -182,8 +184,8 @@ For named adapters, use `queryAdapter`, or `getAdapter`: >>> component.getAdapter(Person("Sally"), IGreeter, 'ted').greet() Hello Sally my name is Ted -If an adapter can't be found, `queryAdapter` returns a default value -and `getAdapter` raises an error: +If an adapter can't be found, :func:`queryAdapter` returns a default value +and :func:`getAdapter` raises an error: .. doctest:: @@ -200,18 +202,17 @@ Adapters can adapt multiple objects: .. doctest:: - >>> class TwoPersonGreeter: - ... - ... component.adapts(IPerson, IPerson) - ... interface.implements(IGreeter) + >>> @component.adapter(IPerson, IPerson) + ... @interface.implementer(IGreeter) + ... class TwoPersonGreeter(object): ... ... def __init__(self, person, greeter): ... self.person = person ... self.greeter = greeter ... ... def greet(self): - ... print "Hello", self.person.name - ... print "my name is", self.greeter.name + ... print("Hello", self.person.name) + ... print("my name is", self.greeter.name) >>> component.provideAdapter(TwoPersonGreeter) @@ -221,8 +222,8 @@ parameters given to the adapter and used to query the adapter. This is especially the case when different Interfaces are adapt to (opposed to this example). -To look up a multi-adapter, use either `queryMultiAdapter` or -`getMultiAdapter`: +To look up a multi-adapter, use either :func:`queryMultiAdapter` or +:func:`getMultiAdapter`: .. doctest:: @@ -250,7 +251,7 @@ adapter decorator to declare that a callable object adapts some interfaces In this example, the personJob function simply returns the person's -`job` attribute if present, or None if it's not present. An adapter +``job`` attribute if present, or None if it's not present. An adapter factory can return None to indicate that adaptation wasn't possible. Let's register this adapter and try it out: @@ -302,8 +303,8 @@ Perhaps we have documents: ... summary = interface.Attribute("Document summary") ... body = interface.Attribute("Document text") - >>> class Document: - ... interface.implements(IDocument) + >>> @interface.implementer(IDocument) + ... class Document(object): ... def __init__(self, summary, body): ... self.summary, self.body = summary, body @@ -313,9 +314,9 @@ line: .. doctest:: - >>> class SingleLineSummary: - ... component.adapts(IDocument) - ... interface.implements(IValidate) + >>> @component.adapter(IDocument) + ... @interface.implementer(IValidate) + ... class SingleLineSummary(object): ... ... def __init__(self, doc): ... self.doc = doc @@ -330,10 +331,9 @@ Or we might require the body to be at least 1000 characters in length: .. doctest:: - >>> class AdequateLength: - ... component.adapts(IDocument) - ... interface.implements(IValidate) - ... + >>> @component.adapter(IDocument) + ... @interface.implementer(IValidate) + ... class AdequateLength(object): ... def __init__(self, doc): ... self.doc = doc ... @@ -410,8 +410,8 @@ To register the subscriber above, we define a document-created event: >>> class IDocumentCreated(interface.Interface): ... doc = interface.Attribute("The document that was created") - >>> class DocumentCreated: - ... interface.implements(IDocumentCreated) + >>> @interface.implementer(IDocumentCreated) + ... class DocumentCreated(object): ... ... def __init__(self, doc): ... self.doc = doc @@ -424,7 +424,7 @@ We'll also change our handler definition to: ... def documentCreated(event): ... event.doc.created = datetime.datetime.utcnow() -This marks the handler as an adapter of `IDocumentCreated` events. +This marks the handler as an adapter of ``IDocumentCreated`` events. Now we'll register the handler [1]_: @@ -432,7 +432,7 @@ Now we'll register the handler [1]_: >>> component.provideHandler(documentCreated) -Now, if we can create an event and use the `handle` function to call +Now, if we can create an event and use the :func:`handle` function to call handlers registered for the event: .. doctest:: diff --git a/docs/socketexample.rst b/docs/socketexample.rst index ed4edea..4638667 100644 --- a/docs/socketexample.rst +++ b/docs/socketexample.rst @@ -5,7 +5,7 @@ The component architecture provides an application framework that provides its functionality through loosely-connected components. A *component* can be any Python object and has a particular purpose associated with it. Thus, in a component-based applications you have many small component in contrast to -classical object-oriented development, where you have a few big objects. +classical object-oriented development, where you have a few big objects. Components communicate via specific APIs, which are formally defined by interfaces, which are provided by the `zope.interface` package. *Interfaces* @@ -43,7 +43,7 @@ So let's say that we have a German socket: .. doctest:: - >>> from zope.interface import Interface, implements + >>> from zope.interface import Interface, implementer >>> class IGermanSocket(Interface): ... pass @@ -52,9 +52,9 @@ So let's say that we have a German socket: ... def __repr__(self): ... return '' %self.__class__.__name__ - >>> class GermanSocket(Socket): + >>> @implementer(IGermanSocket) + ... class GermanSocket(Socket): ... """German wall socket.""" - ... implements(IGermanSocket) and we want to convert it to an US socket @@ -68,10 +68,10 @@ store to look for an adapter that we can plug in the wall: .. doctest:: - >>> class GermanToUSSocketAdapter(Socket): - ... implements(IUSSocket) + >>> @implementer(IUSSocket) + ... class GermanToUSSocketAdapter(Socket): ... __used_for__ = IGermanSocket - ... + ... ... def __init__(self, socket): ... self.context = socket @@ -118,7 +118,7 @@ so that the socket now provides the US version: >>> IUSSocket.providedBy(bathroomUS) True -Now you can insert your shaver and get on with your day. +Now you can insert your shaver and get on with your day. After a week you travel for a couple of days to the Prague and you notice that the Czech have yet another socket type: @@ -128,8 +128,9 @@ the Czech have yet another socket type: >>> class ICzechSocket(Interface): ... pass - >>> class CzechSocket(Socket): - ... implements(ICzechSocket) + >>> @implementer(ICzechSocket) + ... class CzechSocket(Socket): + ... pass >>> czech = CzechSocket() @@ -142,7 +143,7 @@ you do not have one: ... #doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... - ComponentLookupError: (, + ComponentLookupError: (, , '') @@ -171,10 +172,10 @@ frequency of the AC current: .. doctest:: - >>> class GermanToUSSocketAdapterAndTransformer(object): - ... implements(IUSSocket) + >>> @implementer(IUSSocket) + ... class GermanToUSSocketAdapterAndTransformer(object): ... __used_for__ = IGermanSocket - ... + ... ... def __init__(self, socket): ... self.context = socket @@ -208,7 +209,7 @@ Clearly, we do not have an adapter for the MP3 player ... #doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... - ComponentLookupError: (, + ComponentLookupError: (, , 'mp3') @@ -222,15 +223,27 @@ to know about all the adapters that convert a German to a US socket type: >>> sockets = list(zope.component.getAdapters((bathroomDE,), IUSSocket)) >>> len(sockets) 3 - >>> names = [name for name, socket in sockets] - >>> names.sort() + >>> names = sorted([str(name) for name, socket in sockets]) >>> names - [u'', u'dvd', u'shaver'] + ['', 'dvd', 'shaver'] -`zope.component.getAdapters()` returns a list of tuples. The first +:func:`zope.component.getAdapters` returns a list of tuples. The first entry of the tuple is the name of the adapter and the second is the adapter itself. +Note that the names are always text strings, meaning ``unicode`` on +Python 2: + +.. doctest:: + + >>> try: + ... text = unicode + ... except NameError: + ... text = str + >>> [isinstance(name, text) for name, _ in sockets] + [True, True, True] + + Multi-Adapters ~~~~~~~~~~~~~~ @@ -253,8 +266,8 @@ buy yet another adapter, but a piece that provides the grounding plug: >>> class IGrounder(Interface): ... pass - >>> class Grounder(object): - ... implements(IGrounder) + >>> @implementer(IGrounder) + ... class Grounder(object): ... def __repr__(self): ... return '' @@ -263,8 +276,8 @@ Then together they will provided a grounded us socket: .. doctest:: - >>> class GroundedGermanToUSSocketAdapter(object): - ... implements(IUSGroundedSocket) + >>> @implementer(IUSGroundedSocket) + ... class GroundedGermanToUSSocketAdapter(object): ... __used_for__ = (IGermanSocket, IGrounder) ... def __init__(self, socket, grounder): ... self.socket, self.grounder = socket, grounder @@ -293,7 +306,7 @@ we can now get a grounded US socket: .. doctest:: - >>> socket = zope.component.getMultiAdapter((livingroom, grounder), + >>> socket = zope.component.getMultiAdapter((livingroom, grounder), ... IUSGroundedSocket, 'mp3') .. doctest:: @@ -314,8 +327,8 @@ Of course, you do not have a 'dvd' grounded US socket available: ... #doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... - ComponentLookupError: ((, - ), + ComponentLookupError: ((, + ), , 'dvd') @@ -345,8 +358,9 @@ Let's say one of our adapters overheated and caused a small fire: >>> class IFire(Interface): ... pass - >>> class Fire(object): - ... implements(IFire) + >>> @implementer(IFire) + ... class Fire(object): + ... pass >>> fire = Fire() @@ -358,12 +372,18 @@ We want to use all available objects to put out the fire: ... def extinguish(): ... pass - >>> class FireExtinguisher(object): + >>> from functools import total_ordering + >>> @total_ordering + ... class FireExtinguisher(object): ... def __init__(self, fire): ... pass ... def extinguish(self): ... "Place extinguish code here." - ... print 'Used ' + self.__class__.__name__ + '.' + ... print('Used ' + self.__class__.__name__ + '.') + ... def __lt__(self, other): + ... return type(self).__name__ < type(other).__name__ + ... def __eq__(self, other): + ... return self is other Here some specific methods to put out the fire: @@ -371,7 +391,7 @@ Here some specific methods to put out the fire: >>> class PowderExtinguisher(FireExtinguisher): ... pass - >>> gsm.registerSubscriptionAdapter(PowderExtinguisher, + >>> gsm.registerSubscriptionAdapter(PowderExtinguisher, ... (IFire,), IFireExtinguisher) >>> class Blanket(FireExtinguisher): @@ -396,7 +416,7 @@ Now let use all these things to put out the fire: Used SprinklerSystem. If no subscribers are found for a particular object, then an empty list is -returned: +returned: .. doctest:: @@ -429,8 +449,8 @@ electric generator. The generator provides of course a US-style socket: .. doctest:: - >>> class Generator(object): - ... implements(IUSSocket) + >>> @implementer(IUSSocket) + ... class Generator(object): ... def __repr__(self): ... return '' @@ -492,8 +512,8 @@ panels that provide a regular US-style socket as output: .. doctest:: - >>> class SolarPanel(object): - ... implements(IUSSocket) + >>> @implementer(IUSSocket) + ... class SolarPanel(object): ... def __repr__(self): ... return '' @@ -537,11 +557,9 @@ following API function will return a list of name/utility pairs: .. doctest:: - >>> utils = list(zope.component.getUtilitiesFor(IUSSocket)) - >>> utils.sort() - >>> utils #doctest: +NORMALIZE_WHITESPACE - [(u'', ), - (u'Solar Panel', )] + >>> utils = sorted(list(zope.component.getUtilitiesFor(IUSSocket))) + >>> [(str(name), socket) for name, socket in utils] + [('', ), ('Solar Panel', )] Another method of looking up all utilities is by using `getAllUtilitiesRegisteredFor(iface)`. This function will return an iterable @@ -551,8 +569,8 @@ need this method. .. doctest:: - >>> utils = list(zope.component.getAllUtilitiesRegisteredFor(IUSSocket)) - >>> utils.sort() + >>> utils = sorted(list(zope.component.getAllUtilitiesRegisteredFor(IUSSocket)), + ... key=lambda x: type(x).__name__) >>> utils [, ] @@ -577,7 +595,7 @@ the solar panel. To do this, we can use a standard implementation of the .. doctest:: >>> from zope.component.factory import Factory - >>> factory = Factory(SolarPanel, + >>> factory = Factory(SolarPanel, ... 'Solar Panel', ... 'This factory creates a solar panel.') @@ -631,11 +649,9 @@ providing a certain interface: .. doctest:: >>> factories = zope.component.getFactoriesFor(IUSSocket) - >>> factories = [(name, factory.__class__) for name, factory in factories] - >>> factories.sort() - >>> factories #doctest: +NORMALIZE_WHITESPACE - [(u'Generator', ), - (u'SolarPanel', )] + >>> factories = sorted([(name, factory.__class__) for name, factory in factories]) + >>> [(str(name), kind) for name, kind in factories] + [('Generator', ), ('SolarPanel', )] Site Managers diff --git a/docs/testlayer.rst b/docs/testlayer.rst index 5388d51..98dc96c 100644 --- a/docs/testlayer.rst +++ b/docs/testlayer.rst @@ -21,16 +21,16 @@ own. We do this simply by subclassing: >>> class OurLayer(LayerBase): ... def setUp(self): ... super(OurLayer, self).setUp() - ... print "setUp called" + ... print("setUp called") ... def tearDown(self): ... super(OurLayer, self).tearDown() - ... print "tearDown called" + ... print("tearDown called") ... def testSetUp(self): ... super(OurLayer, self).testSetUp() - ... print "testSetUp called" + ... print("testSetUp called") ... def testTearDown(self): ... super(OurLayer, self).testTearDown() - ... print "testTearDown called" + ... print("testTearDown called") Note that if we wanted to ensure that the methods of the superclass were called we have to use super(). In this case we actually wouldn't @@ -54,7 +54,7 @@ Now we run some tests with this layer: ... layer = layer ... ... def testFoo(self): - ... print "testFoo" + ... print("testFoo") >>> suite = unittest.TestSuite() >>> suite.addTest(unittest.makeSuite(TestCase)) >>> from zope.testrunner.runner import Runner @@ -108,4 +108,3 @@ Since the ZCML sets up an adapter, we expect the tests to pass: Ran 1 tests with 0 failures, 0 errors and 0 skipped in ... seconds. Tearing down left over layers: Tear down zope.component.testfiles.ZCMLFileLayer in ... seconds. - diff --git a/docs/zcml.rst b/docs/zcml.rst index cb94284..f5b4f63 100644 --- a/docs/zcml.rst +++ b/docs/zcml.rst @@ -15,7 +15,7 @@ This helper will let us easily execute ZCML snippets: .. doctest:: - >>> from cStringIO import StringIO + >>> from io import BytesIO >>> from zope.configuration.xmlconfig import xmlconfig >>> def runSnippet(snippet): ... template = """\ @@ -23,7 +23,7 @@ This helper will let us easily execute ZCML snippets: ... i18n_domain="zope"> ... %s ... """ - ... xmlconfig(StringIO(template % snippet)) + ... xmlconfig(BytesIO((template % snippet).encode("ascii"))) adapter ------- @@ -225,8 +225,10 @@ instance of the ``Content`` class: .. doctest:: >>> import zope.interface - >>> class MyContent: - ... zope.interface.implements(IContent) + >>> @zope.interface.implementer(IContent) + ... class MyContent(object): + ... pass + >>> zope.component.getAdapter(MyContent(), I1, '') # doctest: +ELLIPSIS Traceback (most recent call last): ... @@ -495,7 +497,7 @@ adapter: >>> p = ProxyFactory(ob) >>> a = I1(p) >>> type(a) - + <... 'zope.security...proxy...Proxy...'> While the adapter is security-proxied, the object it adapts is now proxy-free. The adapter has umlimited access to it: @@ -544,7 +546,7 @@ adapter: >>> p = ProxyFactory(ob) >>> a = I1(p) >>> type(a) - + <... 'zope.security...proxy...Proxy...'> Since we protected the adapter with a permission, we now encounter a location proxy behind the security proxy: @@ -576,7 +578,7 @@ permission (``zope.Public``), there will be no location proxy: >>> p = ProxyFactory(ob) >>> a = I1(p) >>> type(a) - + <... 'zope.security...proxy...Proxy...'> >>> a = removeSecurityProxy(a) >>> type(a) is A1 @@ -601,7 +603,7 @@ get location proxies: >>> p = ProxyFactory(ob) >>> a = I1(p) >>> type(a) - + <... 'zope.security...proxy...Proxy...'> >>> a = removeSecurityProxy(a) >>> type(a) @@ -614,7 +616,7 @@ subscriber With the directive you can register subscription adapters or event subscribers with the adapter registry. Consider this very typical example of a directive: - + .. doctest:: >>> clearZCML() @@ -644,7 +646,7 @@ components, such as the ZCML filename and line numbers: >>> sm = zope.component.getSiteManager() >>> doc = [reg.info for reg in sm.registeredSubscriptionAdapters() ... if reg.provided is IS][0] - >>> print doc + >>> print(doc) File "", line 4.2-9.8 Could not read source. @@ -818,7 +820,7 @@ unproxied access to it, but the subscriber itself is proxied: >>> p = ProxyFactory(content) >>> a3 = zope.component.subscribers((p, a1), IS)[0] >>> type(a3) - + <... 'zope.security...proxy...Proxy...'> There's no location proxy behind the security proxy: @@ -866,7 +868,7 @@ With a proxied object, we again get a security-proxied subscriber: >>> a3 = zope.component.subscribers((p, a1), IS)[0] >>> type(a3) - + <... 'zope.security...proxy...Proxy...'> >>> removeSecurityProxy(a3).context[0] is content True @@ -1129,6 +1131,10 @@ First we provide a stub configuration context: .. doctest:: >>> import re, pprint + >>> try: + ... from cStringIO import StringIO + ... except ImportError: + ... from io import StringIO >>> atre = re.compile(' at [0-9a-fA-Fx]+') >>> class Context(object): ... actions = () @@ -1139,7 +1145,7 @@ First we provide a stub configuration context: ... pprinter = pprint.PrettyPrinter(stream=stream, width=60) ... pprinter.pprint(self.actions) ... r = stream.getvalue() - ... return (''.join(atre.split(r))).strip() + ... return (u''.join(atre.split(r))).strip() >>> context = Context() Then we provide a test interface that we'd like to register: @@ -1168,7 +1174,7 @@ However, after calling the directive handler... ((None, , ('', - , + , )),) ...it does provide ``ITestType``: diff --git a/tox.ini b/tox.ini index a39c893..5a67188 100644 --- a/tox.ini +++ b/tox.ini @@ -15,8 +15,10 @@ deps = [testenv] deps = {[fulldeps]deps} + .[docs] commands = zope-testrunner --test-path=src + sphinx-build -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest [testenv:py27-minimal] basepython =