summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorTres Seaver <tseaver@palladion.com>2012-03-26 20:56:58 +0000
committerTres Seaver <tseaver@palladion.com>2012-03-26 20:56:58 +0000
commitbc4d8bc1569c9d4dc28c62272a5acbaebc04cdfe (patch)
tree605f4701a12fc03dc084e6115d6f07ef1a152029 /docs
parent2fd652d0d095b79e2e6365b00c739f589bf631a5 (diff)
downloadzope-interface-bc4d8bc1569c9d4dc28c62272a5acbaebc04cdfe.tar.gz
Merge from LP branch.
Diffstat (limited to 'docs')
-rw-r--r--docs/Makefile153
-rw-r--r--docs/README.rst829
-rw-r--r--docs/README.ru.rst803
-rw-r--r--docs/adapter.rst543
-rw-r--r--docs/adapter.ru.rst540
-rw-r--r--docs/conf.py246
-rw-r--r--docs/foodforthought.rst61
-rw-r--r--docs/human.rst152
-rw-r--r--docs/human.ru.rst156
-rw-r--r--docs/index.rst38
-rw-r--r--docs/make.bat190
-rw-r--r--docs/verify.rst127
12 files changed, 3838 insertions, 0 deletions
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d34ea74
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,153 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/zopeinterface.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/zopeinterface.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/zopeinterface"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/zopeinterface"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/docs/README.rst b/docs/README.rst
new file mode 100644
index 0000000..253d1ed
--- /dev/null
+++ b/docs/README.rst
@@ -0,0 +1,829 @@
+==========
+Interfaces
+==========
+
+Interfaces are objects that specify (document) the external behavior
+of objects that "provide" them. An interface specifies behavior
+through:
+
+- Informal documentation in a doc string
+
+- Attribute definitions
+
+- Invariants, which are conditions that must hold for objects that
+ provide the interface
+
+Attribute definitions specify specific attributes. They define the
+attribute name and provide documentation and constraints of attribute
+values. Attribute definitions can take a number of forms, as we'll
+see below.
+
+Defining interfaces
+===================
+
+Interfaces are defined using Python class statements::
+
+ >>> import zope.interface
+ >>> class IFoo(zope.interface.Interface):
+ ... """Foo blah blah"""
+ ...
+ ... x = zope.interface.Attribute("""X blah blah""")
+ ...
+ ... def bar(q, r=None):
+ ... """bar blah blah"""
+
+In the example above, we've created an interface, `IFoo`. We
+subclassed `zope.interface.Interface`, which is an ancestor interface for
+all interfaces, much as `object` is an ancestor of all new-style
+classes [#create]_. The interface is not a class, it's an Interface,
+an instance of `InterfaceClass`::
+
+ >>> type(IFoo)
+ <class 'zope.interface.interface.InterfaceClass'>
+
+We can ask for the interface's documentation::
+
+ >>> IFoo.__doc__
+ 'Foo blah blah'
+
+and its name::
+
+ >>> IFoo.__name__
+ 'IFoo'
+
+and even its module::
+
+ >>> IFoo.__module__
+ '__main__'
+
+The interface defined two attributes:
+
+`x`
+ This is the simplest form of attribute definition. It has a name
+ and a doc string. It doesn't formally specify anything else.
+
+`bar`
+ This is a method. A method is defined via a function definition. A
+ method is simply an attribute constrained to be a callable with a
+ particular signature, as provided by the function definition.
+
+ Note that `bar` doesn't take a `self` argument. Interfaces document
+ how an object is *used*. When calling instance methods, you don't
+ pass a `self` argument, so a `self` argument isn't included in the
+ interface signature. The `self` argument in instance methods is
+ really an implementation detail of Python instances. Other objects,
+ besides instances can provide interfaces and their methods might not
+ be instance methods. For example, modules can provide interfaces and
+ their methods are usually just functions. Even instances can have
+ methods that are not instance methods.
+
+You can access the attributes defined by an interface using mapping
+syntax::
+
+ >>> x = IFoo['x']
+ >>> type(x)
+ <class 'zope.interface.interface.Attribute'>
+ >>> x.__name__
+ 'x'
+ >>> x.__doc__
+ 'X blah blah'
+
+ >>> IFoo.get('x').__name__
+ 'x'
+
+ >>> IFoo.get('y')
+
+You can use `in` to determine if an interface defines a name::
+
+ >>> 'x' in IFoo
+ True
+
+You can iterate over interfaces to get the names they define::
+
+ >>> names = list(IFoo)
+ >>> names.sort()
+ >>> names
+ ['bar', 'x']
+
+Remember that interfaces aren't classes. You can't access attribute
+definitions as attributes of interfaces::
+
+ >>> IFoo.x
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'InterfaceClass' object has no attribute 'x'
+
+Methods provide access to the method signature::
+
+ >>> bar = IFoo['bar']
+ >>> bar.getSignatureString()
+ '(q, r=None)'
+
+TODO
+ Methods really should have a better API. This is something that
+ needs to be improved.
+
+Declaring interfaces
+====================
+
+Having defined interfaces, we can *declare* that objects provide
+them. Before we describe the details, lets define some terms:
+
+*provide*
+ We say that objects *provide* interfaces. If an object provides an
+ interface, then the interface specifies the behavior of the
+ object. In other words, interfaces specify the behavior of the
+ objects that provide them.
+
+*implement*
+ We normally say that classes *implement* interfaces. If a class
+ implements an interface, then the instances of the class provide
+ the interface. Objects provide interfaces that their classes
+ implement [#factory]_. (Objects can provide interfaces directly,
+ in addition to what their classes implement.)
+
+ It is important to note that classes don't usually provide the
+ interfaces that they implement.
+
+ We can generalize this to factories. For any callable object we
+ can declare that it produces objects that provide some interfaces
+ by saying that the factory implements the interfaces.
+
+Now that we've defined these terms, we can talk about the API for
+declaring interfaces.
+
+Declaring implemented interfaces
+--------------------------------
+
+The most common way to declare interfaces is using the implements
+function in a class statement::
+
+ >>> class Foo:
+ ... zope.interface.implements(IFoo)
+ ...
+ ... def __init__(self, x=None):
+ ... self.x = x
+ ...
+ ... def bar(self, q, r=None):
+ ... return q, r, self.x
+ ...
+ ... def __repr__(self):
+ ... return "Foo(%s)" % self.x
+
+
+In this example, we declared that `Foo` implements `IFoo`. This means
+that instances of `Foo` provide `IFoo`. Having made this declaration,
+there are several ways we can introspect the declarations. First, we
+can ask an interface whether it is implemented by a class::
+
+ >>> IFoo.implementedBy(Foo)
+ True
+
+And we can ask whether an interface is provided by an object::
+
+ >>> foo = Foo()
+ >>> IFoo.providedBy(foo)
+ True
+
+Of course, `Foo` doesn't provide `IFoo`, it implements it::
+
+ >>> IFoo.providedBy(Foo)
+ False
+
+We can also ask what interfaces are implemented by an object::
+
+ >>> list(zope.interface.implementedBy(Foo))
+ [<InterfaceClass __main__.IFoo>]
+
+It's an error to ask for interfaces implemented by a non-callable
+object::
+
+ >>> IFoo.implementedBy(foo)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('ImplementedBy called for non-factory', Foo(None))
+
+ >>> list(zope.interface.implementedBy(foo))
+ Traceback (most recent call last):
+ ...
+ TypeError: ('ImplementedBy called for non-factory', Foo(None))
+
+Similarly, we can ask what interfaces are provided by an object::
+
+ >>> list(zope.interface.providedBy(foo))
+ [<InterfaceClass __main__.IFoo>]
+ >>> list(zope.interface.providedBy(Foo))
+ []
+
+We can declare interfaces implemented by other factories (besides
+classes). We do this using a Python-2.4-style decorator named
+`implementer`. In versions of Python before 2.4, this looks like::
+
+ >>> def yfoo(y):
+ ... foo = Foo()
+ ... foo.y = y
+ ... return foo
+ >>> yfoo = zope.interface.implementer(IFoo)(yfoo)
+
+ >>> list(zope.interface.implementedBy(yfoo))
+ [<InterfaceClass __main__.IFoo>]
+
+Note that the implementer decorator may modify it's argument. Callers
+should not assume that a new object is created.
+
+Using implementer also works on callable objects. This is used by
+zope.formlib, as an example.
+
+ >>> class yfactory:
+ ... def __call__(self, y):
+ ... foo = Foo()
+ ... foo.y = y
+ ... return foo
+ >>> yfoo = yfactory()
+ >>> yfoo = zope.interface.implementer(IFoo)(yfoo)
+
+ >>> list(zope.interface.implementedBy(yfoo))
+ [<InterfaceClass __main__.IFoo>]
+
+XXX: Double check and update these version numbers:
+
+In zope.interface 3.5.2 and lower, the implementer decorator can not
+be used for classes, but in 3.6.0 and higher it can:
+
+ >>> Foo = zope.interface.implementer(IFoo)(Foo)
+ >>> list(zope.interface.providedBy(Foo()))
+ [<InterfaceClass __main__.IFoo>]
+
+Note that class decorators using the @implementer(IFoo) syntax are only
+supported in Python 2.6 and later.
+
+
+Declaring provided interfaces
+-----------------------------
+
+We can declare interfaces directly provided by objects. Suppose that
+we want to document what the `__init__` method of the `Foo` class
+does. It's not *really* part of `IFoo`. You wouldn't normally call
+the `__init__` method on Foo instances. Rather, the `__init__` method
+is part of the `Foo`'s `__call__` method::
+
+ >>> class IFooFactory(zope.interface.Interface):
+ ... """Create foos"""
+ ...
+ ... def __call__(x=None):
+ ... """Create a foo
+ ...
+ ... The argument provides the initial value for x ...
+ ... """
+
+It's the class that provides this interface, so we declare the
+interface on the class::
+
+ >>> zope.interface.directlyProvides(Foo, IFooFactory)
+
+And then, we'll see that Foo provides some interfaces::
+
+ >>> list(zope.interface.providedBy(Foo))
+ [<InterfaceClass __main__.IFooFactory>]
+ >>> IFooFactory.providedBy(Foo)
+ True
+
+Declaring class interfaces is common enough that there's a special
+declaration function for it, `classProvides`, that allows the
+declaration from within a class statement::
+
+ >>> class Foo2:
+ ... zope.interface.implements(IFoo)
+ ... zope.interface.classProvides(IFooFactory)
+ ...
+ ... def __init__(self, x=None):
+ ... self.x = x
+ ...
+ ... def bar(self, q, r=None):
+ ... return q, r, self.x
+ ...
+ ... def __repr__(self):
+ ... return "Foo(%s)" % self.x
+
+ >>> list(zope.interface.providedBy(Foo2))
+ [<InterfaceClass __main__.IFooFactory>]
+ >>> IFooFactory.providedBy(Foo2)
+ True
+
+There's a similar function, `moduleProvides`, that supports interface
+declarations from within module definitions. For example, see the use
+of `moduleProvides` call in `zope.interface.__init__`, which declares that
+the package `zope.interface` provides `IInterfaceDeclaration`.
+
+Sometimes, we want to declare interfaces on instances, even though
+those instances get interfaces from their classes. Suppose we create
+a new interface, `ISpecial`::
+
+ >>> class ISpecial(zope.interface.Interface):
+ ... reason = zope.interface.Attribute("Reason why we're special")
+ ... def brag():
+ ... "Brag about being special"
+
+We can make an existing foo instance special by providing `reason`
+and `brag` attributes::
+
+ >>> foo.reason = 'I just am'
+ >>> def brag():
+ ... return "I'm special!"
+ >>> foo.brag = brag
+ >>> foo.reason
+ 'I just am'
+ >>> foo.brag()
+ "I'm special!"
+
+and by declaring the interface::
+
+ >>> zope.interface.directlyProvides(foo, ISpecial)
+
+then the new interface is included in the provided interfaces::
+
+ >>> ISpecial.providedBy(foo)
+ True
+ >>> list(zope.interface.providedBy(foo))
+ [<InterfaceClass __main__.ISpecial>, <InterfaceClass __main__.IFoo>]
+
+We can find out what interfaces are directly provided by an object::
+
+ >>> list(zope.interface.directlyProvidedBy(foo))
+ [<InterfaceClass __main__.ISpecial>]
+
+ >>> newfoo = Foo()
+ >>> list(zope.interface.directlyProvidedBy(newfoo))
+ []
+
+Inherited declarations
+----------------------
+
+Normally, declarations are inherited::
+
+ >>> class SpecialFoo(Foo):
+ ... zope.interface.implements(ISpecial)
+ ... reason = 'I just am'
+ ... def brag(self):
+ ... return "I'm special because %s" % self.reason
+
+ >>> list(zope.interface.implementedBy(SpecialFoo))
+ [<InterfaceClass __main__.ISpecial>, <InterfaceClass __main__.IFoo>]
+
+ >>> list(zope.interface.providedBy(SpecialFoo()))
+ [<InterfaceClass __main__.ISpecial>, <InterfaceClass __main__.IFoo>]
+
+Sometimes, you don't want to inherit declarations. In that case, you
+can use `implementsOnly`, instead of `implements`::
+
+ >>> class Special(Foo):
+ ... zope.interface.implementsOnly(ISpecial)
+ ... reason = 'I just am'
+ ... def brag(self):
+ ... return "I'm special because %s" % self.reason
+
+ >>> list(zope.interface.implementedBy(Special))
+ [<InterfaceClass __main__.ISpecial>]
+
+ >>> list(zope.interface.providedBy(Special()))
+ [<InterfaceClass __main__.ISpecial>]
+
+External declarations
+---------------------
+
+Normally, we make implementation declarations as part of a class
+definition. Sometimes, we may want to make declarations from outside
+the class definition. For example, we might want to declare interfaces
+for classes that we didn't write. The function `classImplements` can
+be used for this purpose::
+
+ >>> class C:
+ ... pass
+
+ >>> zope.interface.classImplements(C, IFoo)
+ >>> list(zope.interface.implementedBy(C))
+ [<InterfaceClass __main__.IFoo>]
+
+We can use `classImplementsOnly` to exclude inherited interfaces::
+
+ >>> class C(Foo):
+ ... pass
+
+ >>> zope.interface.classImplementsOnly(C, ISpecial)
+ >>> list(zope.interface.implementedBy(C))
+ [<InterfaceClass __main__.ISpecial>]
+
+
+
+Declaration Objects
+-------------------
+
+When we declare interfaces, we create *declaration* objects. When we
+query declarations, declaration objects are returned::
+
+ >>> type(zope.interface.implementedBy(Special))
+ <class 'zope.interface.declarations.Implements'>
+
+Declaration objects and interface objects are similar in many ways. In
+fact, they share a common base class. The important thing to realize
+about them is that they can be used where interfaces are expected in
+declarations. Here's a silly example::
+
+ >>> class Special2(Foo):
+ ... zope.interface.implementsOnly(
+ ... zope.interface.implementedBy(Foo),
+ ... ISpecial,
+ ... )
+ ... reason = 'I just am'
+ ... def brag(self):
+ ... return "I'm special because %s" % self.reason
+
+The declaration here is almost the same as
+``zope.interface.implements(ISpecial)``, except that the order of
+interfaces in the resulting declaration is different::
+
+ >>> list(zope.interface.implementedBy(Special2))
+ [<InterfaceClass __main__.IFoo>, <InterfaceClass __main__.ISpecial>]
+
+
+Interface Inheritance
+=====================
+
+Interfaces can extend other interfaces. They do this simply by listing
+the other interfaces as base interfaces::
+
+ >>> class IBlat(zope.interface.Interface):
+ ... """Blat blah blah"""
+ ...
+ ... y = zope.interface.Attribute("y blah blah")
+ ... def eek():
+ ... """eek blah blah"""
+
+ >>> IBlat.__bases__
+ (<InterfaceClass zope.interface.Interface>,)
+
+ >>> class IBaz(IFoo, IBlat):
+ ... """Baz blah"""
+ ... def eek(a=1):
+ ... """eek in baz blah"""
+ ...
+
+ >>> IBaz.__bases__
+ (<InterfaceClass __main__.IFoo>, <InterfaceClass __main__.IBlat>)
+
+ >>> names = list(IBaz)
+ >>> names.sort()
+ >>> names
+ ['bar', 'eek', 'x', 'y']
+
+Note that `IBaz` overrides eek::
+
+ >>> IBlat['eek'].__doc__
+ 'eek blah blah'
+ >>> IBaz['eek'].__doc__
+ 'eek in baz blah'
+
+We were careful to override eek in a compatible way. When extending
+an interface, the extending interface should be compatible [#compat]_
+with the extended interfaces.
+
+We can ask whether one interface extends another::
+
+ >>> IBaz.extends(IFoo)
+ True
+ >>> IBlat.extends(IFoo)
+ False
+
+Note that interfaces don't extend themselves::
+
+ >>> IBaz.extends(IBaz)
+ False
+
+Sometimes we wish they did, but we can, instead use `isOrExtends`::
+
+ >>> IBaz.isOrExtends(IBaz)
+ True
+ >>> IBaz.isOrExtends(IFoo)
+ True
+ >>> IFoo.isOrExtends(IBaz)
+ False
+
+When we iterate over an interface, we get all of the names it defines,
+including names defined by base interfaces. Sometimes, we want *just*
+the names defined by the interface directly. We bane use the `names`
+method for that::
+
+ >>> list(IBaz.names())
+ ['eek']
+
+Inheritance of attribute specifications
+---------------------------------------
+
+An interface may override attribute definitions from base interfaces.
+If two base interfaces define the same attribute, the attribute is
+inherited from the most specific interface. For example, with::
+
+ >>> class IBase(zope.interface.Interface):
+ ...
+ ... def foo():
+ ... "base foo doc"
+
+ >>> class IBase1(IBase):
+ ... pass
+
+ >>> class IBase2(IBase):
+ ...
+ ... def foo():
+ ... "base2 foo doc"
+
+ >>> class ISub(IBase1, IBase2):
+ ... pass
+
+ISub's definition of foo is the one from IBase2, since IBase2 is more
+specific that IBase::
+
+ >>> ISub['foo'].__doc__
+ 'base2 foo doc'
+
+Note that this differs from a depth-first search.
+
+Sometimes, it's useful to ask whether an interface defines an
+attribute directly. You can use the direct method to get a directly
+defined definitions::
+
+ >>> IBase.direct('foo').__doc__
+ 'base foo doc'
+
+ >>> ISub.direct('foo')
+
+Specifications
+--------------
+
+Interfaces and declarations are both special cases of specifications.
+What we described above for interface inheritance applies to both
+declarations and specifications. Declarations actually extend the
+interfaces that they declare::
+
+ >>> class Baz(object):
+ ... zope.interface.implements(IBaz)
+
+ >>> baz_implements = zope.interface.implementedBy(Baz)
+ >>> baz_implements.__bases__
+ (<InterfaceClass __main__.IBaz>, <implementedBy ...object>)
+
+ >>> baz_implements.extends(IFoo)
+ True
+
+ >>> baz_implements.isOrExtends(IFoo)
+ True
+ >>> baz_implements.isOrExtends(baz_implements)
+ True
+
+Specifications (interfaces and declarations) provide an `__sro__`
+that lists the specification and all of it's ancestors::
+
+ >>> baz_implements.__sro__
+ (<implementedBy __main__.Baz>,
+ <InterfaceClass __main__.IBaz>,
+ <InterfaceClass __main__.IFoo>,
+ <InterfaceClass __main__.IBlat>,
+ <InterfaceClass zope.interface.Interface>,
+ <implementedBy ...object>)
+
+
+Tagged Values
+=============
+
+Interfaces and attribute descriptions support an extension mechanism,
+borrowed from UML, called "tagged values" that lets us store extra
+data::
+
+ >>> IFoo.setTaggedValue('date-modified', '2004-04-01')
+ >>> IFoo.setTaggedValue('author', 'Jim Fulton')
+ >>> IFoo.getTaggedValue('date-modified')
+ '2004-04-01'
+ >>> IFoo.queryTaggedValue('date-modified')
+ '2004-04-01'
+ >>> IFoo.queryTaggedValue('datemodified')
+ >>> tags = list(IFoo.getTaggedValueTags())
+ >>> tags.sort()
+ >>> tags
+ ['author', 'date-modified']
+
+Function attributes are converted to tagged values when method
+attribute definitions are created::
+
+ >>> class IBazFactory(zope.interface.Interface):
+ ... def __call__():
+ ... "create one"
+ ... __call__.return_type = IBaz
+
+ >>> IBazFactory['__call__'].getTaggedValue('return_type')
+ <InterfaceClass __main__.IBaz>
+
+Tagged values can also be defined from within an interface definition::
+
+ >>> class IWithTaggedValues(zope.interface.Interface):
+ ... zope.interface.taggedValue('squish', 'squash')
+ >>> IWithTaggedValues.getTaggedValue('squish')
+ 'squash'
+
+Invariants
+==========
+
+Interfaces can express conditions that must hold for objects that
+provide them. These conditions are expressed using one or more
+invariants. Invariants are callable objects that will be called with
+an object that provides an interface. An invariant raises an `Invalid`
+exception if the condition doesn't hold. Here's an example::
+
+ >>> class RangeError(zope.interface.Invalid):
+ ... """A range has invalid limits"""
+ ... def __repr__(self):
+ ... return "RangeError(%r)" % self.args
+
+ >>> def range_invariant(ob):
+ ... if ob.max < ob.min:
+ ... raise RangeError(ob)
+
+Given this invariant, we can use it in an interface definition::
+
+ >>> class IRange(zope.interface.Interface):
+ ... min = zope.interface.Attribute("Lower bound")
+ ... max = zope.interface.Attribute("Upper bound")
+ ...
+ ... zope.interface.invariant(range_invariant)
+
+Interfaces have a method for checking their invariants::
+
+ >>> class Range(object):
+ ... zope.interface.implements(IRange)
+ ...
+ ... def __init__(self, min, max):
+ ... self.min, self.max = min, max
+ ...
+ ... def __repr__(self):
+ ... return "Range(%s, %s)" % (self.min, self.max)
+
+ >>> IRange.validateInvariants(Range(1,2))
+ >>> IRange.validateInvariants(Range(1,1))
+ >>> IRange.validateInvariants(Range(2,1))
+ Traceback (most recent call last):
+ ...
+ RangeError: Range(2, 1)
+
+If you have multiple invariants, you may not want to stop checking
+after the first error. If you pass a list to `validateInvariants`,
+then a single `Invalid` exception will be raised with the list of
+exceptions as it's argument::
+
+ >>> from zope.interface.exceptions import Invalid
+ >>> errors = []
+ >>> try:
+ ... IRange.validateInvariants(Range(2,1), errors)
+ ... except Invalid, e:
+ ... str(e)
+ '[RangeError(Range(2, 1))]'
+
+And the list will be filled with the individual exceptions::
+
+ >>> errors
+ [RangeError(Range(2, 1))]
+
+
+ >>> del errors[:]
+
+Adaptation
+==========
+
+Interfaces can be called to perform adaptation.
+
+The semantics are based on those of the PEP 246 adapt function.
+
+If an object cannot be adapted, then a TypeError is raised::
+
+ >>> class I(zope.interface.Interface):
+ ... pass
+
+ >>> I(0)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt', 0, <InterfaceClass __main__.I>)
+
+
+
+unless an alternate value is provided as a second positional argument::
+
+ >>> I(0, 'bob')
+ 'bob'
+
+If an object already implements the interface, then it will be returned::
+
+ >>> class C(object):
+ ... zope.interface.implements(I)
+
+ >>> obj = C()
+ >>> I(obj) is obj
+ True
+
+If an object implements __conform__, then it will be used::
+
+ >>> class C(object):
+ ... zope.interface.implements(I)
+ ... def __conform__(self, proto):
+ ... return 0
+
+ >>> I(C())
+ 0
+
+Adapter hooks (see __adapt__) will also be used, if present::
+
+ >>> from zope.interface.interface import adapter_hooks
+ >>> def adapt_0_to_42(iface, obj):
+ ... if obj == 0:
+ ... return 42
+
+ >>> adapter_hooks.append(adapt_0_to_42)
+ >>> I(0)
+ 42
+
+ >>> adapter_hooks.remove(adapt_0_to_42)
+ >>> I(0)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt', 0, <InterfaceClass __main__.I>)
+
+__adapt__
+---------
+
+ >>> class I(zope.interface.Interface):
+ ... pass
+
+Interfaces implement the PEP 246 __adapt__ method.
+
+This method is normally not called directly. It is called by the PEP
+246 adapt framework and by the interface __call__ operator.
+
+The adapt method is responsible for adapting an object to the
+reciever.
+
+The default version returns None::
+
+ >>> I.__adapt__(0)
+
+unless the object given provides the interface::
+
+ >>> class C(object):
+ ... zope.interface.implements(I)
+
+ >>> obj = C()
+ >>> I.__adapt__(obj) is obj
+ True
+
+Adapter hooks can be provided (or removed) to provide custom
+adaptation. We'll install a silly hook that adapts 0 to 42.
+We install a hook by simply adding it to the adapter_hooks
+list::
+
+ >>> from zope.interface.interface import adapter_hooks
+ >>> def adapt_0_to_42(iface, obj):
+ ... if obj == 0:
+ ... return 42
+
+ >>> adapter_hooks.append(adapt_0_to_42)
+ >>> I.__adapt__(0)
+ 42
+
+Hooks must either return an adapter, or None if no adapter can
+be found.
+
+Hooks can be uninstalled by removing them from the list::
+
+ >>> adapter_hooks.remove(adapt_0_to_42)
+ >>> I.__adapt__(0)
+
+
+.. [#create] The main reason we subclass `Interface` is to cause the
+ Python class statement to create an interface, rather
+ than a class.
+
+ It's possible to create interfaces by calling a special
+ interface class directly. Doing this, it's possible
+ (and, on rare occasions, useful) to create interfaces
+ that don't descend from `Interface`. Using this
+ technique is beyond the scope of this document.
+
+.. [#factory] Classes are factories. They can be called to create
+ their instances. We expect that we will eventually
+ extend the concept of implementation to other kinds of
+ factories, so that we can declare the interfaces
+ provided by the objects created.
+
+.. [#compat] The goal is substitutability. An object that provides an
+ extending interface should be substitutable for an object
+ that provides the extended interface. In our example, an
+ object that provides IBaz should be usable whereever an
+ object that provides IBlat is expected.
+
+ The interface implementation doesn't enforce this.
+ but maybe it should do some checks.
diff --git a/docs/README.ru.rst b/docs/README.ru.rst
new file mode 100644
index 0000000..a284c9a
--- /dev/null
+++ b/docs/README.ru.rst
@@ -0,0 +1,803 @@
+==========
+Интерфейсы
+==========
+
+.. contents::
+
+Интерфейсы - это объекты специфицирующие (документирующие) внешнее поведение
+объектов которые их "предоставляют". Интерфейсы определяют поведение через
+следующие составляющие:
+
+- Неформальную документацию в строках документации
+
+- Определения атрибутов
+
+- Инварианты - условия, которые должны соблюдаться для объектов предоставляющих
+ интерфейс
+
+Определения атрибутов описывают конкретные атрибуты. Они определяют
+имя атрибута и предоставляют документацию и ограничения для значений
+атрибута. Определения атрибутов могут быть заданы несколькими путями
+как мы увидим ниже.
+
+Определение интерфейсов
+=======================
+
+Интерфейсы определяются с использованием ключевого слова class::
+
+ >>> import zope.interface
+ >>> class IFoo(zope.interface.Interface):
+ ... """Foo blah blah"""
+ ...
+ ... x = zope.interface.Attribute("""X blah blah""")
+ ...
+ ... def bar(q, r=None):
+ ... """bar blah blah"""
+
+В примере выше мы создали интерфейс `IFoo`. Мы наследуем его от
+класса `zope.interface.Interface`, который является родительским интерфейсом
+для всех интерфейсов, как `object` - это родительский класс для всех новых
+классов [#create]_. Данный интерфейс не является классом, а является
+Интерфейсом, экземпляром `InterfaceClass`::
+
+ >>> type(IFoo)
+ <class 'zope.interface.interface.InterfaceClass'>
+
+Мы можем запросить у интерфейса его документацию::
+
+ >>> IFoo.__doc__
+ 'Foo blah blah'
+
+и его имя::
+
+ >>> IFoo.__name__
+ 'IFoo'
+
+и даже модуль в котором он определен::
+
+ >>> IFoo.__module__
+ '__main__'
+
+Наш интерфейс определяет два атрибута:
+
+`x`
+ Это простейшая форма определения атрибутов. Определяются имя
+ и строка документации. Формально здесь не определяется ничего более.
+
+`bar`
+ Это метод. Методы определяются как обычные функции. Метод - это просто
+ атрибут который должен быть вызываемым с указанием сигнатуры,
+ предоставляемой определением функции.
+
+ Надо отметить, что аргумент `self` не указывается для `bar`. Интерфейс
+ документирует как объект *используется*. Когда методы экземпляров классов
+ вызываются мы не передаем аргумент `self`, таким образом аргумент `self`
+ не включается и в сигнатуру интерфейса. Аргумент `self` в методах
+ экземпляров классов на самом деле деталь реализации экземпляров классов
+ в Python. Другие объекты кроме экземпляров классов могут предоставлять
+ интерфейсы и их методы могут не быть методами экземпляров классов. Для
+ примера модули могут предоставлять интерфейсы и их методы обычно просто
+ функции. Даже экземпляры могут иметь методы не являющиеся методами
+ экземпляров класса.
+
+Мы можем получить доступ к атрибутам определенным интерфейсом используя
+синтаксис доступа к элементам массива::
+
+ >>> x = IFoo['x']
+ >>> type(x)
+ <class 'zope.interface.interface.Attribute'>
+ >>> x.__name__
+ 'x'
+ >>> x.__doc__
+ 'X blah blah'
+
+ >>> IFoo.get('x').__name__
+ 'x'
+
+ >>> IFoo.get('y')
+
+Можно использовать `in` для определения содержит ли интерфейс
+определенное имя::
+
+ >>> 'x' in IFoo
+ True
+
+Мы можем использовать итератор для интерфейсов что бы получить все имена
+которые интерфейсы определяют::
+
+ >>> names = list(IFoo)
+ >>> names.sort()
+ >>> names
+ ['bar', 'x']
+
+Надо помнить, что интерфейсы не являются классами. Мы не можем получить
+доступ к определениям атрибутов через доступ к атрибутам интерфейсов::
+
+ >>> IFoo.x
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: 'InterfaceClass' object has no attribute 'x'
+
+Методы также предоставляют доступ к сигнатуре метода::
+
+ >>> bar = IFoo['bar']
+ >>> bar.getSignatureString()
+ '(q, r=None)'
+
+Объявление интерфейсов
+======================
+
+Определив интерфейс мы можем теперь *объявить*, что объекты предоставляют их.
+Перед описанием деталей определим некоторые термины:
+
+*предоставлять*
+ Мы говорим, что объекты *предоставляют* интерфейсы. Если объект
+ предоставляет интерфейс, тогда интерфейс специфицирует поведение объекта.
+ Другими словами, интерфейсы специфицируют поведение объектов которые
+ предоставляют их.
+
+*реализовать*
+ Мы обычно говорим что классы *реализуют* интерфейсы. Если класс
+ реализует интерфейс, тогда экземпляры этого класса предоставляют
+ данный интерфейс. Объекты предоставляют интерфейсы которые их классы
+ реализуют [#factory]_. (Объекты также могут предоставлять интерфейсы напрямую
+ плюс к тем которые реализуют их классы.)
+
+ Важно помнить, что классы обычно не предоставляют интерфейсы которые
+ они реализуют.
+
+ Мы можем обобщить это до фабрик. Для любого вызываемого объекта мы можем
+ объявить что он производит объекты которые предоставляют какие-либо
+ интерфейсы сказав, что фабрика реализует данные интерфейсы.
+
+Теперь после того как мы определили эти термины мы можем поговорить об
+API для объявления интерфейсов.
+
+Объявление реализуемых интерфейсов
+----------------------------------
+
+Наиболее часто используемый путь для объявления интерфейсов - это использование
+функции implements в определении класса::
+
+ >>> class Foo:
+ ... zope.interface.implements(IFoo)
+ ...
+ ... def __init__(self, x=None):
+ ... self.x = x
+ ...
+ ... def bar(self, q, r=None):
+ ... return q, r, self.x
+ ...
+ ... def __repr__(self):
+ ... return "Foo(%s)" % self.x
+
+В этом примере мы объявили, что `Foo` реализует `IFoo`. Это значит, что
+экземпляры `Foo` предоставляют `IFoo`. После данного объявления есть
+несколько путей для анализа объявлений. Во-первых мы можем спросить
+что интерфейс реализован классом::
+
+ >>> IFoo.implementedBy(Foo)
+ True
+
+Также мы можем спросить если интерфейс предоставляется объектами класса::
+
+ >>> foo = Foo()
+ >>> IFoo.providedBy(foo)
+ True
+
+Конечно `Foo` не предоставляет `IFoo`, он реализует его::
+
+ >>> IFoo.providedBy(Foo)
+ False
+
+Мы можем также узнать какие интерфейсы реализуются объектами::
+
+ >>> list(zope.interface.implementedBy(Foo))
+ [<InterfaceClass __main__.IFoo>]
+
+Это ошибка спрашивать про интерфейсы реализуемые не вызываемым объектом::
+
+ >>> IFoo.implementedBy(foo)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('ImplementedBy called for non-factory', Foo(None))
+
+ >>> list(zope.interface.implementedBy(foo))
+ Traceback (most recent call last):
+ ...
+ TypeError: ('ImplementedBy called for non-factory', Foo(None))
+
+Также можно узнать какие интерфейсы предоставляются объектами::
+
+ >>> list(zope.interface.providedBy(foo))
+ [<InterfaceClass __main__.IFoo>]
+ >>> list(zope.interface.providedBy(Foo))
+ []
+
+Мы можем объявить интерфейсы реализуемые другими фабриками (кроме классов).
+Это можно сделать используя декоратор `implementer` (в стиле Python 2.4).
+Для версий Python ниже 2.4 это будет выглядеть следующим образом::
+
+ >>> def yfoo(y):
+ ... foo = Foo()
+ ... foo.y = y
+ ... return foo
+ >>> yfoo = zope.interface.implementer(IFoo)(yfoo)
+
+ >>> list(zope.interface.implementedBy(yfoo))
+ [<InterfaceClass __main__.IFoo>]
+
+Надо заметить, что декоратор implementer может модифицировать свои аргументы.
+Вызывающая сторона не должна предполагать, что всегда будет создаваться
+новый объект.
+
+XXX: Double check and update these version numbers, and translate to russian:
+
+In zope.interface 3.5.1 and lower, the implementer decorator can not
+be used for classes, but in 3.5.2 and higher it can:
+
+ >>> Foo = zope.interface.implementer(IFoo)(Foo)
+ >>> list(zope.interface.providedBy(Foo()))
+ [<InterfaceClass __main__.IFoo>]
+
+Note that class decorators using the @implementer(IFoo) syntax are only
+supported in Python 2.6 and later.
+
+
+Объявление предоставляемых интерфейсов
+--------------------------------------
+
+Мы можем объявлять интерфейсы напрямую предоставляемые объектами. Предположим
+что мы хотим документировать что делает метод `__init__` класса `Foo`. Это
+*точно* не часть `IFoo`. Обычно мы не должны напрямую вызывать метод `__init__`
+для экземпляров Foo. Скорее метод `__init__` является частью метода `__call__`
+класса `Foo`::
+
+ >>> class IFooFactory(zope.interface.Interface):
+ ... """Create foos"""
+ ...
+ ... def __call__(x=None):
+ ... """Create a foo
+ ...
+ ... The argument provides the initial value for x ...
+ ... """
+
+У нас есть класс предоставляющий данный интерфейс, таким образом мы можем
+объявить интерфейс класса::
+
+ >>> zope.interface.directlyProvides(Foo, IFooFactory)
+
+Теперь мы видим, что Foo уже предоставляет интерфейсы::
+
+ >>> list(zope.interface.providedBy(Foo))
+ [<InterfaceClass __main__.IFooFactory>]
+ >>> IFooFactory.providedBy(Foo)
+ True
+
+Объявление интерфейсов класса достаточно частая операция и для нее есть
+специальная функция объявления `classProvides`, которая позволяет объявлять
+интерфейсы при определении класса::
+
+ >>> class Foo2:
+ ... zope.interface.implements(IFoo)
+ ... zope.interface.classProvides(IFooFactory)
+ ...
+ ... def __init__(self, x=None):
+ ... self.x = x
+ ...
+ ... def bar(self, q, r=None):
+ ... return q, r, self.x
+ ...
+ ... def __repr__(self):
+ ... return "Foo(%s)" % self.x
+
+ >>> list(zope.interface.providedBy(Foo2))
+ [<InterfaceClass __main__.IFooFactory>]
+ >>> IFooFactory.providedBy(Foo2)
+ True
+
+Похожая функция `moduleProvides` поддерживает объявление интерфейсов при
+определении модуля. Для примера смотрите использование вызова
+`moduleProvides` в `zope.interface.__init__`, который объявляет, что
+пакет `zope.interface` предоставляет `IInterfaceDeclaration`.
+
+Иногда мы хотим объявить интерфейсы экземпляров, даже если эти экземпляры
+уже берут интерфейсы от своих классов. Предположим, что мы создаем новый
+интерфейс `ISpecial`::
+
+ >>> class ISpecial(zope.interface.Interface):
+ ... reason = zope.interface.Attribute("Reason why we're special")
+ ... def brag():
+ ... "Brag about being special"
+
+Мы можем сделать созданный экземпляр foo специальным предоставив атрибуты
+`reason` и `brag`::
+
+ >>> foo.reason = 'I just am'
+ >>> def brag():
+ ... return "I'm special!"
+ >>> foo.brag = brag
+ >>> foo.reason
+ 'I just am'
+ >>> foo.brag()
+ "I'm special!"
+
+и объявив интерфейс::
+
+ >>> zope.interface.directlyProvides(foo, ISpecial)
+
+таким образом новый интерфейс включается в список предоставляемых интерфейсов::
+
+ >>> ISpecial.providedBy(foo)
+ True
+ >>> list(zope.interface.providedBy(foo))
+ [<InterfaceClass __main__.ISpecial>, <InterfaceClass __main__.IFoo>]
+
+Мы также можем определить, что интерфейсы напрямую предоставляются
+объектами::
+
+ >>> list(zope.interface.directlyProvidedBy(foo))
+ [<InterfaceClass __main__.ISpecial>]
+
+ >>> newfoo = Foo()
+ >>> list(zope.interface.directlyProvidedBy(newfoo))
+ []
+
+Наследуемые объявления
+----------------------
+
+Обычно объявления наследуются::
+
+ >>> class SpecialFoo(Foo):
+ ... zope.interface.implements(ISpecial)
+ ... reason = 'I just am'
+ ... def brag(self):
+ ... return "I'm special because %s" % self.reason
+
+ >>> list(zope.interface.implementedBy(SpecialFoo))
+ [<InterfaceClass __main__.ISpecial>, <InterfaceClass __main__.IFoo>]
+
+ >>> list(zope.interface.providedBy(SpecialFoo()))
+ [<InterfaceClass __main__.ISpecial>, <InterfaceClass __main__.IFoo>]
+
+Иногда мы не хотим наследовать объявления. В этом случае мы можем
+использовать `implementsOnly` вместо `implements`::
+
+ >>> class Special(Foo):
+ ... zope.interface.implementsOnly(ISpecial)
+ ... reason = 'I just am'
+ ... def brag(self):
+ ... return "I'm special because %s" % self.reason
+
+ >>> list(zope.interface.implementedBy(Special))
+ [<InterfaceClass __main__.ISpecial>]
+
+ >>> list(zope.interface.providedBy(Special()))
+ [<InterfaceClass __main__.ISpecial>]
+
+Внешние объявления
+------------------
+
+Обычно мы создаем объявления реализации как часть объявления класса. Иногда
+мы можем захотеть создать объявления вне объявления класса. Для примера,
+мы можем хотеть объявить интерфейсы для классов которые писали не мы.
+Для этого может использоваться функция `classImplements`::
+
+ >>> class C:
+ ... pass
+
+ >>> zope.interface.classImplements(C, IFoo)
+ >>> list(zope.interface.implementedBy(C))
+ [<InterfaceClass __main__.IFoo>]
+
+Мы можем использовать `classImplementsOnly` для исключения наследуемых
+интерфейсов::
+
+ >>> class C(Foo):
+ ... pass
+
+ >>> zope.interface.classImplementsOnly(C, ISpecial)
+ >>> list(zope.interface.implementedBy(C))
+ [<InterfaceClass __main__.ISpecial>]
+
+Объекты объявлений
+------------------
+
+Когда мы объявляем интерфейсы мы создаем объект *объявления*. Когда мы
+запрашиваем объявления возвращается объект объявления::
+
+ >>> type(zope.interface.implementedBy(Special))
+ <class 'zope.interface.declarations.Implements'>
+
+Объекты объявления и объекты интерфейсов во многом похожи друг на друга.
+На самом деле они даже имеют общий базовый класс. Важно понять, что они могут
+использоваться там где в объявлениях ожидаются интерфейсы. Вот простой
+пример::
+
+ >>> class Special2(Foo):
+ ... zope.interface.implementsOnly(
+ ... zope.interface.implementedBy(Foo),
+ ... ISpecial,
+ ... )
+ ... reason = 'I just am'
+ ... def brag(self):
+ ... return "I'm special because %s" % self.reason
+
+Объявление здесь практически такое же как
+``zope.interface.implements(ISpecial)``, отличие только в порядке
+интерфейсов в итоговом объявления::
+
+ >>> list(zope.interface.implementedBy(Special2))
+ [<InterfaceClass __main__.IFoo>, <InterfaceClass __main__.ISpecial>]
+
+Наследование интерфейсов
+========================
+
+Интерфейсы могут расширять другие интерфейсы. Они делают это просто
+показывая эти интерфейсы как базовые::
+
+ >>> class IBlat(zope.interface.Interface):
+ ... """Blat blah blah"""
+ ...
+ ... y = zope.interface.Attribute("y blah blah")
+ ... def eek():
+ ... """eek blah blah"""
+
+ >>> IBlat.__bases__
+ (<InterfaceClass zope.interface.Interface>,)
+
+ >>> class IBaz(IFoo, IBlat):
+ ... """Baz blah"""
+ ... def eek(a=1):
+ ... """eek in baz blah"""
+ ...
+
+ >>> IBaz.__bases__
+ (<InterfaceClass __main__.IFoo>, <InterfaceClass __main__.IBlat>)
+
+ >>> names = list(IBaz)
+ >>> names.sort()
+ >>> names
+ ['bar', 'eek', 'x', 'y']
+
+Заметим, что `IBaz` переопределяет eek::
+
+ >>> IBlat['eek'].__doc__
+ 'eek blah blah'
+ >>> IBaz['eek'].__doc__
+ 'eek in baz blah'
+
+Мы были осторожны переопределяя eek совместимым путем. Когда интерфейс
+расширяется, расширенный интерфейс должен быть совместимым [#compat]_ с
+расширяемыми интерфейсами.
+
+Мы можем запросить расширяет ли один из интерфейсов другой::
+
+ >>> IBaz.extends(IFoo)
+ True
+ >>> IBlat.extends(IFoo)
+ False
+
+Заметим, что интерфейсы не расширяют сами себя::
+
+ >>> IBaz.extends(IBaz)
+ False
+
+Иногда мы можем хотеть что бы они расширяли сами себя, но вместо этого
+мы можем использовать `isOrExtends`::
+
+ >>> IBaz.isOrExtends(IBaz)
+ True
+ >>> IBaz.isOrExtends(IFoo)
+ True
+ >>> IFoo.isOrExtends(IBaz)
+ False
+
+Когда мы применяем итерацию к интерфейсу мы получаем все имена которые он
+определяет включая имена определенные для базовых интерфейсов. Иногда
+мы хотим получить *только* имена определенные интерфейсом напрямую.
+Для этого мы используем метод `names`::
+
+ >>> list(IBaz.names())
+ ['eek']
+
+Наследование в случае определения атрибутов
+--------------------------------------------
+
+Интерфейс может переопределять определения атрибутов из базовых интерфейсов.
+Если два базовых интерфейса определяют один и тот же атрибут атрибут
+наследуется от более специфичного интерфейса. Для примера::
+
+ >>> class IBase(zope.interface.Interface):
+ ...
+ ... def foo():
+ ... "base foo doc"
+
+ >>> class IBase1(IBase):
+ ... pass
+
+ >>> class IBase2(IBase):
+ ...
+ ... def foo():
+ ... "base2 foo doc"
+
+ >>> class ISub(IBase1, IBase2):
+ ... pass
+
+Определение ISub для foo будет из IBase2 т.к. IBase2 более специфичен для
+IBase::
+
+ >>> ISub['foo'].__doc__
+ 'base2 foo doc'
+
+Заметим, что это отличается от поиска в глубину.
+
+Иногда полезно узнать, что интерфейс определяет атрибут напрямую. Мы можем
+использовать метод direct для получения напрямую определенных атрибутов::
+
+ >>> IBase.direct('foo').__doc__
+ 'base foo doc'
+
+ >>> ISub.direct('foo')
+
+Спецификации
+------------
+
+Интерфейсы и объявления - это специальные случаи спецификаций. Описание
+выше для наследования интерфейсов можно применить и к объявлениям и
+к спецификациям. Объявления фактически расширяют интерфейсы которые они
+объявляют::
+
+ >>> class Baz(object):
+ ... zope.interface.implements(IBaz)
+
+ >>> baz_implements = zope.interface.implementedBy(Baz)
+ >>> baz_implements.__bases__
+ (<InterfaceClass __main__.IBaz>, <implementedBy ...object>)
+
+ >>> baz_implements.extends(IFoo)
+ True
+
+ >>> baz_implements.isOrExtends(IFoo)
+ True
+ >>> baz_implements.isOrExtends(baz_implements)
+ True
+
+Спецификации (интерфейсы и объявления) предоставляют атрибут `__sro__`
+который описывает спецификацию и всех ее предков::
+
+ >>> baz_implements.__sro__
+ (<implementedBy __main__.Baz>,
+ <InterfaceClass __main__.IBaz>,
+ <InterfaceClass __main__.IFoo>,
+ <InterfaceClass __main__.IBlat>,
+ <InterfaceClass zope.interface.Interface>,
+ <implementedBy ...object>)
+
+Помеченные значения
+===================
+
+Интерфейсы и описания атрибутов поддерживают механизм расширения
+заимствованный из UML и называемый "помеченные значения" который позволяет
+сохранять дополнительные данные::
+
+ >>> IFoo.setTaggedValue('date-modified', '2004-04-01')
+ >>> IFoo.setTaggedValue('author', 'Jim Fulton')
+ >>> IFoo.getTaggedValue('date-modified')
+ '2004-04-01'
+ >>> IFoo.queryTaggedValue('date-modified')
+ '2004-04-01'
+ >>> IFoo.queryTaggedValue('datemodified')
+ >>> tags = list(IFoo.getTaggedValueTags())
+ >>> tags.sort()
+ >>> tags
+ ['author', 'date-modified']
+
+Атрибуты функций конвертируются в помеченные значения когда создаются
+определения атрибутов метода::
+
+ >>> class IBazFactory(zope.interface.Interface):
+ ... def __call__():
+ ... "create one"
+ ... __call__.return_type = IBaz
+
+ >>> IBazFactory['__call__'].getTaggedValue('return_type')
+ <InterfaceClass __main__.IBaz>
+
+Помеченные значения также могут быть определены внутри определения
+интерфейса::
+
+ >>> class IWithTaggedValues(zope.interface.Interface):
+ ... zope.interface.taggedValue('squish', 'squash')
+ >>> IWithTaggedValues.getTaggedValue('squish')
+ 'squash'
+
+Инварианты
+==========
+
+Интерфейсы могут описывать условия которые должны быть соблюдены для объектов
+которые их предоставляют. Эти условия описываются используя один или более
+инвариантов. Инварианты - это вызываемые объекты которые будут вызваны
+с объектом предоставляющим интерфейс в качестве параметра. Инвариант
+должен выкинуть исключение `Invalid` если условие не соблюдено. Например::
+
+ >>> class RangeError(zope.interface.Invalid):
+ ... """A range has invalid limits"""
+ ... def __repr__(self):
+ ... return "RangeError(%r)" % self.args
+
+ >>> def range_invariant(ob):
+ ... if ob.max < ob.min:
+ ... raise RangeError(ob)
+
+Определив этот инвариант мы можем использовать его в определении интерфейсов::
+
+ >>> class IRange(zope.interface.Interface):
+ ... min = zope.interface.Attribute("Lower bound")
+ ... max = zope.interface.Attribute("Upper bound")
+ ...
+ ... zope.interface.invariant(range_invariant)
+
+Интерфейсы имеют метод для проверки своих инвариантов::
+
+ >>> class Range(object):
+ ... zope.interface.implements(IRange)
+ ...
+ ... def __init__(self, min, max):
+ ... self.min, self.max = min, max
+ ...
+ ... def __repr__(self):
+ ... return "Range(%s, %s)" % (self.min, self.max)
+
+ >>> IRange.validateInvariants(Range(1,2))
+ >>> IRange.validateInvariants(Range(1,1))
+ >>> IRange.validateInvariants(Range(2,1))
+ Traceback (most recent call last):
+ ...
+ RangeError: Range(2, 1)
+
+В случае нескольких инвариантов мы можем захотеть остановить проверку после
+первой ошибки. Если мы передадим в `validateInvariants` пустой список тогда
+будет выкинуто единственное исключение `Invalid` со списком исключений
+как аргументом::
+
+ >>> from zope.interface.exceptions import Invalid
+ >>> errors = []
+ >>> try:
+ ... IRange.validateInvariants(Range(2,1), errors)
+ ... except Invalid, e:
+ ... str(e)
+ '[RangeError(Range(2, 1))]'
+
+И список будет заполнен индивидуальными исключениями::
+
+ >>> errors
+ [RangeError(Range(2, 1))]
+
+ >>> del errors[:]
+
+Адаптация
+=========
+
+Интерфейсы могут быть вызваны для осуществления адаптации. Эта семантика
+основана на функции adapt из PEP 246. Если объект не может быть адаптирован
+будет выкинут TypeError::
+
+ >>> class I(zope.interface.Interface):
+ ... pass
+
+ >>> I(0)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt', 0, <InterfaceClass __main__.I>)
+
+только если альтернативное значение не передано как второй аргумент::
+
+ >>> I(0, 'bob')
+ 'bob'
+
+Если объект уже реализует нужный интерфейс он будет возвращен::
+
+ >>> class C(object):
+ ... zope.interface.implements(I)
+
+ >>> obj = C()
+ >>> I(obj) is obj
+ True
+
+Если объект реализует __conform__, тогда она будет использована::
+
+ >>> class C(object):
+ ... zope.interface.implements(I)
+ ... def __conform__(self, proto):
+ ... return 0
+
+ >>> I(C())
+ 0
+
+Также если присутствуют функции для вызова адаптации (см. __adapt__) они будут
+использованы::
+
+ >>> from zope.interface.interface import adapter_hooks
+ >>> def adapt_0_to_42(iface, obj):
+ ... if obj == 0:
+ ... return 42
+
+ >>> adapter_hooks.append(adapt_0_to_42)
+ >>> I(0)
+ 42
+
+ >>> adapter_hooks.remove(adapt_0_to_42)
+ >>> I(0)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt', 0, <InterfaceClass __main__.I>)
+
+
+__adapt__
+---------
+
+ >>> class I(zope.interface.Interface):
+ ... pass
+
+Интерфейсы реализуют метод __adapt__ из PEP 246. Этот метод обычно не
+вызывается напрямую. Он вызывается архитектурой адаптации из PEP 246 и методом
+__call__ интерфейсов. Метод адаптации отвечает за адаптацию объекта к
+получателю. Версия по умолчанию возвращает None::
+
+ >>> I.__adapt__(0)
+
+если только переданный объект не предоставляет нужный интерфейс::
+
+ >>> class C(object):
+ ... zope.interface.implements(I)
+
+ >>> obj = C()
+ >>> I.__adapt__(obj) is obj
+ True
+
+Функции для вызова адаптации могут быть добавлены (или удалены) для
+предоставления адаптации "на заказ". Мы установим глупую функцию которая
+адаптирует 0 к 42. Мы устанавливаем функцию просто добавляя ее к списку
+adapter_hooks::
+
+ >>> from zope.interface.interface import adapter_hooks
+ >>> def adapt_0_to_42(iface, obj):
+ ... if obj == 0:
+ ... return 42
+
+ >>> adapter_hooks.append(adapt_0_to_42)
+ >>> I.__adapt__(0)
+ 42
+
+Функции должны возвращать либо адаптер, либо None если адаптер не найден.
+Функции могут быть удалены удалением их из списка::
+
+ >>> adapter_hooks.remove(adapt_0_to_42)
+ >>> I.__adapt__(0)
+
+
+.. [#create] Основная причина по которой мы наследуемся от `Interface` - это
+ что бы быть уверенными в том, что ключевое слово class будет
+ создавать интерфейс, а не класс.
+
+ Есть возможность создать интерфейсы вызвав специальный
+ класс интерфейса напрямую. Делая это, возможно (и в редких
+ случаях полезно) создать интерфейсы которые не наследуются
+ от `Interface`. Однако использование этой техники выходит
+ за рамки данного документа.
+
+.. [#factory] Классы - это фабрики. Они могут быть вызваны для создания
+ своих экземпляров. Мы ожидаем что в итоге мы расширим
+ концепцию реализации на другие типы фабрик, таким образом
+ мы сможем объявлять интерфейсы предоставляемые созданными
+ фабриками объектами.
+
+.. [#compat] Цель - заменяемость. Объект который предоставляет расширенный
+ интерфейс должен быть заменяем в качестве объектов которые
+ предоставляют расширяемый интерфейс. В нашем примере объект
+ который предоставляет IBaz должен быть используемым и
+ в случае если ожидается объект который предоставляет IBlat.
+
+ Реализация интерфейса не требует этого. Но возможно в дальнейшем
+ она должна будет делать какие-либо проверки.
diff --git a/docs/adapter.rst b/docs/adapter.rst
new file mode 100644
index 0000000..298a862
--- /dev/null
+++ b/docs/adapter.rst
@@ -0,0 +1,543 @@
+================
+Adapter Registry
+================
+
+Adapter registries provide a way to register objects that depend on
+one or more interface specifications and provide (perhaps indirectly)
+some interface. In addition, the registrations have names. (You can
+think of the names as qualifiers of the provided interfaces.)
+
+The term "interface specification" refers both to interfaces and to
+interface declarations, such as declarations of interfaces implemented
+by a class.
+
+
+Single Adapters
+===============
+
+Let's look at a simple example, using a single required specification::
+
+ >>> from zope.interface.adapter import AdapterRegistry
+ >>> import zope.interface
+
+ >>> class IR1(zope.interface.Interface):
+ ... pass
+ >>> class IP1(zope.interface.Interface):
+ ... pass
+ >>> class IP2(IP1):
+ ... pass
+
+ >>> registry = AdapterRegistry()
+
+We'll register an object that depends on IR1 and "provides" IP2::
+
+ >>> registry.register([IR1], IP2, '', 12)
+
+Given the registration, we can look it up again::
+
+ >>> registry.lookup([IR1], IP2, '')
+ 12
+
+Note that we used an integer in the example. In real applications,
+one would use some objects that actually depend on or provide
+interfaces. The registry doesn't care about what gets registered, so
+we'll use integers and strings to keep the examples simple. There is
+one exception. Registering a value of None unregisters any
+previously-registered value.
+
+If an object depends on a specification, it can be looked up with a
+specification that extends the specification that it depends on::
+
+ >>> class IR2(IR1):
+ ... pass
+ >>> registry.lookup([IR2], IP2, '')
+ 12
+
+We can use a class implementation specification to look up the object::
+
+ >>> class C2:
+ ... zope.interface.implements(IR2)
+
+ >>> registry.lookup([zope.interface.implementedBy(C2)], IP2, '')
+ 12
+
+
+and it can be looked up for interfaces that its provided interface
+extends::
+
+ >>> registry.lookup([IR1], IP1, '')
+ 12
+ >>> registry.lookup([IR2], IP1, '')
+ 12
+
+But if you require a specification that doesn't extend the specification the
+object depends on, you won't get anything::
+
+ >>> registry.lookup([zope.interface.Interface], IP1, '')
+
+By the way, you can pass a default value to lookup::
+
+ >>> registry.lookup([zope.interface.Interface], IP1, '', 42)
+ 42
+
+If you try to get an interface the object doesn't provide, you also
+won't get anything::
+
+ >>> class IP3(IP2):
+ ... pass
+ >>> registry.lookup([IR1], IP3, '')
+
+You also won't get anything if you use the wrong name::
+
+ >>> registry.lookup([IR1], IP1, 'bob')
+ >>> registry.register([IR1], IP2, 'bob', "Bob's 12")
+ >>> registry.lookup([IR1], IP1, 'bob')
+ "Bob's 12"
+
+You can leave the name off when doing a lookup::
+
+ >>> registry.lookup([IR1], IP1)
+ 12
+
+If we register an object that provides IP1::
+
+ >>> registry.register([IR1], IP1, '', 11)
+
+then that object will be prefered over O(12)::
+
+ >>> registry.lookup([IR1], IP1, '')
+ 11
+
+Also, if we register an object for IR2, then that will be prefered
+when using IR2::
+
+ >>> registry.register([IR2], IP1, '', 21)
+ >>> registry.lookup([IR2], IP1, '')
+ 21
+
+Finding out what, if anything, is registered
+--------------------------------------------
+
+We can ask if there is an adapter registered for a collection of
+interfaces. This is different than lookup, because it looks for an
+exact match.
+
+ >>> print registry.registered([IR1], IP1)
+ 11
+
+ >>> print registry.registered([IR1], IP2)
+ 12
+
+ >>> print registry.registered([IR1], IP2, 'bob')
+ Bob's 12
+
+
+ >>> print registry.registered([IR2], IP1)
+ 21
+
+ >>> print registry.registered([IR2], IP2)
+ None
+
+In the last example, None was returned because nothing was registered
+exactly for the given interfaces.
+
+lookup1
+-------
+
+Lookup of single adapters is common enough that there is a specialized
+version of lookup that takes a single required interface::
+
+ >>> registry.lookup1(IR2, IP1, '')
+ 21
+ >>> registry.lookup1(IR2, IP1)
+ 21
+
+Actual Adaptation
+-----------------
+
+The adapter registry is intended to support adaptation, where one
+object that implements an interface is adapted to another object that
+supports a different interface. The adapter registry supports the
+computation of adapters. In this case, we have to register adapter
+factories::
+
+ >>> class IR(zope.interface.Interface):
+ ... pass
+
+ >>> class X:
+ ... zope.interface.implements(IR)
+
+ >>> class Y:
+ ... zope.interface.implements(IP1)
+ ... def __init__(self, context):
+ ... self.context = context
+
+ >>> registry.register([IR], IP1, '', Y)
+
+In this case, we registered a class as the factory. Now we can call
+`queryAdapter` to get the adapted object::
+
+ >>> x = X()
+ >>> y = registry.queryAdapter(x, IP1)
+ >>> y.__class__.__name__
+ 'Y'
+ >>> y.context is x
+ True
+
+We can register and lookup by name too::
+
+ >>> class Y2(Y):
+ ... pass
+
+ >>> registry.register([IR], IP1, 'bob', Y2)
+ >>> y = registry.queryAdapter(x, IP1, 'bob')
+ >>> y.__class__.__name__
+ 'Y2'
+ >>> y.context is x
+ True
+
+When the adapter factory produces `None`, then this is treated as if no
+adapter has been found. This allows us to prevent adaptation (when desired)
+and let the adapter factory determine whether adaptation is possible based on
+the state of the object being adapted.
+
+ >>> def factory(context):
+ ... if context.name == 'object':
+ ... return 'adapter'
+ ... return None
+
+ >>> class Object(object):
+ ... zope.interface.implements(IR)
+ ... name = 'object'
+
+ >>> registry.register([IR], IP1, 'conditional', factory)
+ >>> obj = Object()
+ >>> registry.queryAdapter(obj, IP1, 'conditional')
+ 'adapter'
+ >>> obj.name = 'no object'
+ >>> registry.queryAdapter(obj, IP1, 'conditional') is None
+ True
+ >>> registry.queryAdapter(obj, IP1, 'conditional', 'default')
+ 'default'
+
+An alternate method that provides the same function as `queryAdapter()` is
+`adapter_hook()`::
+
+ >>> y = registry.adapter_hook(IP1, x)
+ >>> y.__class__.__name__
+ 'Y'
+ >>> y.context is x
+ True
+ >>> y = registry.adapter_hook(IP1, x, 'bob')
+ >>> y.__class__.__name__
+ 'Y2'
+ >>> y.context is x
+ True
+
+The `adapter_hook()` simply switches the order of the object and
+interface arguments. It is used to hook into the interface call
+mechanism.
+
+
+Default Adapters
+----------------
+
+Sometimes, you want to provide an adapter that will adapt anything.
+For that, provide None as the required interface::
+
+ >>> registry.register([None], IP1, '', 1)
+
+then we can use that adapter for interfaces we don't have specific
+adapters for::
+
+ >>> class IQ(zope.interface.Interface):
+ ... pass
+ >>> registry.lookup([IQ], IP1, '')
+ 1
+
+Of course, specific adapters are still used when applicable::
+
+ >>> registry.lookup([IR2], IP1, '')
+ 21
+
+Class adapters
+--------------
+
+You can register adapters for class declarations, which is almost the
+same as registering them for a class::
+
+ >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', 'C21')
+ >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
+ 'C21'
+
+Dict adapters
+-------------
+
+At some point it was impossible to register dictionary-based adapters due a
+bug. Let's make sure this works now:
+
+ >>> adapter = {}
+ >>> registry.register((), IQ, '', adapter)
+ >>> registry.lookup((), IQ, '') is adapter
+ True
+
+Unregistering
+-------------
+
+You can unregister by registering None, rather than an object::
+
+ >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', None)
+ >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
+ 21
+
+Of course, this means that None can't be registered. This is an
+exception to the statement, made earlier, that the registry doesn't
+care what gets registered.
+
+Multi-adapters
+==============
+
+You can adapt multiple specifications::
+
+ >>> registry.register([IR1, IQ], IP2, '', '1q2')
+ >>> registry.lookup([IR1, IQ], IP2, '')
+ '1q2'
+ >>> registry.lookup([IR2, IQ], IP1, '')
+ '1q2'
+
+ >>> class IS(zope.interface.Interface):
+ ... pass
+ >>> registry.lookup([IR2, IS], IP1, '')
+
+ >>> class IQ2(IQ):
+ ... pass
+
+ >>> registry.lookup([IR2, IQ2], IP1, '')
+ '1q2'
+
+ >>> registry.register([IR1, IQ2], IP2, '', '1q22')
+ >>> registry.lookup([IR2, IQ2], IP1, '')
+ '1q22'
+
+Multi-adaptation
+----------------
+
+You can adapt multiple objects::
+
+ >>> class Q:
+ ... zope.interface.implements(IQ)
+
+As with single adapters, we register a factory, which is often a class::
+
+ >>> class IM(zope.interface.Interface):
+ ... pass
+ >>> class M:
+ ... zope.interface.implements(IM)
+ ... def __init__(self, x, q):
+ ... self.x, self.q = x, q
+ >>> registry.register([IR, IQ], IM, '', M)
+
+And then we can call `queryMultiAdapter` to compute an adapter::
+
+ >>> q = Q()
+ >>> m = registry.queryMultiAdapter((x, q), IM)
+ >>> m.__class__.__name__
+ 'M'
+ >>> m.x is x and m.q is q
+ True
+
+and, of course, we can use names::
+
+ >>> class M2(M):
+ ... pass
+ >>> registry.register([IR, IQ], IM, 'bob', M2)
+ >>> m = registry.queryMultiAdapter((x, q), IM, 'bob')
+ >>> m.__class__.__name__
+ 'M2'
+ >>> m.x is x and m.q is q
+ True
+
+Default Adapters
+----------------
+
+As with single adapters, you can define default adapters by specifying
+None for the *first* specification::
+
+ >>> registry.register([None, IQ], IP2, '', 'q2')
+ >>> registry.lookup([IS, IQ], IP2, '')
+ 'q2'
+
+Null Adapters
+=============
+
+You can also adapt no specification::
+
+ >>> registry.register([], IP2, '', 2)
+ >>> registry.lookup([], IP2, '')
+ 2
+ >>> registry.lookup([], IP1, '')
+ 2
+
+Listing named adapters
+----------------------
+
+Adapters are named. Sometimes, it's useful to get all of the named
+adapters for given interfaces::
+
+ >>> adapters = list(registry.lookupAll([IR1], IP1))
+ >>> adapters.sort()
+ >>> assert adapters == [(u'', 11), (u'bob', "Bob's 12")]
+
+This works for multi-adapters too::
+
+ >>> registry.register([IR1, IQ2], IP2, 'bob', '1q2 for bob')
+ >>> adapters = list(registry.lookupAll([IR2, IQ2], IP1))
+ >>> adapters.sort()
+ >>> assert adapters == [(u'', '1q22'), (u'bob', '1q2 for bob')]
+
+And even null adapters::
+
+ >>> registry.register([], IP2, 'bob', 3)
+ >>> adapters = list(registry.lookupAll([], IP1))
+ >>> adapters.sort()
+ >>> assert adapters == [(u'', 2), (u'bob', 3)]
+
+Subscriptions
+=============
+
+Normally, we want to look up an object that most-closely matches a
+specification. Sometimes, we want to get all of the objects that
+match some specification. We use subscriptions for this. We
+subscribe objects against specifications and then later find all of
+the subscribed objects::
+
+ >>> registry.subscribe([IR1], IP2, 'sub12 1')
+ >>> registry.subscriptions([IR1], IP2)
+ ['sub12 1']
+
+Note that, unlike regular adapters, subscriptions are unnamed.
+
+You can have multiple subscribers for the same specification::
+
+ >>> registry.subscribe([IR1], IP2, 'sub12 2')
+ >>> registry.subscriptions([IR1], IP2)
+ ['sub12 1', 'sub12 2']
+
+If subscribers are registered for the same required interfaces, they
+are returned in the order of definition.
+
+You can register subscribers for all specifications using None::
+
+ >>> registry.subscribe([None], IP1, 'sub_1')
+ >>> registry.subscriptions([IR2], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2']
+
+Note that the new subscriber is returned first. Subscribers defined
+for less general required interfaces are returned before subscribers
+for more general interfaces.
+
+Subscriptions may be combined over multiple compatible specifications::
+
+ >>> registry.subscriptions([IR2], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2']
+ >>> registry.subscribe([IR1], IP1, 'sub11')
+ >>> registry.subscriptions([IR2], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2', 'sub11']
+ >>> registry.subscribe([IR2], IP2, 'sub22')
+ >>> registry.subscriptions([IR2], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2', 'sub11', 'sub22']
+ >>> registry.subscriptions([IR2], IP2)
+ ['sub12 1', 'sub12 2', 'sub22']
+
+Subscriptions can be on multiple specifications::
+
+ >>> registry.subscribe([IR1, IQ], IP2, 'sub1q2')
+ >>> registry.subscriptions([IR1, IQ], IP2)
+ ['sub1q2']
+
+As with single subscriptions and non-subscription adapters, you can
+specify None for the first required interface, to specify a default::
+
+ >>> registry.subscribe([None, IQ], IP2, 'sub_q2')
+ >>> registry.subscriptions([IS, IQ], IP2)
+ ['sub_q2']
+ >>> registry.subscriptions([IR1, IQ], IP2)
+ ['sub_q2', 'sub1q2']
+
+You can have subscriptions that are indepenent of any specifications::
+
+ >>> list(registry.subscriptions([], IP1))
+ []
+
+ >>> registry.subscribe([], IP2, 'sub2')
+ >>> registry.subscriptions([], IP1)
+ ['sub2']
+ >>> registry.subscribe([], IP1, 'sub1')
+ >>> registry.subscriptions([], IP1)
+ ['sub2', 'sub1']
+ >>> registry.subscriptions([], IP2)
+ ['sub2']
+
+Unregistering subscribers
+-------------------------
+
+We can unregister subscribers. When unregistering a subscriber, we
+can unregister a specific subscriber::
+
+ >>> registry.unsubscribe([IR1], IP1, 'sub11')
+ >>> registry.subscriptions([IR1], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2']
+
+If we don't specify a value, then all subscribers matching the given
+interfaces will be unsubscribed:
+
+ >>> registry.unsubscribe([IR1], IP2)
+ >>> registry.subscriptions([IR1], IP1)
+ ['sub_1']
+
+
+Subscription adapters
+---------------------
+
+We normally register adapter factories, which then allow us to compute
+adapters, but with subscriptions, we get multiple adapters. Here's an
+example of multiple-object subscribers::
+
+ >>> registry.subscribe([IR, IQ], IM, M)
+ >>> registry.subscribe([IR, IQ], IM, M2)
+
+ >>> subscribers = registry.subscribers((x, q), IM)
+ >>> len(subscribers)
+ 2
+ >>> class_names = [s.__class__.__name__ for s in subscribers]
+ >>> class_names.sort()
+ >>> class_names
+ ['M', 'M2']
+ >>> [(s.x is x and s.q is q) for s in subscribers]
+ [True, True]
+
+adapter factory subcribers can't return None values::
+
+ >>> def M3(x, y):
+ ... return None
+
+ >>> registry.subscribe([IR, IQ], IM, M3)
+ >>> subscribers = registry.subscribers((x, q), IM)
+ >>> len(subscribers)
+ 2
+
+Handlers
+--------
+
+A handler is a subscriber factory that doesn't produce any normal
+output. It returns None. A handler is unlike adapters in that it does
+all of its work when the factory is called.
+
+To register a handler, simply provide None as the provided interface::
+
+ >>> def handler(event):
+ ... print 'handler', event
+
+ >>> registry.subscribe([IR1], None, handler)
+ >>> registry.subscriptions([IR1], None) == [handler]
+ True
diff --git a/docs/adapter.ru.rst b/docs/adapter.ru.rst
new file mode 100644
index 0000000..30c2782
--- /dev/null
+++ b/docs/adapter.ru.rst
@@ -0,0 +1,540 @@
+================
+Реестр адаптеров
+================
+
+.. contents::
+
+Реестры адаптеров предоставляют возможность для регистрации объектов которые
+зависят от одной, или нескольких спецификаций интерфейсов и предоставляют
+(возможно не напрямую) какой-либо интерфейс. В дополнение, регистрации имеют
+имена. (Можно думать об именах как о спецификаторах предоставляемого
+интерфейса.)
+
+Термин "спецификация интерфейса" ссылается и на интерфейсы и на определения
+интерфейсов, такие как определения интерфейсов реализованных некоторым классом.
+
+Одиночные адаптеры
+==================
+
+Давайте рассмотрим простой пример использующий единственную требуемую
+спецификацию::
+
+ >>> from zope.interface.adapter import AdapterRegistry
+ >>> import zope.interface
+
+ >>> class IR1(zope.interface.Interface):
+ ... pass
+ >>> class IP1(zope.interface.Interface):
+ ... pass
+ >>> class IP2(IP1):
+ ... pass
+
+ >>> registry = AdapterRegistry()
+
+Мы зарегистрируем объект который зависит от IR1 и "предоставляет" IP2::
+
+ >>> registry.register([IR1], IP2, '', 12)
+
+После регистрации мы можем запросить объект снова::
+
+ >>> registry.lookup([IR1], IP2, '')
+ 12
+
+Заметьте, что мы используем целое в этом примере. В реальных приложениях вы
+можете использовать объекты которые на самом деле зависят или предоставляют
+интерфейсы. Реестр не заботиться о том, что регистрируется и таким образом мы
+можем использовать целые, или строки что бы упростить наши примеры. Здесь есть
+одно исключение. Регистрация значения None удаляет регистрацию для любого
+зарегистрированного прежде значения.
+
+Если объект зависит от спецификации он может быть запрошен с помощью
+спецификации которая расширяет спецификацию от которой он зависит::
+
+ >>> class IR2(IR1):
+ ... pass
+ >>> registry.lookup([IR2], IP2, '')
+ 12
+
+Мы можем использовать класс реализующий спецификацию для запроса объекта::
+
+ >>> class C2:
+ ... zope.interface.implements(IR2)
+
+ >>> registry.lookup([zope.interface.implementedBy(C2)], IP2, '')
+ 12
+
+и объект может быть запрошен для интерфейсов которые предоставляемый объектом
+интерфейс расширяет::
+
+ >>> registry.lookup([IR1], IP1, '')
+ 12
+ >>> registry.lookup([IR2], IP1, '')
+ 12
+
+Но если вы требуете спецификацию которая не расширяет спецификацию от которой
+зависит объект, вы не получите ничего::
+
+ >>> registry.lookup([zope.interface.Interface], IP1, '')
+
+Между прочим, вы можете передать значение по умолчанию при запросе::
+
+ >>> registry.lookup([zope.interface.Interface], IP1, '', 42)
+ 42
+
+Если вы пробуете получить интерфейс который объект не предоставляет вы также
+не получите ничего::
+
+ >>> class IP3(IP2):
+ ... pass
+ >>> registry.lookup([IR1], IP3, '')
+
+Вы также не получите ничего если вы используете неверное имя::
+
+ >>> registry.lookup([IR1], IP1, 'bob')
+ >>> registry.register([IR1], IP2, 'bob', "Bob's 12")
+ >>> registry.lookup([IR1], IP1, 'bob')
+ "Bob's 12"
+
+Вы можете не использовать имя при запросе::
+
+ >>> registry.lookup([IR1], IP1)
+ 12
+
+Если мы регистрируем объект который предоставляет IP1::
+
+ >>> registry.register([IR1], IP1, '', 11)
+
+тогда этот объект будет иметь преимущество перед O(12)::
+
+ >>> registry.lookup([IR1], IP1, '')
+ 11
+
+Также, если мы регистрируем объект для IR2 тогда он будет иметь преимущество
+когда используется IR2::
+
+ >>> registry.register([IR2], IP1, '', 21)
+ >>> registry.lookup([IR2], IP1, '')
+ 21
+
+Поиск того, что (если вообще что-то) зарегистрировано
+-----------------------------------------------------
+
+Мы можем спросить есть-ли адаптер зарегистрированный для набора интерфейсов.
+Это отличается от обычного запроса так как здесь мы ищем точное совпадение::
+
+ >>> print registry.registered([IR1], IP1)
+ 11
+
+ >>> print registry.registered([IR1], IP2)
+ 12
+
+ >>> print registry.registered([IR1], IP2, 'bob')
+ Bob's 12
+
+
+ >>> print registry.registered([IR2], IP1)
+ 21
+
+ >>> print registry.registered([IR2], IP2)
+ None
+
+В последнем примере, None был возвращен потому, что для данного интерфейса
+ничего не было зарегистрировано.
+
+lookup1
+-------
+
+Запрос одиночного адаптера - это наиболее частая операция и для нее есть
+специализированная версия запроса которая получает на вход единственный
+требуемый интерфейс::
+
+ >>> registry.lookup1(IR2, IP1, '')
+ 21
+ >>> registry.lookup1(IR2, IP1)
+ 21
+
+Адаптация на практике
+---------------------
+
+Реестр адаптеров предназначен для поддержки адаптации когда один объект
+реализующий интерфейс адаптируется к другому объекту который поддерживает
+другой интерфейс. Реестр адаптеров также поддерживает вычисление адаптеров. В
+этом случае мы должны регистрировать фабрики для адаптеров::
+
+ >>> class IR(zope.interface.Interface):
+ ... pass
+
+ >>> class X:
+ ... zope.interface.implements(IR)
+
+ >>> class Y:
+ ... zope.interface.implements(IP1)
+ ... def __init__(self, context):
+ ... self.context = context
+
+ >>> registry.register([IR], IP1, '', Y)
+
+В этом случае мы регистрируем класс как фабрику. Теперь мы можем вызвать
+`queryAdapter` для получения адаптированного объекта::
+
+ >>> x = X()
+ >>> y = registry.queryAdapter(x, IP1)
+ >>> y.__class__.__name__
+ 'Y'
+ >>> y.context is x
+ True
+
+Мы также можем регистрировать и запрашивать по имени::
+
+ >>> class Y2(Y):
+ ... pass
+
+ >>> registry.register([IR], IP1, 'bob', Y2)
+ >>> y = registry.queryAdapter(x, IP1, 'bob')
+ >>> y.__class__.__name__
+ 'Y2'
+ >>> y.context is x
+ True
+
+Когда фабрика для адаптера возвращает `None` - это рассматривается как если бы
+адаптер не был найден. Это позволяет нам избежать адаптации (по желанию) и дает
+возможность фабрике адаптера определить возможна ли адаптация основываясь на
+состоянии объекта который адаптируется::
+
+ >>> def factory(context):
+ ... if context.name == 'object':
+ ... return 'adapter'
+ ... return None
+
+ >>> class Object(object):
+ ... zope.interface.implements(IR)
+ ... name = 'object'
+
+ >>> registry.register([IR], IP1, 'conditional', factory)
+ >>> obj = Object()
+ >>> registry.queryAdapter(obj, IP1, 'conditional')
+ 'adapter'
+ >>> obj.name = 'no object'
+ >>> registry.queryAdapter(obj, IP1, 'conditional') is None
+ True
+ >>> registry.queryAdapter(obj, IP1, 'conditional', 'default')
+ 'default'
+
+Альтернативный метод для предоставления такой же функциональности как и
+`queryAdapter()` - это `adapter_hook()`::
+
+ >>> y = registry.adapter_hook(IP1, x)
+ >>> y.__class__.__name__
+ 'Y'
+ >>> y.context is x
+ True
+ >>> y = registry.adapter_hook(IP1, x, 'bob')
+ >>> y.__class__.__name__
+ 'Y2'
+ >>> y.context is x
+ True
+
+`adapter_hook()` просто меняет порядок аргументов для объекта и интерфейса. Это
+используется для встраивания в механизм вызовов интерфейсов.
+
+Адаптеры по умолчанию
+---------------------
+
+Иногда вы можете захотеть предоставить адаптер который не будет ничего
+адаптировать. Для этого нужно передать None как требуемый интерфейс::
+
+ >>> registry.register([None], IP1, '', 1)
+
+после этого вы можете использовать этот адаптер для интерфейсов для которых у
+вас нет конкретного адаптера::
+
+ >>> class IQ(zope.interface.Interface):
+ ... pass
+ >>> registry.lookup([IQ], IP1, '')
+ 1
+
+Конечно, конкретные адаптеры все еще используются когда необходимо::
+
+ >>> registry.lookup([IR2], IP1, '')
+ 21
+
+Адаптеры классов
+----------------
+
+Вы можете регистрировать адаптеры для определений классов, что будет похоже на
+регистрацию их для классов::
+
+ >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', 'C21')
+ >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
+ 'C21'
+
+Адаптеры для словарей
+---------------------
+
+В какой-то момент было невозможно регистрировать адаптеры основанные на
+словарях из-за ошибки. Давайте удостоверимся что это теперь работает::
+
+ >>> adapter = {}
+ >>> registry.register((), IQ, '', adapter)
+ >>> registry.lookup((), IQ, '') is adapter
+ True
+
+Удаление регистрации
+--------------------
+
+Вы можете удалить регистрацию регистрируя None вместо объекта::
+
+ >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', None)
+ >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
+ 21
+
+Конечно это значит, что None не может быть зарегистрирован. Это исключение к
+утверждению выше о том, что реестр не заботиться о том, что регистрируется.
+
+Мульти-адаптеры
+===============
+
+Вы можете адаптировать несколько спецификаций::
+
+ >>> registry.register([IR1, IQ], IP2, '', '1q2')
+ >>> registry.lookup([IR1, IQ], IP2, '')
+ '1q2'
+ >>> registry.lookup([IR2, IQ], IP1, '')
+ '1q2'
+
+ >>> class IS(zope.interface.Interface):
+ ... pass
+ >>> registry.lookup([IR2, IS], IP1, '')
+
+ >>> class IQ2(IQ):
+ ... pass
+
+ >>> registry.lookup([IR2, IQ2], IP1, '')
+ '1q2'
+
+ >>> registry.register([IR1, IQ2], IP2, '', '1q22')
+ >>> registry.lookup([IR2, IQ2], IP1, '')
+ '1q22'
+
+Мульти-адаптация
+----------------
+
+Вы можете адаптировать несколько объектов::
+
+ >>> class Q:
+ ... zope.interface.implements(IQ)
+
+Как и с одиночными адаптерами, мы регистрируем фабрику которая возвращает
+класс::
+
+ >>> class IM(zope.interface.Interface):
+ ... pass
+ >>> class M:
+ ... zope.interface.implements(IM)
+ ... def __init__(self, x, q):
+ ... self.x, self.q = x, q
+ >>> registry.register([IR, IQ], IM, '', M)
+
+И затем мы можем вызвать `queryMultiAdapter` для вычисления адаптера::
+
+ >>> q = Q()
+ >>> m = registry.queryMultiAdapter((x, q), IM)
+ >>> m.__class__.__name__
+ 'M'
+ >>> m.x is x and m.q is q
+ True
+
+и, конечно, мы можем использовать имена::
+
+ >>> class M2(M):
+ ... pass
+ >>> registry.register([IR, IQ], IM, 'bob', M2)
+ >>> m = registry.queryMultiAdapter((x, q), IM, 'bob')
+ >>> m.__class__.__name__
+ 'M2'
+ >>> m.x is x and m.q is q
+ True
+
+Адаптеры по умолчанию
+---------------------
+
+Как и для одиночных адаптеров вы можете определить адаптер по умолчанию передав
+None вместо *первой* спецификации::
+
+ >>> registry.register([None, IQ], IP2, '', 'q2')
+ >>> registry.lookup([IS, IQ], IP2, '')
+ 'q2'
+
+Нулевые адаптеры
+================
+
+Вы можете также адаптировать без спецификации::
+
+ >>> registry.register([], IP2, '', 2)
+ >>> registry.lookup([], IP2, '')
+ 2
+ >>> registry.lookup([], IP1, '')
+ 2
+
+Перечисление именованных адаптеров
+----------------------------------
+
+Адаптеры имеют имена. Иногда это полезно для получения всех именованных
+адаптеров для заданного интерфейса::
+
+ >>> adapters = list(registry.lookupAll([IR1], IP1))
+ >>> adapters.sort()
+ >>> assert adapters == [(u'', 11), (u'bob', "Bob's 12")]
+
+Это работает также и для мульти-адаптеров::
+
+ >>> registry.register([IR1, IQ2], IP2, 'bob', '1q2 for bob')
+ >>> adapters = list(registry.lookupAll([IR2, IQ2], IP1))
+ >>> adapters.sort()
+ >>> assert adapters == [(u'', '1q22'), (u'bob', '1q2 for bob')]
+
+И даже для нулевых адаптеров::
+
+ >>> registry.register([], IP2, 'bob', 3)
+ >>> adapters = list(registry.lookupAll([], IP1))
+ >>> adapters.sort()
+ >>> assert adapters == [(u'', 2), (u'bob', 3)]
+
+Подписки
+========
+
+Обычно мы хотим запросить объект который наиболее близко соответствует
+спецификации. Иногда мы хотим получить все объекты которые соответствуют
+какой-либо спецификации. Мы используем подписки для этого. Мы подписываем
+объекты для спецификаций и затем позже находим все подписанные объекты::
+
+ >>> registry.subscribe([IR1], IP2, 'sub12 1')
+ >>> registry.subscriptions([IR1], IP2)
+ ['sub12 1']
+
+Заметьте, что в отличие от обычных адаптеров подписки не имеют имен.
+
+Вы можете иметь несколько подписчиков для одной спецификации::
+
+ >>> registry.subscribe([IR1], IP2, 'sub12 2')
+ >>> registry.subscriptions([IR1], IP2)
+ ['sub12 1', 'sub12 2']
+
+Если подписчики зарегистрированы для одних и тех же требуемых интерфейсов, они
+возвращаются в порядке определения.
+
+Вы можете зарегистрировать подписчики для всех спецификаций используя None::
+
+ >>> registry.subscribe([None], IP1, 'sub_1')
+ >>> registry.subscriptions([IR2], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2']
+
+Заметьте, что новый подписчик возвращается первым. Подписчики определенные
+для менее общих требуемых интерфейсов возвращаются перед подписчиками
+для более общих интерфейсов.
+
+Подписки могут смешиваться между несколькими совместимыми спецификациями::
+
+ >>> registry.subscriptions([IR2], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2']
+ >>> registry.subscribe([IR1], IP1, 'sub11')
+ >>> registry.subscriptions([IR2], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2', 'sub11']
+ >>> registry.subscribe([IR2], IP2, 'sub22')
+ >>> registry.subscriptions([IR2], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2', 'sub11', 'sub22']
+ >>> registry.subscriptions([IR2], IP2)
+ ['sub12 1', 'sub12 2', 'sub22']
+
+Подписки могут существовать для нескольких спецификаций::
+
+ >>> registry.subscribe([IR1, IQ], IP2, 'sub1q2')
+ >>> registry.subscriptions([IR1, IQ], IP2)
+ ['sub1q2']
+
+Как и с одиночными подписчиками и адаптерами без подписок, вы можете определить
+None для первого требуемого интерфейса, что бы задать значение по умолчанию::
+
+ >>> registry.subscribe([None, IQ], IP2, 'sub_q2')
+ >>> registry.subscriptions([IS, IQ], IP2)
+ ['sub_q2']
+ >>> registry.subscriptions([IR1, IQ], IP2)
+ ['sub_q2', 'sub1q2']
+
+Вы можете создать подписки которые независимы от любых спецификаций::
+
+ >>> list(registry.subscriptions([], IP1))
+ []
+
+ >>> registry.subscribe([], IP2, 'sub2')
+ >>> registry.subscriptions([], IP1)
+ ['sub2']
+ >>> registry.subscribe([], IP1, 'sub1')
+ >>> registry.subscriptions([], IP1)
+ ['sub2', 'sub1']
+ >>> registry.subscriptions([], IP2)
+ ['sub2']
+
+Удаление регистрации подписчиков
+--------------------------------
+
+Мы можем удалять регистрацию подписчиков. При удалении регистрации подписчика
+мы можем удалить регистрацию заданного адаптера::
+
+ >>> registry.unsubscribe([IR1], IP1, 'sub11')
+ >>> registry.subscriptions([IR1], IP1)
+ ['sub_1', 'sub12 1', 'sub12 2']
+
+Если мы не задаем никакого значения тогда подписки будут удалены для всех
+подписчиков совпадающих с заданным интерфейсом::
+
+ >>> registry.unsubscribe([IR1], IP2)
+ >>> registry.subscriptions([IR1], IP1)
+ ['sub_1']
+
+Адаптеры подписки
+-----------------
+
+Обычно мы регистрируем фабрики для адаптеров которые затем позволяют нам
+вычислять адаптеры, но с подписками мы получаем несколько адаптеров. Это пример
+подписчика для нескольких объектов::
+
+ >>> registry.subscribe([IR, IQ], IM, M)
+ >>> registry.subscribe([IR, IQ], IM, M2)
+
+ >>> subscribers = registry.subscribers((x, q), IM)
+ >>> len(subscribers)
+ 2
+ >>> class_names = [s.__class__.__name__ for s in subscribers]
+ >>> class_names.sort()
+ >>> class_names
+ ['M', 'M2']
+ >>> [(s.x is x and s.q is q) for s in subscribers]
+ [True, True]
+
+подписчики фабрик адаптеров не могут возвращать None::
+
+ >>> def M3(x, y):
+ ... return None
+
+ >>> registry.subscribe([IR, IQ], IM, M3)
+ >>> subscribers = registry.subscribers((x, q), IM)
+ >>> len(subscribers)
+ 2
+
+Обработчики
+-----------
+
+Обработчик - это подписанная фабрика которая не возвращает нормального
+значения. Она возвращает None. Обработчик отличается от адаптеров тем, что он
+делает всю работу когда вызывается фабрика.
+
+Для регистрации обработчика надо просто передать None как предоставляемый
+интерфейс::
+
+ >>> def handler(event):
+ ... print 'handler', event
+
+ >>> registry.subscribe([IR1], None, handler)
+ >>> registry.subscriptions([IR1], None) == [handler]
+ True
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..581a428
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,246 @@
+# -*- coding: utf-8 -*-
+#
+# zope.interface documentation build configuration file, created by
+# sphinx-quickstart on Mon Mar 26 16:31:31 2012.
+#
+# 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, os
+
+# 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.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode']
+
+# 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 = u'zope.interface'
+copyright = u'2012, Zope Foundation 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 = '3.8'
+# The full version, including alpha/beta/rc tags.
+release = '3.8.1'
+
+# 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 = []
+
+
+# -- 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
+# "<project> v<release> 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']
+
+# 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 <link> 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 = 'zopeinterfacedoc'
+
+
+# -- 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]).
+latex_documents = [
+ ('index', 'zopeinterface.tex', u'zope.interface Documentation',
+ u'Zope Foundation 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', 'zopeinterface', u'zope.interface Documentation',
+ [u'Zope Foundation 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', 'zopeinterface', u'zope.interface Documentation',
+ u'Zope Foundation contributors', 'zopeinterface', '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'
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'http://docs.python.org/': None}
diff --git a/docs/foodforthought.rst b/docs/foodforthought.rst
new file mode 100644
index 0000000..45d961b
--- /dev/null
+++ b/docs/foodforthought.rst
@@ -0,0 +1,61 @@
+================================
+Food-based subscription examples
+================================
+
+
+This file gives more subscription examples using a cooking-based example::
+
+ >>> from zope.interface.adapter import AdapterRegistry
+ >>> registry = AdapterRegistry()
+
+ >>> import zope.interface
+ >>> class IAnimal(zope.interface.Interface):
+ ... pass
+ >>> class IPoultry(IAnimal):
+ ... pass
+ >>> class IChicken(IPoultry):
+ ... pass
+ >>> class ISeafood(IAnimal):
+ ... pass
+
+Adapting to some other interface for which there is no
+subscription adapter returns an empty sequence::
+
+ >>> class IRecipe(zope.interface.Interface):
+ ... pass
+ >>> class ISausages(IRecipe):
+ ... pass
+ >>> class INoodles(IRecipe):
+ ... pass
+ >>> class IKFC(IRecipe):
+ ... pass
+
+ >>> list(registry.subscriptions([IPoultry], IRecipe))
+ []
+
+unless we define a subscription::
+
+ >>> registry.subscribe([IAnimal], ISausages, 'sausages')
+ >>> list(registry.subscriptions([IPoultry], ISausages))
+ ['sausages']
+
+And define another subscription adapter::
+
+ >>> registry.subscribe([IPoultry], INoodles, 'noodles')
+ >>> meals = list(registry.subscriptions([IPoultry], IRecipe))
+ >>> meals.sort()
+ >>> meals
+ ['noodles', 'sausages']
+
+ >>> registry.subscribe([IChicken], IKFC, 'kfc')
+ >>> meals = list(registry.subscriptions([IChicken], IRecipe))
+ >>> meals.sort()
+ >>> meals
+ ['kfc', 'noodles', 'sausages']
+
+And the answer for poultry hasn't changed::
+
+ >>> meals = list(registry.subscriptions([IPoultry], IRecipe))
+ >>> meals.sort()
+ >>> meals
+ ['noodles', 'sausages']
diff --git a/docs/human.rst b/docs/human.rst
new file mode 100644
index 0000000..749b87d
--- /dev/null
+++ b/docs/human.rst
@@ -0,0 +1,152 @@
+==========================
+Using the Adapter Registry
+==========================
+
+This is a small demonstration of the ``zope.interface`` package including its
+adapter registry. It is intended to provide a concrete but narrow example on
+how to use interfaces and adapters outside of Zope 3.
+
+First we have to import the interface package::
+
+ >>> import zope.interface
+
+We now develop an interface for our object, which is a simple file in this
+case. For now we simply support one attribute, the body, which contains the
+actual file contents::
+
+ >>> class IFile(zope.interface.Interface):
+ ...
+ ... body = zope.interface.Attribute('Contents of the file.')
+ ...
+
+For statistical reasons we often want to know the size of a file. However, it
+would be clumsy to implement the size directly in the file object, since the
+size really represents meta-data. Thus we create another interface that
+provides the size of something::
+
+ >>> class ISize(zope.interface.Interface):
+ ...
+ ... def getSize():
+ ... 'Return the size of an object.'
+ ...
+
+Now we need to implement the file. It is essential that the object states
+that it implements the `IFile` interface. We also provide a default body
+value (just to make things simpler for this example)::
+
+ >>> class File(object):
+ ...
+ ... zope.interface.implements(IFile)
+ ... body = 'foo bar'
+ ...
+
+Next we implement an adapter that can provide the `ISize` interface given any
+object providing `IFile`. By convention we use `__used_for__` to specify the
+interface that we expect the adapted object to provide, in our case
+`IFile`. However, this attribute is not used for anything. If you have
+multiple interfaces for which an adapter is used, just specify the interfaces
+via a tuple.
+
+Again by convention, the constructor of an adapter takes one argument, the
+context. The context in this case is an instance of `File` (providing `IFile`)
+that is used to extract the size from. Also by convention the context is
+stored in an attribute named `context` on the adapter. The twisted community
+refers to the context as the `original` object. However, you may feel free to
+use a specific argument name, such as `file`::
+
+ >>> class FileSize(object):
+ ...
+ ... zope.interface.implements(ISize)
+ ... __used_for__ = IFile
+ ...
+ ... def __init__(self, context):
+ ... self.context = context
+ ...
+ ... def getSize(self):
+ ... return len(self.context.body)
+ ...
+
+Now that we have written our adapter, we have to register it with an adapter
+registry, so that it can be looked up when needed. There is no such thing as a
+global registry; thus we have to instantiate one for our example manually::
+
+ >>> from zope.interface.adapter import AdapterRegistry
+ >>> registry = AdapterRegistry()
+
+
+The registry keeps a map of what adapters implement based on another
+interface, the object already provides. Therefore, we next have to register an
+adapter that adapts from `IFile` to `ISize`. The first argument to
+the registry's `register()` method is a list of original interfaces.In our
+cause we have only one original interface, `IFile`. A list makes sense, since
+the interface package has the concept of multi-adapters, which are adapters
+that require multiple objects to adapt to a new interface. In these
+situations, your adapter constructor will require an argument for each
+specified interface.
+
+The second argument is the interface the adapter provides, in our case
+`ISize`. The third argument is the name of the adapter. Since we do not care
+about names, we simply leave it as an empty string. Names are commonly useful,
+if you have adapters for the same set of interfaces, but they are useful in
+different situations. The last argument is simply the adapter class::
+
+ >>> registry.register([IFile], ISize, '', FileSize)
+
+You can now use the the registry to lookup the adapter::
+
+ >>> registry.lookup1(IFile, ISize, '')
+ <class '__main__.FileSize'>
+
+Let's get a little bit more practical. Let's create a `File` instance and
+create the adapter using a registry lookup. Then we see whether the adapter
+returns the correct size by calling `getSize()`::
+
+ >>> file = File()
+ >>> size = registry.lookup1(IFile, ISize, '')(file)
+ >>> size.getSize()
+ 7
+
+However, this is not very practical, since I have to manually pass in the
+arguments to the lookup method. There is some syntactic candy that will allow
+us to get an adapter instance by simply calling `ISize(file)`. To make use of
+this functionality, we need to add our registry to the adapter_hooks list,
+which is a member of the adapters module. This list stores a collection of
+callables that are automatically invoked when IFoo(obj) is called; their
+purpose is to locate adapters that implement an interface for a certain
+context instance.
+
+You are required to implement your own adapter hook; this example covers one
+of the simplest hooks that use the registry, but you could implement one that
+used an adapter cache or persistent adapters, for instance. The helper hook is
+required to expect as first argument the desired output interface (for us
+`ISize`) and as the second argument the context of the adapter (here
+`file`). The function returns an adapter, i.e. a `FileSize` instance::
+
+ >>> def hook(provided, object):
+ ... adapter = registry.lookup1(zope.interface.providedBy(object),
+ ... provided, '')
+ ... return adapter(object)
+ ...
+
+We now just add the hook to an `adapter_hooks` list::
+
+ >>> from zope.interface.interface import adapter_hooks
+ >>> adapter_hooks.append(hook)
+
+Once the hook is registered, you can use the desired syntax::
+
+ >>> size = ISize(file)
+ >>> size.getSize()
+ 7
+
+Now we have to cleanup after ourselves, so that others after us have a clean
+`adapter_hooks` list::
+
+ >>> adapter_hooks.remove(hook)
+
+That's it. I have intentionally left out a discussion of named adapters and
+multi-adapters, since this text is intended as a practical and simple
+introduction to Zope 3 interfaces and adapters. You might want to read the
+`adapter.txt` in the `zope.interface` package for a more formal, referencial
+and complete treatment of the package. Warning: People have reported that
+`adapter.txt` makes their brain feel soft!
diff --git a/docs/human.ru.rst b/docs/human.ru.rst
new file mode 100644
index 0000000..a149072
--- /dev/null
+++ b/docs/human.ru.rst
@@ -0,0 +1,156 @@
+===============================
+Использование реестра адаптеров
+===============================
+
+Данный документ содержит небольшую демонстрацию пакета ``zope.interface`` и его
+реестра адаптеров. Документ рассчитывался как конкретный, но более узкий пример
+того как использовать интерфейсы и адаптеры вне Zope 3.
+
+Сначала нам необходимо импортировать пакет для работы с интерфейсами::
+
+ >>> import zope.interface
+
+Теперь мы разработаем интерфейс для нашего объекта - простого файла. Наш файл
+будет содержать всего один атрибут - body, в котором фактически будет сохранено
+содержимое файла::
+
+ >>> class IFile(zope.interface.Interface):
+ ...
+ ... body = zope.interface.Attribute(u'Содержимое файла.')
+ ...
+
+Для статистики нам часто необходимо знать размер файла. Но было бы несколько
+топорно реализовывать определение размера прямо для объекта файла, т.к. размер
+больше относится к мета-данным. Таким образом мы создаем еще один интерфейс для
+представления размера какого-либо объекта::
+
+ >>> class ISize(zope.interface.Interface):
+ ...
+ ... def getSize():
+ ... 'Return the size of an object.'
+ ...
+
+Теперь мы должны создать класс реализующий наш файл. Необходимо что бы наш
+объект хранил информацию о том, что он реализует интерфейс `IFile`. Мы также
+создаем атрибут с содержимым файла по умолчанию (для упрощения нашего
+примера)::
+
+ >>> class File(object):
+ ...
+ ... zope.interface.implements(IFile)
+ ... body = 'foo bar'
+ ...
+
+Дальше мы создаем адаптер, который будет предоставлять интерфейс `ISize`
+получая любой объект предоставляющий интерфейс `IFile`. По соглашению мы
+используем атрибут `__used_for__` для указания интерфейса который как мы
+ожидаем предоставляет адаптируемый объект, `IFile` в нашем случае. На самом
+деле этот атрибут используется только для документирования. В случае если
+адаптер используется для нескольких интерфейсов можно указать их все в виде
+кортежа.
+
+Опять же по соглашению конструктор адаптера получает один аргумент - context
+(контекст). В нашем случае контекст - это экземпляр `IFile` (объект,
+предоставляющий `IFile`) который используется для получения из него размера.
+Так же по соглашению контекст сохраняется а адаптере в атрибуте с именем
+`context`. Twisted комьюнити ссылается на контекст как на объект `original`.
+Таким образом можно также дать аргументу любое подходящее имя, например
+`file`::
+
+ >>> class FileSize(object):
+ ...
+ ... zope.interface.implements(ISize)
+ ... __used_for__ = IFile
+ ...
+ ... def __init__(self, context):
+ ... self.context = context
+ ...
+ ... def getSize(self):
+ ... return len(self.context.body)
+ ...
+
+Теперь когда мы написали наш адаптер мы должны зарегистрировать его в реестре
+адаптеров, что бы его можно было запросить когда он понадобится. Здесь нет
+какого-либо глобального реестра адаптеров, таким образом мы должны
+самостоятельно создать для нашего примера реестр::
+
+ >>> from zope.interface.adapter import AdapterRegistry
+ >>> registry = AdapterRegistry()
+
+Реестр содержит отображение того, что адаптер реализует на основе другого
+интерфейса который предоставляет объект. Поэтому дальше мы регистрируем адаптер
+который адаптирует интерфейс `IFile` к интерфейсу `ISize`. Первый аргумент к
+методу `register()` реестра - это список адаптируемых интерфейсов. В нашем
+случае мы имеем только один адаптируемый интерфейс - `IFile`. Список
+интерфейсов имеет смысл для использования концепции мульти-адаптеров, которые
+требуют нескольких оригинальных объектов для адаптации к новому интерфейсу. В
+этой ситуации конструктор адаптера будет требовать новый аргумент для каждого
+оригинального интерфейса.
+
+Второй аргумент метода `register()` - это интерфейс который предоставляет
+адаптер, в нашем случае `ISize`. Третий аргумент - имя адаптера. Сейчас нам не
+важно имя адаптера и мы передаем его как пустую строку. Обычно имена полезны
+если используются адаптеры для одинакового набора интерфейсов, но в различных
+ситуациях. Последний аргумент - это класс адаптера::
+
+ >>> registry.register([IFile], ISize, '', FileSize)
+
+Теперь мы можем использовать реестр для запроса адаптера::
+
+ >>> registry.lookup1(IFile, ISize, '')
+ <class '__main__.FileSize'>
+
+Попробуем более практичный пример. Создадим экземпляр `File` и создадим адаптер
+использующий запрос реестра. Затем мы увидим возвращает ли адаптер корректный
+размер при вызове `getSize()`::
+
+ >>> file = File()
+ >>> size = registry.lookup1(IFile, ISize, '')(file)
+ >>> size.getSize()
+ 7
+
+На самом деле это не очень практично, т.к. нам нужно самим передавать все
+аргументы методу запроса. Существует некоторый синтаксический леденец который
+позволяет нам получить экземпляр адаптера просто вызвав `ISize(file)`. Что бы
+использовать эту функциональность нам понадобится добавить наш реестр к списку
+adapter_hooks, который находится в модуле с адаптерами. Этот список хранит
+коллекцию вызываемых объектов которые вызываются автоматически когда вызывается
+IFoo(obj); их предназначение - найти адаптеры которые реализуют интерфейс для
+определенного экземпляра контекста.
+
+Необходимо реализовать свою собственную функцию для поиска адаптера; данный
+пример описывает одну из простейших функций для использования с реестром, но
+также можно реализовать поисковые функции которые, например, используют
+кэширование, или адаптеры сохраняемые в базе. Функция поиска должна принимать
+желаемый на выходе интерфейс (в нашем случае `ISize`) как первый аргумент и
+контекст для адаптации (`file`) как второй. Функция должна вернуть адаптер,
+т.е. экземпляр `FileSize`::
+
+ >>> def hook(provided, object):
+ ... adapter = registry.lookup1(zope.interface.providedBy(object),
+ ... provided, '')
+ ... return adapter(object)
+ ...
+
+Теперь мы просто добавляем нашу функцию к списку `adapter_hooks`::
+
+ >>> from zope.interface.interface import adapter_hooks
+ >>> adapter_hooks.append(hook)
+
+Как только функция зарегистрирована мы можем использовать желаемый синтаксис::
+
+ >>> size = ISize(file)
+ >>> size.getSize()
+ 7
+
+После нам нужно прибраться за собой, что бы другие получили чистый список
+`adaper_hooks` после нас::
+
+ >>> adapter_hooks.remove(hook)
+
+Это все. Здесь намеренно отложена дискуссия об именованных и мульти-адаптерах,
+т.к. данный текст рассчитан как практическое и простое введение в интерфейсы и
+адаптеры Zope 3. Для более подробной информации имеет смысл прочитать
+`adapter.txt` из пакета `zope.interface`, что бы получить более формальное,
+справочное и полное трактование пакета. Внимание: многие жаловались, что
+`adapter.txt` приводит их мозг к расплавленному состоянию!
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..a44105d
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,38 @@
+.. zope.interface documentation master file, created by
+ sphinx-quickstart on Mon Mar 26 16:31:31 2012.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to zope.interface's documentation!
+==========================================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ README
+ adapter
+ human
+ verify
+ foodforthought
+
+По-русски
+=========
+
+.. toctree::
+ :maxdepth: 2
+
+ README.ru
+ adapter.ru
+ human.ru
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..f59869c
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,190 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\zopeinterface.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\zopeinterface.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+:end
diff --git a/docs/verify.rst b/docs/verify.rst
new file mode 100644
index 0000000..7eec6d2
--- /dev/null
+++ b/docs/verify.rst
@@ -0,0 +1,127 @@
+===================================
+Verifying interface implementations
+===================================
+
+The ``zope.interface.verify`` module provides functions that test whether a
+given interface is implemented by a class or provided by an object, resp.
+
+
+Verifying classes
+=================
+
+This is covered by unit tests defined in ``zope.interface.tests.test_verify``.
+
+
+Verifying objects
+=================
+
+An object provides an interface if
+
+- either its class declares that it implements the interfaces, or the object
+ declares that it directly provides the interface
+
+- the object defines all the methods required by the interface
+
+- all the methods have the correct signature
+
+- the object defines all non-method attributes required by the interface
+
+This doctest currently covers only the latter item.
+
+Testing for attributes
+----------------------
+
+Attributes of the object, be they defined by its class or added by its
+``__init__`` method, will be recognized:
+
+>>> from zope.interface import Interface, Attribute, implements
+>>> from zope.interface.exceptions import BrokenImplementation
+>>> class IFoo(Interface):
+... x = Attribute("The X attribute")
+... y = Attribute("The Y attribute")
+
+>>> class Foo(object):
+... implements(IFoo)
+... x = 1
+... def __init__(self):
+... self.y = 2
+
+>>> from zope.interface.verify import verifyObject
+>>> verifyObject(IFoo, Foo())
+True
+
+If either attribute is missing, verification will fail:
+
+>>> class Foo(object):
+... implements(IFoo)
+... x = 1
+
+>>> try: #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+... verifyObject(IFoo, Foo())
+... except BrokenImplementation, e:
+... print str(e)
+An object has failed to implement interface <InterfaceClass ...IFoo>
+<BLANKLINE>
+ The y attribute was not provided.
+<BLANKLINE>
+
+>>> class Foo(object):
+... implements(IFoo)
+... def __init__(self):
+... self.y = 2
+
+>>> try: #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+... verifyObject(IFoo, Foo())
+... except BrokenImplementation, e:
+... print str(e)
+An object has failed to implement interface <InterfaceClass ...IFoo>
+<BLANKLINE>
+ The x attribute was not provided.
+<BLANKLINE>
+
+If an attribute is implemented as a property that raises an AttributeError
+when trying to get its value, the attribute is considered missing:
+
+>>> class IFoo(Interface):
+... x = Attribute('The X attribute')
+
+>>> class Foo(object):
+... implements(IFoo)
+... @property
+... def x(self):
+... raise AttributeError
+
+>>> try: #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+... verifyObject(IFoo, Foo())
+... except BrokenImplementation, e:
+... print str(e)
+An object has failed to implement interface <InterfaceClass ...IFoo>
+<BLANKLINE>
+ The x attribute was not provided.
+<BLANKLINE>
+
+Any other exception raised by a property will propagate to the caller of
+``verifyObject``:
+
+>>> class Foo(object):
+... implements(IFoo)
+... @property
+... def x(self):
+... raise Exception
+
+>>> verifyObject(IFoo, Foo())
+Traceback (most recent call last):
+Exception
+
+Of course, broken properties that are not required by the interface don't do
+any harm:
+
+>>> class Foo(object):
+... implements(IFoo)
+... x = 1
+... @property
+... def y(self):
+... raise Exception
+
+>>> verifyObject(IFoo, Foo())
+True