lazr.delegates-1.2.0/ 0000755 0001751 0001751 00000000000 11420057564 014232 5 ustar salgado salgado lazr.delegates-1.2.0/setup.cfg 0000644 0001751 0001751 00000000073 11420057564 016053 0 ustar salgado salgado [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
lazr.delegates-1.2.0/PKG-INFO 0000644 0001751 0001751 00000026571 11420057564 015342 0 ustar salgado salgado Metadata-Version: 1.0
Name: lazr.delegates
Version: 1.2.0
Summary: Easily write objects that delegate behavior.
Home-page: https://launchpad.net/lazr.delegates
Author: LAZR Developers
Author-email: lazr-developers@lists.launchpad.net
License: LGPL v3
Download-URL: https://launchpad.net/lazr.delegates/+download
Description: ..
This file is part of lazr.delegates.
lazr.delegates is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
lazr.delegates is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with lazr.delegates. If not, see .
The ``lazr.delegates`` Package
******************************
The ``lazr.delegates`` package makes it easy to write objects that delegate
behavior to another object. The new object adds some property or behavior on
to the other object, while still providing the underlying interface, and
delegating behavior.
=====
Usage
=====
The ``delegates`` function makes a class implement zero or more
interfaces by delegating the implementation to another object. In the
case of a class providing an adapter, that object will be the 'context',
but it can really be any object stored in an attribute. So while the
interfaces use an inheritance mechanism, the classes use a composition
mechanism.
For example we can define two interfaces IFoo0 <- IFoo...
>>> from lazr.delegates import delegates
>>> from zope.interface import Interface, Attribute
>>> class IFoo0(Interface):
... spoo = Attribute('attribute in IFoo0')
>>> class IFoo(IFoo0):
... def bar():
... "some method"
... baz = Attribute("some attribute")
And two classes (BaseFoo0 <- BaseFoo) that do something interesting.
>>> class BaseFoo0:
... spoo = 'some spoo'
>>> class BaseFoo(BaseFoo0):
... def bar(self):
... return 'bar'
... baz = 'hi baz!'
SomeClass can implement IFoo by delegating to an instance of BaseFoo
stored in the 'context' attribute. Note that ``delegates`` takes the
interface as the argument. By default, 'context' is the attribute
containing the object to which the interface implementation is
delegated.
>>> class SomeClass(object):
... delegates(IFoo)
... def __init__(self, context):
... self.context = context
>>> f = BaseFoo()
>>> s = SomeClass(f)
>>> s.bar()
'bar'
>>> s.baz
'hi baz!'
>>> s.spoo
'some spoo'
>>> IFoo.providedBy(s)
True
The ``delegates()`` function takes an optional keyword argument to change
attribute containing the object to delegate to. So an existing class,
such as SomeOtherClass, can declare the name of the attribute to which to
delegate.
>>> class SomeOtherClass(object):
... delegates(IFoo, context='myfoo')
... def __init__(self, foo):
... self.myfoo = foo
... spoo = 'spoo from SomeOtherClass'
>>> f = BaseFoo()
>>> s = SomeOtherClass(f)
>>> s.bar()
'bar'
>>> s.baz
'hi baz!'
>>> s.spoo
'spoo from SomeOtherClass'
>>> s.baz = 'fish'
>>> s.baz
'fish'
>>> f.baz
'fish'
The ``delegates()`` function can only be used in new-style classes. An
error is raised when a classic-style class is modified to implement an
interface.
>>> class SomeClassicClass:
... delegates(IFoo)
Traceback (most recent call last):
...
TypeError: Cannot use delegates() on a classic
class: __builtin__.SomeClassicClass.
The ``delegates()`` function cannot be used out side of a class definition,
such as in a module or in a function.
>>> delegates(IFoo)
Traceback (most recent call last):
...
TypeError: delegates() can be used only from a class definition.
Multiple interfaces can be specified by passing an iterable to
delegates().
>>> class IOther(Interface):
... another = Attribute("another attribute")
>>> class BaseOtherFoo(BaseFoo):
... another = 'yes, another'
>>> class SomeOtherClass(object):
... delegates([IFoo, IOther])
>>> s = SomeOtherClass()
>>> s.context = BaseOtherFoo()
>>> s.another
'yes, another'
>>> s.baz
'hi baz!'
>>> s.spoo
'some spoo'
>>> IFoo.providedBy(s)
True
>>> IOther.providedBy(s)
True
This can be convenient when decorating an existing object.
>>> from zope.interface import implements
>>> class MoreFoo(BaseFoo, BaseOtherFoo):
... implements(IFoo, IOther)
>>> foo = MoreFoo()
>>> from zope.interface import providedBy
>>> class WithExtraTeapot(object):
... delegates(providedBy(foo))
... teapot = 'i am a teapot'
>>> foo_with_teapot = WithExtraTeapot()
>>> foo_with_teapot.context = foo
>>> foo_with_teapot.baz
'hi baz!'
>>> foo_with_teapot.another
'yes, another'
>>> foo_with_teapot.teapot
'i am a teapot'
>>> IFoo.providedBy(foo_with_teapot)
True
>>> IOther.providedBy(foo_with_teapot)
True
==============
Implementation
==============
The Passthrough class is the implementation machinery of ``delegates()``. It
uses the descriptor protocol to implement the delegation behaviour provided by
``delegates()``. It takes at least two arguments: the name of the attribute
that is delegated, and the name of the attribute containing the object to
which to delegate.
To illustrate, p and p2 are two Passthrough instances that use the
instance assigned to 'mycontext' to call the 'foo' attribute and
the 'clsmethod' method.
>>> from lazr.delegates import Passthrough
>>> p = Passthrough('foo', 'mycontext')
>>> p2 = Passthrough('clsmethod', 'mycontext')
Base is a class the implements both 'foo' and 'clsmethod'.
>>> class Base:
... foo = 'foo from Base'
... def clsmethod(cls):
... return str(cls)
... clsmethod = classmethod(clsmethod)
Adapter is a class that has an instance of Base assigned to the
attribute 'mycontext'.
>>> base = Base()
>>> class Adapter:
... mycontext = base
>>> adapter = Adapter()
The Passthrough instances can get and set their prescribed attributes
when passed an instance of adapter.
>>> p.__get__(adapter)
'foo from Base'
>>> p.__get__(None, Adapter) is p
True
>>> p2.__get__(adapter)()
'__builtin__.Base'
>>> p.__set__(adapter, 'new value')
>>> base.foo
'new value'
Passthrough does not implement __delete__. An error is raised if
it is called.
>>> p.__delete__(adapter)
Traceback (most recent call last):
...
NotImplementedError
Passthrough's third argument (adaptation) is optional and, when provided,
should be a zope.interface.Interface subclass (although in practice any
callable will do) to which the instance is adapted before getting/setting the
delegated attribute.
# HasNoFoo does not have a .foo attribute...
>>> class HasNoFoo(object):
... _foo = 1
>>> no_foo = HasNoFoo()
# ... but IHasFooAdapter uses HasNoFoo._foo to provide its own .foo, so it
# works like an adapter for HasNoFoo into some interface that provides
# a 'foo' attribute.
>>> class IHasFooAdapter(object):
... def __init__(self, inst):
... self.inst = inst
... def _get_foo(self):
... return self.inst._foo
... def _set_foo(self, value):
... self.inst._foo = value
... foo = property(_get_foo, _set_foo)
>>> class Example(object):
... context = no_foo
>>> p = Passthrough('foo', 'context', adaptation=IHasFooAdapter)
>>> e = Example()
>>> p.__get__(e)
1
>>> p.__set__(e, 2)
>>> p.__get__(e)
2
===============
Other Documents
===============
.. toctree::
:glob:
*
docs/*
=======================
NEWS for lazr.delegates
=======================
1.2.0 (2010-07-16)
==================
- Extend Passthrough so that it takes an extra (optional) callable argument,
used to adapt the context before accessing the delegated attribute.
1.1.0 (2009-08-31)
==================
- Remove build dependencies on bzr and egg_info
- remove sys.path hack in setup.py for __version__
1.0.1 (2009-03-24)
==================
- specify only v3 of LGPL
- build/developer improvements
1.0 (2008-12-19)
================
- Initial release
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
lazr.delegates-1.2.0/README.txt 0000644 0001751 0001751 00000001320 11417351266 015726 0 ustar salgado salgado Easily write objects that delegate behavior.
..
This file is part of lazr.delegates.
lazr.delegates is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
lazr.delegates is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with lazr.delegates. If not, see .
lazr.delegates-1.2.0/setup.py 0000755 0001751 0001751 00000004562 11417351266 015760 0 ustar salgado salgado #!/usr/bin/env python
# Copyright 2008-2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.delegates.
#
# lazr.delegates is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.delegates is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.delegates. If not, see .
import ez_setup
ez_setup.use_setuptools()
import sys
from setuptools import setup, find_packages
# generic helpers primarily for the long_description
def generate(*docname_or_string):
res = []
for value in docname_or_string:
if value.endswith('.txt'):
f = open(value)
value = f.read()
f.close()
res.append(value)
if not value.endswith('\n'):
res.append('')
return '\n'.join(res)
# end generic helpers
__version__ = open("src/lazr/delegates/version.txt").read().strip()
setup(
name='lazr.delegates',
version=__version__,
namespace_packages=['lazr'],
packages=find_packages('src'),
package_dir={'':'src'},
include_package_data=True,
zip_safe=False,
maintainer='LAZR Developers',
maintainer_email='lazr-developers@lists.launchpad.net',
description=open('README.txt').readline().strip(),
long_description=generate(
'src/lazr/delegates/README.txt',
'src/lazr/delegates/NEWS.txt'),
license='LGPL v3',
install_requires=[
'setuptools',
'zope.interface',
],
url='https://launchpad.net/lazr.delegates',
download_url= 'https://launchpad.net/lazr.delegates/+download',
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Operating System :: OS Independent",
"Programming Language :: Python"],
extras_require=dict(
docs=['Sphinx',
'z3c.recipe.sphinxdoc']
),
test_suite='lazr.delegates.tests',
)
lazr.delegates-1.2.0/ez_setup.py 0000644 0001751 0001751 00000022466 11417351266 016456 0 ustar salgado salgado #!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c8"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15, min_version=None
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
# Work around a hack in the ez_setup.py file from simplejson==1.7.3.
if min_version:
version = min_version
was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
def do_download():
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
try:
import pkg_resources
except ImportError:
return do_download()
try:
pkg_resources.require("setuptools>="+version); return
except pkg_resources.VersionConflict, e:
if was_imported:
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first, using 'easy_install -U setuptools'."
"\n\n(Currently using %r)"
) % (version, e.args[0])
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return do_download()
except pkg_resources.DistributionNotFound:
return do_download()
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])
lazr.delegates-1.2.0/src/ 0000755 0001751 0001751 00000000000 11420057564 015021 5 ustar salgado salgado lazr.delegates-1.2.0/src/lazr/ 0000755 0001751 0001751 00000000000 11420057564 015771 5 ustar salgado salgado lazr.delegates-1.2.0/src/lazr/delegates/ 0000755 0001751 0001751 00000000000 11420057564 017726 5 ustar salgado salgado lazr.delegates-1.2.0/src/lazr/delegates/NEWS.txt 0000644 0001751 0001751 00000001047 11420056465 021244 0 ustar salgado salgado =======================
NEWS for lazr.delegates
=======================
1.2.0 (2010-07-16)
==================
- Extend Passthrough so that it takes an extra (optional) callable argument,
used to adapt the context before accessing the delegated attribute.
1.1.0 (2009-08-31)
==================
- Remove build dependencies on bzr and egg_info
- remove sys.path hack in setup.py for __version__
1.0.1 (2009-03-24)
==================
- specify only v3 of LGPL
- build/developer improvements
1.0 (2008-12-19)
================
- Initial release
lazr.delegates-1.2.0/src/lazr/delegates/version.txt 0000644 0001751 0001751 00000000006 11420056375 022147 0 ustar salgado salgado 1.2.0
lazr.delegates-1.2.0/src/lazr/delegates/tests/ 0000755 0001751 0001751 00000000000 11420057564 021070 5 ustar salgado salgado lazr.delegates-1.2.0/src/lazr/delegates/tests/test_docs.py 0000644 0001751 0001751 00000003402 11417351266 023432 0 ustar salgado salgado # Copyright 2008-2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.delegates
#
# lazr.delegates is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.delegates is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.delegates. If not, see .
"Test harness for doctests."
# pylint: disable-msg=E0611,W0142
__metaclass__ = type
__all__ = [
'additional_tests',
]
import atexit
import doctest
import os
from pkg_resources import (
resource_filename, resource_exists, resource_listdir, cleanup_resources)
import unittest
DOCTEST_FLAGS = (
doctest.ELLIPSIS |
doctest.NORMALIZE_WHITESPACE |
doctest.REPORT_NDIFF)
def additional_tests():
"Run the doc tests (README.txt and docs/*, if any exist)"
doctest_files = [
os.path.abspath(resource_filename('lazr.delegates', 'README.txt'))]
if resource_exists('lazr.delegates', 'docs'):
for name in resource_listdir('lazr.delegates', 'docs'):
if name.endswith('.txt'):
doctest_files.append(
os.path.abspath(
resource_filename('lazr.delegates', 'docs/%s' % name)))
kwargs = dict(module_relative=False, optionflags=DOCTEST_FLAGS)
atexit.register(cleanup_resources)
return unittest.TestSuite((
doctest.DocFileSuite(*doctest_files, **kwargs)))
lazr.delegates-1.2.0/src/lazr/delegates/tests/__init__.py 0000644 0001751 0001751 00000001345 11417351266 023206 0 ustar salgado salgado # Copyright 2008 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.delegates.
#
# lazr.delegates is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.delegates is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.delegates. If not, see .
"""Tests for lazr.delegates"""
lazr.delegates-1.2.0/src/lazr/delegates/README.txt 0000644 0001751 0001751 00000017330 11420055514 021421 0 ustar salgado salgado ..
This file is part of lazr.delegates.
lazr.delegates is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
lazr.delegates is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with lazr.delegates. If not, see .
The ``lazr.delegates`` Package
******************************
The ``lazr.delegates`` package makes it easy to write objects that delegate
behavior to another object. The new object adds some property or behavior on
to the other object, while still providing the underlying interface, and
delegating behavior.
=====
Usage
=====
The ``delegates`` function makes a class implement zero or more
interfaces by delegating the implementation to another object. In the
case of a class providing an adapter, that object will be the 'context',
but it can really be any object stored in an attribute. So while the
interfaces use an inheritance mechanism, the classes use a composition
mechanism.
For example we can define two interfaces IFoo0 <- IFoo...
>>> from lazr.delegates import delegates
>>> from zope.interface import Interface, Attribute
>>> class IFoo0(Interface):
... spoo = Attribute('attribute in IFoo0')
>>> class IFoo(IFoo0):
... def bar():
... "some method"
... baz = Attribute("some attribute")
And two classes (BaseFoo0 <- BaseFoo) that do something interesting.
>>> class BaseFoo0:
... spoo = 'some spoo'
>>> class BaseFoo(BaseFoo0):
... def bar(self):
... return 'bar'
... baz = 'hi baz!'
SomeClass can implement IFoo by delegating to an instance of BaseFoo
stored in the 'context' attribute. Note that ``delegates`` takes the
interface as the argument. By default, 'context' is the attribute
containing the object to which the interface implementation is
delegated.
>>> class SomeClass(object):
... delegates(IFoo)
... def __init__(self, context):
... self.context = context
>>> f = BaseFoo()
>>> s = SomeClass(f)
>>> s.bar()
'bar'
>>> s.baz
'hi baz!'
>>> s.spoo
'some spoo'
>>> IFoo.providedBy(s)
True
The ``delegates()`` function takes an optional keyword argument to change
attribute containing the object to delegate to. So an existing class,
such as SomeOtherClass, can declare the name of the attribute to which to
delegate.
>>> class SomeOtherClass(object):
... delegates(IFoo, context='myfoo')
... def __init__(self, foo):
... self.myfoo = foo
... spoo = 'spoo from SomeOtherClass'
>>> f = BaseFoo()
>>> s = SomeOtherClass(f)
>>> s.bar()
'bar'
>>> s.baz
'hi baz!'
>>> s.spoo
'spoo from SomeOtherClass'
>>> s.baz = 'fish'
>>> s.baz
'fish'
>>> f.baz
'fish'
The ``delegates()`` function can only be used in new-style classes. An
error is raised when a classic-style class is modified to implement an
interface.
>>> class SomeClassicClass:
... delegates(IFoo)
Traceback (most recent call last):
...
TypeError: Cannot use delegates() on a classic
class: __builtin__.SomeClassicClass.
The ``delegates()`` function cannot be used out side of a class definition,
such as in a module or in a function.
>>> delegates(IFoo)
Traceback (most recent call last):
...
TypeError: delegates() can be used only from a class definition.
Multiple interfaces can be specified by passing an iterable to
delegates().
>>> class IOther(Interface):
... another = Attribute("another attribute")
>>> class BaseOtherFoo(BaseFoo):
... another = 'yes, another'
>>> class SomeOtherClass(object):
... delegates([IFoo, IOther])
>>> s = SomeOtherClass()
>>> s.context = BaseOtherFoo()
>>> s.another
'yes, another'
>>> s.baz
'hi baz!'
>>> s.spoo
'some spoo'
>>> IFoo.providedBy(s)
True
>>> IOther.providedBy(s)
True
This can be convenient when decorating an existing object.
>>> from zope.interface import implements
>>> class MoreFoo(BaseFoo, BaseOtherFoo):
... implements(IFoo, IOther)
>>> foo = MoreFoo()
>>> from zope.interface import providedBy
>>> class WithExtraTeapot(object):
... delegates(providedBy(foo))
... teapot = 'i am a teapot'
>>> foo_with_teapot = WithExtraTeapot()
>>> foo_with_teapot.context = foo
>>> foo_with_teapot.baz
'hi baz!'
>>> foo_with_teapot.another
'yes, another'
>>> foo_with_teapot.teapot
'i am a teapot'
>>> IFoo.providedBy(foo_with_teapot)
True
>>> IOther.providedBy(foo_with_teapot)
True
==============
Implementation
==============
The Passthrough class is the implementation machinery of ``delegates()``. It
uses the descriptor protocol to implement the delegation behaviour provided by
``delegates()``. It takes at least two arguments: the name of the attribute
that is delegated, and the name of the attribute containing the object to
which to delegate.
To illustrate, p and p2 are two Passthrough instances that use the
instance assigned to 'mycontext' to call the 'foo' attribute and
the 'clsmethod' method.
>>> from lazr.delegates import Passthrough
>>> p = Passthrough('foo', 'mycontext')
>>> p2 = Passthrough('clsmethod', 'mycontext')
Base is a class the implements both 'foo' and 'clsmethod'.
>>> class Base:
... foo = 'foo from Base'
... def clsmethod(cls):
... return str(cls)
... clsmethod = classmethod(clsmethod)
Adapter is a class that has an instance of Base assigned to the
attribute 'mycontext'.
>>> base = Base()
>>> class Adapter:
... mycontext = base
>>> adapter = Adapter()
The Passthrough instances can get and set their prescribed attributes
when passed an instance of adapter.
>>> p.__get__(adapter)
'foo from Base'
>>> p.__get__(None, Adapter) is p
True
>>> p2.__get__(adapter)()
'__builtin__.Base'
>>> p.__set__(adapter, 'new value')
>>> base.foo
'new value'
Passthrough does not implement __delete__. An error is raised if
it is called.
>>> p.__delete__(adapter)
Traceback (most recent call last):
...
NotImplementedError
Passthrough's third argument (adaptation) is optional and, when provided,
should be a zope.interface.Interface subclass (although in practice any
callable will do) to which the instance is adapted before getting/setting the
delegated attribute.
# HasNoFoo does not have a .foo attribute...
>>> class HasNoFoo(object):
... _foo = 1
>>> no_foo = HasNoFoo()
# ... but IHasFooAdapter uses HasNoFoo._foo to provide its own .foo, so it
# works like an adapter for HasNoFoo into some interface that provides
# a 'foo' attribute.
>>> class IHasFooAdapter(object):
... def __init__(self, inst):
... self.inst = inst
... def _get_foo(self):
... return self.inst._foo
... def _set_foo(self, value):
... self.inst._foo = value
... foo = property(_get_foo, _set_foo)
>>> class Example(object):
... context = no_foo
>>> p = Passthrough('foo', 'context', adaptation=IHasFooAdapter)
>>> e = Example()
>>> p.__get__(e)
1
>>> p.__set__(e, 2)
>>> p.__get__(e)
2
===============
Other Documents
===============
.. toctree::
:glob:
*
docs/*
lazr.delegates-1.2.0/src/lazr/delegates/__init__.py 0000644 0001751 0001751 00000002150 11417351266 022037 0 ustar salgado salgado # Copyright 2008 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.delegates.
#
# lazr.delegates is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.delegates is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.delegates. If not, see .
"""Decorator helpers that simplify class composition."""
import pkg_resources
__version__ = pkg_resources.resource_string(
"lazr.delegates", "version.txt").strip()
# While we generally frown on "*" imports, this, combined with the fact we
# only test code from this module, means that we can verify what has been
# exported.
from lazr.delegates._delegates import *
from lazr.delegates._delegates import __all__
lazr.delegates-1.2.0/src/lazr/delegates/_delegates.py 0000644 0001751 0001751 00000010543 11420055514 022370 0 ustar salgado salgado # Copyright 2008 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.delegates.
#
# lazr.delegates is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.delegates is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.delegates. If not, see .
"""Decorator helpers that simplify class composition."""
__metaclass__ = type
__all__ = ['delegates', 'Passthrough']
import sys
from types import ClassType
from zope.interface.advice import addClassAdvisor
from zope.interface import classImplements
from zope.interface.interfaces import IInterface
def delegates(interface_spec, context='context'):
"""Make an adapter into a decorator.
Use like:
class RosettaProject:
implements(IRosettaProject)
delegates(IProject)
def __init__(self, context):
self.context = context
def methodFromRosettaProject(self):
return self.context.methodFromIProject()
If you want to use a different name than "context" then you can explicitly
say so:
class RosettaProject:
implements(IRosettaProject)
delegates(IProject, context='project')
def __init__(self, project):
self.project = project
def methodFromRosettaProject(self):
return self.project.methodFromIProject()
The adapter class will implement the interface it is decorating.
The minimal decorator looks like this:
class RosettaProject:
delegates(IProject)
def __init__(self, context):
self.context = context
"""
# pylint: disable-msg=W0212
frame = sys._getframe(1)
locals = frame.f_locals
# Try to make sure we were called from a class def
if (locals is frame.f_globals) or ('__module__' not in locals):
raise TypeError(
"delegates() can be used only from a class definition.")
locals['__delegates_advice_data__'] = interface_spec, context
addClassAdvisor(_delegates_advice, depth=2)
def _delegates_advice(cls):
"""Add a Passthrough class for each missing interface attribute.
This function connects the decorator class to the delegate class.
Only new-style classes are supported.
"""
interface_spec, contextvar = cls.__dict__['__delegates_advice_data__']
del cls.__delegates_advice_data__
if type(cls) is ClassType:
raise TypeError(
'Cannot use delegates() on a classic class: %s.' % cls)
if IInterface.providedBy(interface_spec):
interfaces = [interface_spec]
else:
interfaces = interface_spec
not_found = object()
for interface in interfaces:
classImplements(cls, interface)
for name in interface:
if getattr(cls, name, not_found) is not_found:
setattr(cls, name, Passthrough(name, contextvar))
return cls
class Passthrough:
"""Call the delegated class for the decorator class.
If the ``adaptation`` argument is not None, it should be a callable. It
will be called with the context, and should return an object that will
have the delegated attribute. The ``adaptation`` argument is expected to
be used with an interface, to adapt the context.
"""
def __init__(self, name, contextvar, adaptation=None):
self.name = name
self.contextvar = contextvar
self.adaptation = adaptation
def __get__(self, inst, cls=None):
if inst is None:
return self
else:
context = getattr(inst, self.contextvar)
if self.adaptation is not None:
context = self.adaptation(context)
return getattr(context, self.name)
def __set__(self, inst, value):
context = getattr(inst, self.contextvar)
if self.adaptation is not None:
context = self.adaptation(context)
setattr(context, self.name, value)
def __delete__(self, inst):
raise NotImplementedError
lazr.delegates-1.2.0/src/lazr/__init__.py 0000644 0001751 0001751 00000001617 11417351266 020111 0 ustar salgado salgado # Copyright 2008 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.delegates.
#
# lazr.delegates is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.delegates is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.delegates. If not, see .
# this is a namespace package
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
lazr.delegates-1.2.0/src/lazr.delegates.egg-info/ 0000755 0001751 0001751 00000000000 11420057564 021417 5 ustar salgado salgado lazr.delegates-1.2.0/src/lazr.delegates.egg-info/requires.txt 0000644 0001751 0001751 00000000075 11420057564 024021 0 ustar salgado salgado setuptools
zope.interface
[docs]
Sphinx
z3c.recipe.sphinxdoc lazr.delegates-1.2.0/src/lazr.delegates.egg-info/SOURCES.txt 0000644 0001751 0001751 00000001105 11420057564 023300 0 ustar salgado salgado README.txt
ez_setup.py
setup.py
src/lazr/__init__.py
src/lazr.delegates.egg-info/PKG-INFO
src/lazr.delegates.egg-info/SOURCES.txt
src/lazr.delegates.egg-info/dependency_links.txt
src/lazr.delegates.egg-info/namespace_packages.txt
src/lazr.delegates.egg-info/not-zip-safe
src/lazr.delegates.egg-info/requires.txt
src/lazr.delegates.egg-info/top_level.txt
src/lazr/delegates/NEWS.txt
src/lazr/delegates/README.txt
src/lazr/delegates/__init__.py
src/lazr/delegates/_delegates.py
src/lazr/delegates/version.txt
src/lazr/delegates/tests/__init__.py
src/lazr/delegates/tests/test_docs.py lazr.delegates-1.2.0/src/lazr.delegates.egg-info/namespace_packages.txt 0000644 0001751 0001751 00000000005 11420057564 025745 0 ustar salgado salgado lazr
lazr.delegates-1.2.0/src/lazr.delegates.egg-info/dependency_links.txt 0000644 0001751 0001751 00000000001 11420057564 025465 0 ustar salgado salgado
lazr.delegates-1.2.0/src/lazr.delegates.egg-info/PKG-INFO 0000644 0001751 0001751 00000026571 11420057564 022527 0 ustar salgado salgado Metadata-Version: 1.0
Name: lazr.delegates
Version: 1.2.0
Summary: Easily write objects that delegate behavior.
Home-page: https://launchpad.net/lazr.delegates
Author: LAZR Developers
Author-email: lazr-developers@lists.launchpad.net
License: LGPL v3
Download-URL: https://launchpad.net/lazr.delegates/+download
Description: ..
This file is part of lazr.delegates.
lazr.delegates is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
lazr.delegates is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with lazr.delegates. If not, see .
The ``lazr.delegates`` Package
******************************
The ``lazr.delegates`` package makes it easy to write objects that delegate
behavior to another object. The new object adds some property or behavior on
to the other object, while still providing the underlying interface, and
delegating behavior.
=====
Usage
=====
The ``delegates`` function makes a class implement zero or more
interfaces by delegating the implementation to another object. In the
case of a class providing an adapter, that object will be the 'context',
but it can really be any object stored in an attribute. So while the
interfaces use an inheritance mechanism, the classes use a composition
mechanism.
For example we can define two interfaces IFoo0 <- IFoo...
>>> from lazr.delegates import delegates
>>> from zope.interface import Interface, Attribute
>>> class IFoo0(Interface):
... spoo = Attribute('attribute in IFoo0')
>>> class IFoo(IFoo0):
... def bar():
... "some method"
... baz = Attribute("some attribute")
And two classes (BaseFoo0 <- BaseFoo) that do something interesting.
>>> class BaseFoo0:
... spoo = 'some spoo'
>>> class BaseFoo(BaseFoo0):
... def bar(self):
... return 'bar'
... baz = 'hi baz!'
SomeClass can implement IFoo by delegating to an instance of BaseFoo
stored in the 'context' attribute. Note that ``delegates`` takes the
interface as the argument. By default, 'context' is the attribute
containing the object to which the interface implementation is
delegated.
>>> class SomeClass(object):
... delegates(IFoo)
... def __init__(self, context):
... self.context = context
>>> f = BaseFoo()
>>> s = SomeClass(f)
>>> s.bar()
'bar'
>>> s.baz
'hi baz!'
>>> s.spoo
'some spoo'
>>> IFoo.providedBy(s)
True
The ``delegates()`` function takes an optional keyword argument to change
attribute containing the object to delegate to. So an existing class,
such as SomeOtherClass, can declare the name of the attribute to which to
delegate.
>>> class SomeOtherClass(object):
... delegates(IFoo, context='myfoo')
... def __init__(self, foo):
... self.myfoo = foo
... spoo = 'spoo from SomeOtherClass'
>>> f = BaseFoo()
>>> s = SomeOtherClass(f)
>>> s.bar()
'bar'
>>> s.baz
'hi baz!'
>>> s.spoo
'spoo from SomeOtherClass'
>>> s.baz = 'fish'
>>> s.baz
'fish'
>>> f.baz
'fish'
The ``delegates()`` function can only be used in new-style classes. An
error is raised when a classic-style class is modified to implement an
interface.
>>> class SomeClassicClass:
... delegates(IFoo)
Traceback (most recent call last):
...
TypeError: Cannot use delegates() on a classic
class: __builtin__.SomeClassicClass.
The ``delegates()`` function cannot be used out side of a class definition,
such as in a module or in a function.
>>> delegates(IFoo)
Traceback (most recent call last):
...
TypeError: delegates() can be used only from a class definition.
Multiple interfaces can be specified by passing an iterable to
delegates().
>>> class IOther(Interface):
... another = Attribute("another attribute")
>>> class BaseOtherFoo(BaseFoo):
... another = 'yes, another'
>>> class SomeOtherClass(object):
... delegates([IFoo, IOther])
>>> s = SomeOtherClass()
>>> s.context = BaseOtherFoo()
>>> s.another
'yes, another'
>>> s.baz
'hi baz!'
>>> s.spoo
'some spoo'
>>> IFoo.providedBy(s)
True
>>> IOther.providedBy(s)
True
This can be convenient when decorating an existing object.
>>> from zope.interface import implements
>>> class MoreFoo(BaseFoo, BaseOtherFoo):
... implements(IFoo, IOther)
>>> foo = MoreFoo()
>>> from zope.interface import providedBy
>>> class WithExtraTeapot(object):
... delegates(providedBy(foo))
... teapot = 'i am a teapot'
>>> foo_with_teapot = WithExtraTeapot()
>>> foo_with_teapot.context = foo
>>> foo_with_teapot.baz
'hi baz!'
>>> foo_with_teapot.another
'yes, another'
>>> foo_with_teapot.teapot
'i am a teapot'
>>> IFoo.providedBy(foo_with_teapot)
True
>>> IOther.providedBy(foo_with_teapot)
True
==============
Implementation
==============
The Passthrough class is the implementation machinery of ``delegates()``. It
uses the descriptor protocol to implement the delegation behaviour provided by
``delegates()``. It takes at least two arguments: the name of the attribute
that is delegated, and the name of the attribute containing the object to
which to delegate.
To illustrate, p and p2 are two Passthrough instances that use the
instance assigned to 'mycontext' to call the 'foo' attribute and
the 'clsmethod' method.
>>> from lazr.delegates import Passthrough
>>> p = Passthrough('foo', 'mycontext')
>>> p2 = Passthrough('clsmethod', 'mycontext')
Base is a class the implements both 'foo' and 'clsmethod'.
>>> class Base:
... foo = 'foo from Base'
... def clsmethod(cls):
... return str(cls)
... clsmethod = classmethod(clsmethod)
Adapter is a class that has an instance of Base assigned to the
attribute 'mycontext'.
>>> base = Base()
>>> class Adapter:
... mycontext = base
>>> adapter = Adapter()
The Passthrough instances can get and set their prescribed attributes
when passed an instance of adapter.
>>> p.__get__(adapter)
'foo from Base'
>>> p.__get__(None, Adapter) is p
True
>>> p2.__get__(adapter)()
'__builtin__.Base'
>>> p.__set__(adapter, 'new value')
>>> base.foo
'new value'
Passthrough does not implement __delete__. An error is raised if
it is called.
>>> p.__delete__(adapter)
Traceback (most recent call last):
...
NotImplementedError
Passthrough's third argument (adaptation) is optional and, when provided,
should be a zope.interface.Interface subclass (although in practice any
callable will do) to which the instance is adapted before getting/setting the
delegated attribute.
# HasNoFoo does not have a .foo attribute...
>>> class HasNoFoo(object):
... _foo = 1
>>> no_foo = HasNoFoo()
# ... but IHasFooAdapter uses HasNoFoo._foo to provide its own .foo, so it
# works like an adapter for HasNoFoo into some interface that provides
# a 'foo' attribute.
>>> class IHasFooAdapter(object):
... def __init__(self, inst):
... self.inst = inst
... def _get_foo(self):
... return self.inst._foo
... def _set_foo(self, value):
... self.inst._foo = value
... foo = property(_get_foo, _set_foo)
>>> class Example(object):
... context = no_foo
>>> p = Passthrough('foo', 'context', adaptation=IHasFooAdapter)
>>> e = Example()
>>> p.__get__(e)
1
>>> p.__set__(e, 2)
>>> p.__get__(e)
2
===============
Other Documents
===============
.. toctree::
:glob:
*
docs/*
=======================
NEWS for lazr.delegates
=======================
1.2.0 (2010-07-16)
==================
- Extend Passthrough so that it takes an extra (optional) callable argument,
used to adapt the context before accessing the delegated attribute.
1.1.0 (2009-08-31)
==================
- Remove build dependencies on bzr and egg_info
- remove sys.path hack in setup.py for __version__
1.0.1 (2009-03-24)
==================
- specify only v3 of LGPL
- build/developer improvements
1.0 (2008-12-19)
================
- Initial release
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
lazr.delegates-1.2.0/src/lazr.delegates.egg-info/top_level.txt 0000644 0001751 0001751 00000000005 11420057564 024144 0 ustar salgado salgado lazr
lazr.delegates-1.2.0/src/lazr.delegates.egg-info/not-zip-safe 0000644 0001751 0001751 00000000001 11420057547 023646 0 ustar salgado salgado