[Checkins] SVN: Sandbox/faassen/iface/ Initial import of an experimental package playing with interface registry

Martijn Faassen faassen at startifact.com
Tue Jan 5 17:37:00 EST 2010


Log message for revision 107724:
  Initial import of an experimental package playing with interface registry
  concepts.
  

Changed:
  A   Sandbox/faassen/iface/
  A   Sandbox/faassen/iface/bootstrap.py
  A   Sandbox/faassen/iface/buildout.cfg
  A   Sandbox/faassen/iface/setup.py
  A   Sandbox/faassen/iface/src/
  A   Sandbox/faassen/iface/src/iface/
  A   Sandbox/faassen/iface/src/iface/__init__.py
  A   Sandbox/faassen/iface/src/iface/mapping.py
  A   Sandbox/faassen/iface/src/iface/mapping.txt
  A   Sandbox/faassen/iface/src/iface/tests.py

-=-
Added: Sandbox/faassen/iface/bootstrap.py
===================================================================
--- Sandbox/faassen/iface/bootstrap.py	                        (rev 0)
+++ Sandbox/faassen/iface/bootstrap.py	2010-01-05 22:37:00 UTC (rev 107724)
@@ -0,0 +1,84 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+is_jython = sys.platform.startswith('java')
+
+try:
+    import pkg_resources
+except ImportError:
+    ez = {}
+    exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                         ).read() in ez
+    ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+    import pkg_resources
+
+if sys.platform == 'win32':
+    def quote(c):
+        if ' ' in c:
+            return '"%s"' % c # work around spawn lamosity on windows
+        else:
+            return c
+else:
+    def quote (c):
+        return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws  = pkg_resources.working_set
+
+if len(sys.argv) > 2 and sys.argv[1] == '--version':
+    VERSION = '==%s' % sys.argv[2]
+    args = sys.argv[3:] + ['bootstrap']
+else:
+    VERSION = ''
+    args = sys.argv[1:] + ['bootstrap']
+
+if is_jython:
+    import subprocess
+
+    assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
+           quote(tmpeggs), 'zc.buildout' + VERSION],
+           env=dict(os.environ,
+               PYTHONPATH=
+               ws.find(pkg_resources.Requirement.parse('setuptools')).location
+               ),
+           ).wait() == 0
+
+else:
+    assert os.spawnle(
+        os.P_WAIT, sys.executable, quote (sys.executable),
+        '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
+        dict(os.environ,
+            PYTHONPATH=
+            ws.find(pkg_resources.Requirement.parse('setuptools')).location
+            ),
+        ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout' + VERSION)
+import zc.buildout.buildout
+zc.buildout.buildout.main(args)
+shutil.rmtree(tmpeggs)

Added: Sandbox/faassen/iface/buildout.cfg
===================================================================
--- Sandbox/faassen/iface/buildout.cfg	                        (rev 0)
+++ Sandbox/faassen/iface/buildout.cfg	2010-01-05 22:37:00 UTC (rev 107724)
@@ -0,0 +1,13 @@
+[buildout]
+develop = .
+parts = devpython test
+
+[devpython]
+recipe = zc.recipe.egg
+interpreter = devpython
+eggs = iface
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = iface
+defaults = ['--tests-pattern', '^f?tests$', '-v']

Added: Sandbox/faassen/iface/setup.py
===================================================================
--- Sandbox/faassen/iface/setup.py	                        (rev 0)
+++ Sandbox/faassen/iface/setup.py	2010-01-05 22:37:00 UTC (rev 107724)
@@ -0,0 +1,43 @@
+import os
+from setuptools import setup, find_packages
+
+def read(*filenames):
+    return open(os.path.join(os.path.dirname(__file__), *filenames)).read()
+
+# long_description = (
+#     read('src', 'traject', 'traject.txt')
+#     + '\n' +
+#     read('CHANGES.txt')
+#     + '\n' +
+#     'Download\n'
+#     '********\n'
+#     )
+
+setup(name='iface',
+      version = '0.11dev',
+      description="Interfaces and lookup for Python.",
+      long_description='',
+      # Use classifiers that are already listed at:
+      # http://pypi.python.org/pypi?%3Aaction=list_classifiers
+      classifiers=['Development Status :: 4 - Beta',
+                   'Environment :: Web Environment',
+                   'Intended Audience :: Developers',
+                   'License :: OSI Approved :: Zope Public License',
+                   'Operating System :: OS Independent',
+                   'Programming Language :: Python',
+                   'Topic :: Software Development :: Libraries',
+                   ],
+      keywords="interface interfaces",
+      author="Martijn Faassen and Thomas Lotze",
+      author_email="faassen at startifact.com",
+      license="ZPL",
+      package_dir={'': 'src'},
+      packages=find_packages('src'),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=['setuptools',
+                        ],
+      entry_points="""
+      # Add entry points here
+      """,
+      )

Added: Sandbox/faassen/iface/src/iface/__init__.py
===================================================================
--- Sandbox/faassen/iface/src/iface/__init__.py	                        (rev 0)
+++ Sandbox/faassen/iface/src/iface/__init__.py	2010-01-05 22:37:00 UTC (rev 107724)
@@ -0,0 +1,2 @@
+from iface.mapping import Map, MapKey
+

Added: Sandbox/faassen/iface/src/iface/mapping.py
===================================================================
--- Sandbox/faassen/iface/src/iface/mapping.py	                        (rev 0)
+++ Sandbox/faassen/iface/src/iface/mapping.py	2010-01-05 22:37:00 UTC (rev 107724)
@@ -0,0 +1,33 @@
+class MapKey(object):
+    def __init__(self, key, parents=()):
+        self.key = key
+        self.parents = parents
+        # we need Python's mro, but we don't have classes. We create
+        # some with the same structure as our parent structure. then we
+        # get the mro
+        self._mro_helper = type('fake_type',
+                                tuple(parent._mro_helper for
+                                      parent in parents),
+                                {'mapkey': self})
+        # we then store the mro without the last entry, which is
+        # always object
+        self._mro = self._mro_helper.__mro__[:-1]
+
+    def __hash__(self):
+        return hash(self.key)
+
+    def __repr__(self):
+        return "<MapKey: %r>" % self.key
+
+class Map(dict):
+    def __getitem__(self, key):
+        for base in key._mro:
+            if base is object:
+                break
+            try:
+                return super(Map, self).__getitem__(base.mapkey)
+            except KeyError:
+                pass
+        raise KeyError(key)
+
+

Added: Sandbox/faassen/iface/src/iface/mapping.txt
===================================================================
--- Sandbox/faassen/iface/src/iface/mapping.txt	                        (rev 0)
+++ Sandbox/faassen/iface/src/iface/mapping.txt	2010-01-05 22:37:00 UTC (rev 107724)
@@ -0,0 +1,99 @@
+A mapping with acyclic directed graph key
+=========================================
+
+A normal mapping (dictionary) in Python has keys that are completely
+independent from each other. If you look up a particular key, either that
+key is present in the mapping or not at all.
+
+This is a mapping that understands about relations between keys. Keys
+can have zero or more parents. If a key is not found, a value will
+still be found if a parent key can be found.
+
+We create a special kind of keys that can have parents. First we
+create one without parents::
+
+  >>> from iface import MapKey
+  >>> a = MapKey('a')
+
+Now we create keys ``b`` and ``c`` that both have ``a`` for a parent::
+
+  >>> b = MapKey('b', [a])
+  >>> c = MapKey('c', [a])
+
+Finally we create a key ``d`` that has two parents, ``b`` and ``c``::
+
+  >>> d = MapKey('d', [b, c])
+
+Now we create a mapping::
+
+  >>> from iface import Map
+  >>> map = Map()
+
+``a`` can be used just like any dictionary key::
+
+  >>> map[a] = u'Value for A'
+  >>> map[a]
+  u'Value for A'
+  >>> del map[a]
+  >>> map[a]
+  Traceback (most recent call last):
+    ...
+  KeyError: <MapKey: 'a'>
+ 
+We now register something for ``b``::
+
+  >>> map[b] = u'Value for B'
+
+Of course we can find ``b``::
+  >>> map[b]
+  u'Value for B'
+
+We cannot find ``c`` as there is no relation to ``b``::
+
+  >>> map[c]
+  Traceback (most recent call last):
+    ...
+  KeyError: <MapKey: 'c'>
+
+Neither can we find ``a``, as ``b`` is not an ancestor of ``a``::
+
+  >>> map[a]
+  Traceback (most recent call last):
+    ...
+  KeyError: <MapKey: 'a'>
+
+Now for the special behavor. Since ``d`` does have ``b`` as an
+ancestor, we can look it up, finding the value for B::
+
+  >>> map[d]
+  u'Value for B'
+
+We register a value for ``c``::
+
+  >>> map[c] = u'Value for C'
+
+When we look up the value for ``d`` we get still the value for ``b``,
+as it comes first in the parent resolution order (similar to Python's
+method resolution order for classes)::
+
+  >>> map[d]
+  u'Value for B'
+
+When we take out the value for ``b`` we get the value for ``c`` when we
+look up ``d``::
+  
+  >>> del map[b]
+  >>> map[d]
+  u'Value for C'
+
+If we store a ``d`` key itself, then we will get its value, not the
+value of its ancestor ``c``::
+
+  >>> map[d] = u'Value for D'
+  >>> map[d]
+  u'Value for D'
+
+next: multi lookup. What is the behavior of zope.interface? Do earlier
+entries in the lookup list always weigh more heavily than the second
+one? We need backtracking in case we don't find anything for the next
+entry. That's not exactly efficient.

Added: Sandbox/faassen/iface/src/iface/tests.py
===================================================================
--- Sandbox/faassen/iface/src/iface/tests.py	                        (rev 0)
+++ Sandbox/faassen/iface/src/iface/tests.py	2010-01-05 22:37:00 UTC (rev 107724)
@@ -0,0 +1,12 @@
+import unittest, doctest
+
+def test_suite():
+    suite = unittest.TestSuite()
+
+    optionflags = doctest.NORMALIZE_WHITESPACE + doctest.ELLIPSIS
+
+    suite.addTests([
+            doctest.DocFileSuite('mapping.txt',
+                                 optionflags=optionflags)])
+    return suite
+



More information about the checkins mailing list