lazr.delegates-1.2.0/0000755000175100017510000000000011420057564014232 5ustar salgadosalgadolazr.delegates-1.2.0/setup.cfg0000644000175100017510000000007311420057564016053 0ustar salgadosalgado[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 lazr.delegates-1.2.0/PKG-INFO0000644000175100017510000002657111420057564015342 0ustar salgadosalgadoMetadata-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.txt0000644000175100017510000000132011417351266015726 0ustar salgadosalgadoEasily 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.py0000755000175100017510000000456211417351266015760 0ustar salgadosalgado#!/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.py0000644000175100017510000002246611417351266016456 0ustar salgadosalgado#!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/0000755000175100017510000000000011420057564015021 5ustar salgadosalgadolazr.delegates-1.2.0/src/lazr/0000755000175100017510000000000011420057564015771 5ustar salgadosalgadolazr.delegates-1.2.0/src/lazr/delegates/0000755000175100017510000000000011420057564017726 5ustar salgadosalgadolazr.delegates-1.2.0/src/lazr/delegates/NEWS.txt0000644000175100017510000000104711420056465021244 0ustar salgadosalgado======================= 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.txt0000644000175100017510000000000611420056375022147 0ustar salgadosalgado1.2.0 lazr.delegates-1.2.0/src/lazr/delegates/tests/0000755000175100017510000000000011420057564021070 5ustar salgadosalgadolazr.delegates-1.2.0/src/lazr/delegates/tests/test_docs.py0000644000175100017510000000340211417351266023432 0ustar salgadosalgado# 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__.py0000644000175100017510000000134511417351266023206 0ustar salgadosalgado# 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.txt0000644000175100017510000001733011420055514021421 0ustar salgadosalgado.. 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__.py0000644000175100017510000000215011417351266022037 0ustar salgadosalgado# 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.py0000644000175100017510000001054311420055514022370 0ustar salgadosalgado# 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__.py0000644000175100017510000000161711417351266020111 0ustar salgadosalgado# 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/0000755000175100017510000000000011420057564021417 5ustar salgadosalgadolazr.delegates-1.2.0/src/lazr.delegates.egg-info/requires.txt0000644000175100017510000000007511420057564024021 0ustar salgadosalgadosetuptools zope.interface [docs] Sphinx z3c.recipe.sphinxdoclazr.delegates-1.2.0/src/lazr.delegates.egg-info/SOURCES.txt0000644000175100017510000000110511420057564023300 0ustar salgadosalgadoREADME.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.pylazr.delegates-1.2.0/src/lazr.delegates.egg-info/namespace_packages.txt0000644000175100017510000000000511420057564025745 0ustar salgadosalgadolazr lazr.delegates-1.2.0/src/lazr.delegates.egg-info/dependency_links.txt0000644000175100017510000000000111420057564025465 0ustar salgadosalgado lazr.delegates-1.2.0/src/lazr.delegates.egg-info/PKG-INFO0000644000175100017510000002657111420057564022527 0ustar salgadosalgadoMetadata-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.txt0000644000175100017510000000000511420057564024144 0ustar salgadosalgadolazr lazr.delegates-1.2.0/src/lazr.delegates.egg-info/not-zip-safe0000644000175100017510000000000111420057547023646 0ustar salgadosalgado