lazr.enum-1.1.4/0000775000175000017500000000000011743442313012774 5ustar ianian00000000000000lazr.enum-1.1.4/setup.py0000775000175000017500000000477611743424744014537 0ustar ianian00000000000000#!/usr/bin/env python # Copyright 2009 Canonical Ltd. All rights reserved. # # This file is part of lazr.enum # # lazr.enum 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.enum 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.enum. 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): marker = '.. pypi description ends here' res = [] for value in docname_or_string: if value.endswith('.txt'): f = open(value) value = f.read() f.close() idx = value.find(marker) if idx >= 0: value = value[:idx] res.append(value) if not value.endswith('\n'): res.append('') return '\n'.join(res) # end generic helpers __version__ = open("src/lazr/enum/version.txt").read().strip() setup( name='lazr.enum', 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/enum/README.txt', 'src/lazr/enum/NEWS.txt'), license='LGPL v3', install_requires=[ 'setuptools', 'zope.interface', 'zope.schema', ], url='https://launchpad.net/lazr.enum', download_url= 'https://launchpad.net/lazr.enum/+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( proxy=['zope.security'], docs=['Sphinx', 'z3c.recipe.sphinxdoc'] ), test_suite='lazr.enum.tests', ) lazr.enum-1.1.4/ez_setup.py0000664000175000017500000002246711743427510015221 0ustar ianian00000000000000#!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.6c11" 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.enum-1.1.4/PKG-INFO0000664000175000017500000004454411743442313014104 0ustar ianian00000000000000Metadata-Version: 1.1 Name: lazr.enum Version: 1.1.4 Summary: Enums with zope.schema vocabulary support and database-friendly conveniences. Home-page: https://launchpad.net/lazr.enum Author: LAZR Developers Author-email: lazr-developers@lists.launchpad.net License: LGPL v3 Download-URL: https://launchpad.net/lazr.enum/+download Description: .. This file is part of lazr.enum. lazr.enum 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.enum 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.enum. If not, see . Enumerated Types **************** Enumerated types are used primarily in two distinct places in the Launchpad code: selector types; and database types. Simple enumerated types do not have values, whereas database enumerated types are a mapping from an integer value to something meaningful in the code. >>> from lazr.enum import ( ... EnumeratedType, DBEnumeratedType, Item, DBItem, use_template) The `enum` values of EnumeratedTypes are instances of Item. >>> class Fruit(EnumeratedType): ... "A choice of fruit." ... APPLE = Item('Apple') ... PEAR = Item('Pear') ... ORANGE = Item('Orange') =================== IVocabulary support =================== Enumerated types support IVocabularyTokenized. >>> from zope.interface.verify import verifyObject >>> from zope.schema.interfaces import ( ... ITitledTokenizedTerm, IVocabularyTokenized) >>> verifyObject(IVocabularyTokenized, Fruit) True The items themselves do not support any interface. Items returned by the methods for vocabularies return wrapped items that support the ITitledTokenizedTerm interface. The token used to identify terms in the vocabulary is the name of the Item variable. >>> item = Fruit.getTermByToken('APPLE') >>> type(item) >>> verifyObject(ITitledTokenizedTerm, item) True TokenizedItems have three attributes (in order to support ITitledTokenizedTerm): >>> item.value >>> item.token 'APPLE' >>> item.title 'Apple' >>> Fruit.getTermByToken('apple').value The length of an EnumeratedType returns the number of items it has. >>> print len(Fruit) 3 =========================== The EnumeratedType registry =========================== All enumerated types that are created are added to the enumerated_type_registry. >>> from lazr.enum import enumerated_type_registry The enumerated_type_registry maps the name of the enumerated type to the type itself. >>> 'Fruit' in enumerated_type_registry True >>> enumerated_type_registry['Fruit'] You cannot redefine an existing enumerated type, nor create another enumerated type with the same name as an existing type. >>> class BranchType(EnumeratedType): ... BAR = Item('Bar') ... >>> BranchType.name = 'AltBranchType' >>> class BranchType(EnumeratedType): ... FOO = Item('Foo') Traceback (most recent call last): ... TypeError: An enumerated type already exists with the name BranchType (...AltBranchType). ====================== Enumerated Type basics ====================== An EnumeratedType has a name and a description. The name is the same as the class name, and the description is the docstring for the class. >>> print Fruit.name Fruit >>> print Fruit.description A choice of fruit. If you do not specify an explicit sort_order for the items of the EnumeratedType one is created for you. This is tuple of the tokens. >>> print Fruit.sort_order ('APPLE', 'PEAR', 'ORANGE') The items of an enumerated type can be iterated over. However the type that is returned by the iteration is the TokenizedItem, not the item itself. >>> for item in Fruit: ... print item.token, item.title APPLE Apple PEAR Pear ORANGE Orange Items can also optionally have a url associated with them. >>> class Guitar(EnumeratedType): ... FENDER = Item('Fender', url='http://www.fender.com') ... RICK = Item('Rickenbacker', url='http://www.rickenbacker.com') ... GIBSON = Item('Gibson', url='http://www.gibson.com') ... FRANKENBASS = Item('Home built') >>> print Guitar.FENDER.url http://www.fender.com >>> print Guitar.FRANKENBASS.url None Items in an enumerator support comparison and equality checks. Comparison is based on the sort order of the items. >>> apple = Fruit.APPLE >>> pear = Fruit.PEAR >>> orange = Fruit.ORANGE >>> apple < pear True >>> apple == pear False >>> apple == apple True >>> apple != pear True >>> apple > pear False >>> pear < orange True >>> apple < orange True Items which are not in an enumerator always compare as False. >>> from lazr.enum import Item >>> Item('a') == Item('b') False The string representation of an Item is the title, and the representation also shows the enumeration that the Item is from. >>> print apple Apple >>> print repr(apple) The `items` attribute of an enumerated type is not a list, but a class that provides iteration over the items, and access to the Item attributes through either the name of the Item, or the database value if there is one. The primary use of this is to provide a backwards compatible accessor for items, but it also provides a suitable alternative to getattr. >>> name = 'APPLE' >>> Fruit.items[name] >>> getattr(Fruit, name) ========================= Database Enumerated Types ========================= A very common use of enumerated types are to give semantic meaning to integer values stored in database columns. EnumeratedType Items themselves don't have any integer values. The DBEnumeratedType provides the semantic framework for a type that is used to map integer values to python enumerated values. >>> # Remove the existing reference to BranchType from the registry >>> del enumerated_type_registry['BranchType'] >>> class BranchType(DBEnumeratedType): ... HOSTED = DBItem(1, """ ... Hosted ... ... Hosted braches use the supermirror as the main repository ... for the branch.""") ... ... MIRRORED = DBItem(2, """ ... Mirrored ... ... Mirrored branches are "pulled" from a remote location.""") ... ... IMPORTED = DBItem(3, """ ... Imported ... ... Imported branches are natively maintained in CVS or SVN""") Note carefully that the value of a DBItem is the integer representation. But the value of the TokenizedItem is the DBItem itself. >>> hosted = BranchType.HOSTED >>> hosted.value 1 >>> hosted == BranchType.HOSTED True >>> tokenized_item = BranchType.getTermByToken('HOSTED') >>> tokenized_item.value DBEnumeratedTypes also support IVocabularyTokenized >>> verifyObject(IVocabularyTokenized, BranchType) True The items attribute of DBEnumeratedTypes provide a mapping from the database values to the DBItems. >>> BranchType.items[3] The items also support the url field. >>> class Bass(DBEnumeratedType): ... FENDER = DBItem(10, 'Fender', url='http://www.fender.com') ... RICK = DBItem(20, 'Rickenbacker', ... url='http://www.rickenbacker.com') ... GIBSON = DBItem(30, 'Gibson', url='http://www.gibson.com') ... FRANKENBASS = DBItem(40, 'Home built') >>> print Bass.FENDER.url http://www.fender.com >>> print Bass.FRANKENBASS.url None Items in a DBEnumeratedType class must be of type DBItem. >>> class BadItemType(DBEnumeratedType): ... TESTING = Item("Testing") Traceback (most recent call last): ... TypeError: Items must be of the appropriate type for the DBEnumeratedType, __builtin__.BadItemType.TESTING You are not able to define a DBEnumeratedType that has two different DBItems that map to the same numeric value. >>> class TwoMapping(DBEnumeratedType): ... FIRST = DBItem(42, 'First') ... SECOND = DBItem(42, 'Second') Traceback (most recent call last): ... TypeError: Two DBItems with the same value 42 (FIRST, SECOND) ========================= Overriding the sort order ========================= By default the sort order of the items in an enumerated type is defined by the order in which the Items are declared. This my be overridden by specifying a sort_order attribute in the class. If a sort_order is specified, it has to specify every item in the enumeration. >>> class AnimalClassification(EnumeratedType): ... sort_order = "REPTILE", "INSECT", "MAMMAL" ... INSECT = Item("Insect") ... MAMMAL = Item("Mammal") ... FISH = Item("Fish") ... REPTILE = Item("Reptile") Traceback (most recent call last): ... TypeError: sort_order for EnumeratedType must contain all and only Item instances ... The sort_order may also appear anywhere in the definition of the class, although convention has it that it appears first, before the Item instances. >>> class AnimalClassification(EnumeratedType): ... sort_order = "REPTILE", "FISH", "INSECT", "MAMMAL" ... INSECT = Item("Insect") ... MAMMAL = Item("Mammal") ... FISH = Item("Fish") ... REPTILE = Item("Reptile") The items attribute of the enumerated type is ordered based on the sort_order. The items attribute is also used to control iteration using __iter__. >>> for item in AnimalClassification.items: ... print item.title Reptile Fish Insect Mammal The sort order also drives the comparison operations. >>> reptile, fish, insect, mammal = AnimalClassification.items >>> reptile < fish < insect < mammal True ========================== Extending enumerated types ========================== The simplest way to extend a class is to derive from it. >>> class AnimalClassificationExtended(AnimalClassification): ... INVERTEBRATE = Item("Invertebrate") >>> for item in AnimalClassificationExtended: ... print item.title Reptile Fish Insect Mammal Invertebrate The use_template function inserts the items from the specified enumerated type into the new enumerated type. The default case is to take all the enumerated items. >>> class UIBranchType(EnumeratedType): ... use_template(BranchType) >>> for item in UIBranchType: ... print item.title Hosted Mirrored Imported You can also specify items to be excluded by referring to the attribute name in the exclude parameter. This can be either a string referring to one name or a tuple or list that refers to multiple attribute names. >>> class UIBranchType2(EnumeratedType): ... use_template(BranchType, exclude='IMPORTED') >>> for item in UIBranchType2: ... print item.title Hosted Mirrored Or limit the items to those specified: >>> class UIBranchType3(EnumeratedType): ... use_template(BranchType, include=('HOSTED', 'MIRRORED')) >>> for item in UIBranchType3: ... print item.title Hosted Mirrored ================================================ Getting from an item back to the enumerated type ================================================ Each Item in an EnumeratedType has a reference back to the EnumeratedType. >>> print repr(apple) >>> print repr(apple.enum) >>> for item in apple.enum: ... print item.title Apple Pear Orange ============ Item.sortkey ============ The sortkey attribute of the Items are defined by the sort_order that is defined for the enumerated type. The value is often used as a hidden value in columns in order to ensure appropriate sorting. >>> for item in Fruit.items: ... print item.title, item.sortkey Apple 0 Pear 1 Orange 2 >>> for item in BranchType.items: ... print item.title, item.sortkey Hosted 0 Mirrored 1 Imported 2 ============ JSON Support ============ Enumerated types instances can be serialised to/from json. This library provides the necessary encode and decode classes which can be used directly or as part of the lazr.json package where they are registered as default handlers for lazr enums. A enum instance is serialised as a dict containing: - the enumerated type name as per the enumerated_type_registry - the enum instance name >>> import json >>> from lazr.enum import EnumJSONEncoder >>> encoded_enum = json.dumps(Fruit.APPLE, cls=EnumJSONEncoder) >>> print encoded_enum {"type": "Fruit", "name": "APPLE"} To deserialse, we can specify a json object_hook as follows. This is done transparently when using the lazr.json package. >>> def fruit_enum_decoder(value_dict): ... return EnumJSONDecoder.from_dict(Fruit, value_dict) >>> from lazr.enum import EnumJSONDecoder >>> json.loads(encoded_enum, object_hook=fruit_enum_decoder) ================== NEWS for lazr.enum ================== 1.1.4 (2012-04-18) ================== - Support for serialising enums to/from json (lp:984549) - Items which are not in an enumerator always compare as False (lp:524259) - Fix the licence statement in _enum.py to be LGPLv3 not LGPLv3+ (lp:526484) 1.1.3 (2011-04-20) ================== - added case insensitivity to getting the term by the token value (lp:154556) 1.1.2 (2009-08-31) ================== - removed unnecessary build dependencies 1.1.1 (2009-08-06) ================== - Removed sys.path hack from setup.py. 1.1 (2009-06-08) ================ - Added `url` argument to the BaseItem and DBItem constructors. 1.0 (2009-03-24) ================ - Initial release on PyPI 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.enum-1.1.4/src/0000775000175000017500000000000011743442313013563 5ustar ianian00000000000000lazr.enum-1.1.4/src/lazr/0000775000175000017500000000000011743442313014533 5ustar ianian00000000000000lazr.enum-1.1.4/src/lazr/__init__.py0000664000175000017500000000157311743424744016662 0ustar ianian00000000000000# Copyright 2009 Canonical Ltd. All rights reserved. # # This file is part of lazr.enum. # # lazr.enum 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.enum 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.enum. 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.enum-1.1.4/src/lazr/enum/0000775000175000017500000000000011743442313015477 5ustar ianian00000000000000lazr.enum-1.1.4/src/lazr/enum/_enum.py0000664000175000017500000004365311743441413017167 0ustar ianian00000000000000# Copyright 2004-2009 Canonical Ltd. All rights reserved. # # This file is part of lazr.enum # # lazr.enum 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.enum 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.enum. If not, see . __metaclass__ = type __all__ = [ 'BaseItem', 'DBEnumeratedType', 'DBItem', 'EnumeratedType', 'IEnumeratedType', 'Item', 'TokenizedItem', 'enumerated_type_registry', 'use_template', 'proxy_isinstance', 'MetaEnum', # needed for configure.zcml 'MetaDBEnum', # needed for configure.zcml ] import itertools import operator import sys import warnings from zope.interface import implements from zope.schema.interfaces import ITitledTokenizedTerm, IVocabularyTokenized try: from zope.proxy import removeAllProxies except ImportError: removeAllProxies = lambda obj: obj # no-op from lazr.enum.interfaces import IEnumeratedType def proxy_isinstance(obj, cls): """Test whether an object is an instance of a type. This works even if the object is proxied by zope.proxy, if that package is available. """ return isinstance(removeAllProxies(obj), cls) def docstring_to_title_descr(string): """When given a classically formatted docstring, returns a tuple (title, description). >>> class Foo: ... ''' ... Title of foo ... ... Description of foo starts here. It may ... spill onto multiple lines. It may also have ... indented examples: ... ... Foo ... Bar ... ... like the above. ... ''' ... >>> title, descr = docstring_to_title_descr(Foo.__doc__) >>> print title Title of foo >>> for num, line in enumerate(descr.splitlines()): ... print "%d.%s" % (num, line) ... 0.Description of foo starts here. It may 1.spill onto multiple lines. It may also have 2.indented examples: 3. 4. Foo 5. Bar 6. 7.like the above. """ lines = string.splitlines() # title is the first non-blank line for num, line in enumerate(lines): line = line.strip() if line: title = line break else: raise ValueError assert not lines[num + 1].strip() descrlines = lines[num + 2:] descr1 = descrlines[0] indent = len(descr1) - len(descr1.lstrip()) descr = '\n'.join([line[indent:] for line in descrlines]) return title, descr class BaseItem: """Items are the primary elements of the enumerated types. `BaseItem` is the base class for both `Item` and `DBItem`. The enum attribute is a reference to the enumerated type that the Item is a member of. The token attribute is the name assigned to the item. The value is the short text string used to identify the item. """ sortkey = 0 name = None description = None title = None url = None def __init__(self, title, description=None, url=None): """Items are the main elements of the EnumeratedType. Where the title is passed in without a description, and the title looks like a docstring (has embedded carriage returns), the title is the first line, and the description is the rest. """ self.sortkey = BaseItem.sortkey BaseItem.sortkey += 1 self.title = title # The enum attribute is set duing the class constructor of the # containing enumerated type. self.description = description self.url = url if self.description is None: # check value if self.title.find('\n') != -1: self.title, self.description = docstring_to_title_descr( self.title) def __int__(self): raise TypeError("Cannot cast Item to int.") def __cmp__(self, other): if proxy_isinstance(other, BaseItem): return cmp(self.sortkey, other.sortkey) else: raise TypeError( 'Comparisons of Items are only valid with other Items') def __eq__(self, other, stacklevel=2): if isinstance(other, int): warnings.warn('comparison of Item to an int: %r' % self, stacklevel=stacklevel) return False elif proxy_isinstance(other, BaseItem): if (not getattr(self, 'enum', None) or not getattr(other, 'enum', None)): warnings.warn('comparison of Items which are not ' 'enumerated types', stacklevel=stacklevel) return False return (self.name == other.name and self.enum == other.enum) else: return False def __ne__(self, other): return not self.__eq__(other, stacklevel=3) def __hash__(self): return hash(self.title) def __str__(self): return str(self.title) class Item(BaseItem): """The `Item` is an element of an `EnumeratedType`.""" @staticmethod def construct(other_item): """Create an Item based on the other_item.""" item = Item(other_item.title, other_item.description, other_item.url) item.sortkey = other_item.sortkey return item def __repr__(self): return "" % ( self.enum.name, self.name, self.title) class DBItem(BaseItem): """The `DBItem` refers to an enumerated item that is used in the database. Database enumerations are stored in the database using integer columns. """ @staticmethod def construct(other_item): """Create an Item based on the other_item.""" item = DBItem(other_item.value, other_item.title, other_item.description, other_item.url) item.sortkey = other_item.sortkey return item def __init__(self, value, title, description=None, url=None): BaseItem.__init__(self, title, description, url) self.value = value def __hash__(self): return self.value def __repr__(self): return "" % ( self.enum.name, self.name, self.value, self.title) # XXX this is probably going away. def __sqlrepr__(self, dbname): return repr(self.value) class TokenizedItem: """Wraps an `Item` or `DBItem` to provide `ITitledTokenizedTerm`.""" implements(ITitledTokenizedTerm) def __init__(self, item): self.value = item self.token = item.name self.title = item.title # The enumerated_type_registry is a mapping of all enumerated types to the # actual class. There should only ever be one EnumeratedType or # DBEnumerateType with a particular name. This serves two purposes: # * a way to get any enumerated type by its name # * a way to iterate over the DBEnumeratedTypes in order to confirm the # values actually stored in the database. enumerated_type_registry = {} class EnumItems: """Allow access to Items of an enumerated type using names or db values. Access can be made to the items using the name of the Item. If the enumerated type has DBItems then the mapping includes a mapping of the database integer values to the DBItems. """ def __init__(self, items, mapping): self.items = items self.mapping = mapping def __getitem__(self, key): if key in self.mapping: return self.mapping[key] else: raise KeyError(key) def __iter__(self): return self.items.__iter__() def __len__(self): return len(self.items) class BaseMetaEnum(type): """The metaclass functionality for `EnumeratedType` and `DBEnumeratedType`. This metaclass defines methods that allow the enumerated types to implement the IVocabularyTokenized interface. The metaclass also enforces "correct" definitions of enumerated types by enforcing capitalisation of Item variable names and defining an appropriate ordering. """ implements(IEnumeratedType, IVocabularyTokenized) @classmethod def _enforceSingleInheritance(cls, classname, bases, classdict): """Only one base class is allowed for enumerated types.""" if len(bases) > 1: raise TypeError( 'Multiple inheritance is not allowed with ' '%s, %s.%s' % ( cls.enum_name, classdict['__module__'], classname)) @classmethod def _updateClassDictWithBaseItems(cls, bases, classdict): """Copy each of the items from the base class that hasn't been explicitly defined in the new class.""" if bases: base_class = bases[0] if hasattr(base_class, 'items'): for item in base_class.items: if item.name not in classdict: new_item = cls.item_type.construct(item) classdict[item.name] = new_item @classmethod def _updateClassDictWithTemplateItems(cls, classdict): """If constructed through use_template, we need to construct the appropriate type of items based on our item_type of our class.""" if 'template_items' in classdict: for item in classdict['template_items']: classdict[item.name] = cls.item_type.construct(item) # The template_items key is not wanted or needed in the new type. del classdict['template_items'] @classmethod def _enforceItemClassAndName(cls, items, classname, module_name): """All items must be of the appropriate type for the enumeration type. All item variable names must be capitalised. """ for item_name, item in items: if not isinstance(item, cls.item_type): raise TypeError( 'Items must be of the appropriate type for the ' '%s, %s.%s.%s' % ( cls.enum_name, module_name, classname, item_name)) if item_name.upper() != item_name: raise TypeError( 'Item instance variable names must be capitalised.' ' %s.%s.%s' % (module_name, classname, item_name)) item.name = item_name @classmethod def _generateItemMapping(cls, items): """Each enumerated type has a mapping of the item names to the item instances.""" return dict(items) @classmethod def _enforceSortOrder(cls, classname, classdict, items): """ Override item's default sort order if sort_order is defined. :return: A list of items ordered appropriately. """ items = dict(items) if 'sort_order' in classdict: sort_order = classdict['sort_order'] item_names = sorted(items.keys()) if item_names != sorted(sort_order): raise TypeError( 'sort_order for %s must contain all and ' 'only Item instances %s.%s' % ( cls.enum_name, classdict['__module__'], classname)) else: # Sort the items by the automatically generated # sortkey. sort_order = [ item.name for item in sorted( items.values(), key=operator.attrgetter('sortkey'))] classdict['sort_order'] = tuple(sort_order) # Assign new sortkey values from zero. sorted_items = [] for sort_id, item_name in enumerate(sort_order): item = classdict[item_name] item.sortkey = sort_id sorted_items.append(item) return sorted_items def __new__(cls, classname, bases, classdict): """Called when defining a new class.""" cls._enforceSingleInheritance(classname, bases, classdict) cls._updateClassDictWithBaseItems(bases, classdict) cls._updateClassDictWithTemplateItems(classdict) items = [(key, value) for key, value in classdict.iteritems() if isinstance(value, BaseItem)] cls._enforceItemClassAndName(items, classname, classdict['__module__']) mapping = cls._generateItemMapping(items) sorted_items = cls._enforceSortOrder(classname, classdict, items) classdict['items'] = EnumItems(sorted_items, mapping) classdict['name'] = classname classdict['description'] = classdict.get('__doc__', None) global enumerated_type_registry if classname in enumerated_type_registry: other = enumerated_type_registry[classname] raise TypeError( 'An enumerated type already exists with the name %s (%s.%s).' % (classname, other.__module__, other.name)) instance = type.__new__(cls, classname, bases, classdict) # Add a reference to the enumerated type to each item. for item in instance.items: item.enum = instance # Add the enumerated type to the registry. enumerated_type_registry[classname] = instance return instance def __contains__(self, value): """See `ISource`.""" return value in self.items def __iter__(self): """See `IIterableVocabulary`.""" return itertools.imap(TokenizedItem, self.items) def __len__(self): """See `IIterableVocabulary`.""" return len(self.items) def getTerm(self, value): """See `IBaseVocabulary`.""" if value in self.items: return TokenizedItem(value) raise LookupError(value) def getTermByToken(self, token): """See `IVocabularyTokenized`.""" # The sort_order of the enumerated type lists all the items. upper_token = token.upper() if upper_token in self.sort_order: return TokenizedItem(getattr(self, upper_token)) else: # If the token is not specified in the sort order then check # the titles of the items. This is to support the transition # of accessing items by their titles. To continue support # of old URLs et al, this will probably stay for some time. for item in self.items: if item.title == token: return TokenizedItem(item) # The token was not in the sort_order (and hence the name of a # variable), nor was the token the title of one of the items. raise LookupError(token) class MetaEnum(BaseMetaEnum): """The metaclass for `EnumeratedType`.""" item_type = Item enum_name = 'EnumeratedType' def __repr__(self): return "" % self.name class MetaDBEnum(BaseMetaEnum): """The meta class for `DBEnumeratedType`. Provides a method for getting the item based on the database identifier in addition to all the normal enumerated type methods. """ item_type = DBItem enum_name = 'DBEnumeratedType' @classmethod def _generateItemMapping(cls, items): """DBEnumeratedTypes also map the database value of the DBItem to the item instance.""" mapping = BaseMetaEnum._generateItemMapping(items) for item_name, item in items: # If the value is already in the mapping then we have two # different items attempting to map the same number. if item.value in mapping: # We really want to provide the names in alphabetical order. args = [item.value] + sorted( [item_name, mapping[item.value].name]) raise TypeError( 'Two DBItems with the same value %s (%s, %s)' % tuple(args)) else: mapping[item.value] = item return mapping def __repr__(self): return "" % self.name class EnumeratedType: """An enumeration of items. The items of the enumeration must be instances of the class `Item`. These items are accessible through a class attribute `items`. The ordering of the items attribute is the same order that the items are defined in the class. A `sort_order` attribute can be defined to override the default ordering. The sort_order should contain the names of the all the items in the ordering that is desired. """ __metaclass__ = MetaEnum class DBEnumeratedType: """An enumeration with additional mapping from an integer to `Item`. The items of a class inheriting from DBEnumeratedType must be of type `DBItem`. """ __metaclass__ = MetaDBEnum def use_template(enum_type, include=None, exclude=None): """An alternative way to extend an enumerated type other than inheritance. The parameters include and exclude should either be the name values of the items (the parameter names), or a list or tuple that contains string values. """ 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( "use_template can be used only from a class definition.") # You can specify either includes or excludes, not both. if include and exclude: raise ValueError("You can specify includes or excludes not both.") if include is None: items = enum_type.items else: if isinstance(include, str): include = [include] items = [item for item in enum_type.items if item.name in include] if exclude is None: exclude = [] elif isinstance(exclude, str): exclude = [exclude] template_items = [] for item in items: if item.name not in exclude: template_items.append(item) locals['template_items'] = template_items lazr.enum-1.1.4/src/lazr/enum/__init__.py0000664000175000017500000000241211743440712017610 0ustar ianian00000000000000# Copyright 2009 Canonical Ltd. All rights reserved. # # This file is part of lazr.enum # # lazr.enum 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.enum 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.enum. If not, see . """Enumerations.""" import pkg_resources __version__ = pkg_resources.resource_string( "lazr.enum", "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.enum._enum import * from lazr.enum._enum import __all__ as _all from lazr.enum._json import * from lazr.enum._json import __all__ as _jall from lazr.enum.interfaces import * from lazr.enum.interfaces import __all__ as _iall __all__ = [] __all__.extend(_all) __all__.extend(_iall) __all__.extend(_jall) lazr.enum-1.1.4/src/lazr/enum/_json.py0000664000175000017500000000314411743442244017166 0ustar ianian00000000000000# Copyright 2012 Canonical Ltd. All rights reserved. # # This file is part of lazr.enum # # lazr.enum 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, either version 3 of the License, or (at your # option) any later version. # # lazr.enum 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.enum. If not, see . __metaclass__ = type __all__ = [ 'EnumJSONDecoder', 'EnumJSONEncoder', ] import json from lazr.enum import ( BaseItem, enumerated_type_registry, ) class EnumJSONEncoder(json.JSONEncoder): """A JSON encoder that understands enum objects. Objects are serialized using their type and enum name. """ def default(self, obj): if isinstance(obj, BaseItem): return { 'type': obj.enum.name, 'name': obj.name} return json.JSONEncoder.default(self, obj) class EnumJSONDecoder(): """A decoder can reconstruct enums from a JSON dict. Objects are reconstructed using their type and enum name. """ @classmethod def from_dict(cls, class_, values): type_name = values['type'] item_name = values['name'] return enumerated_type_registry[type_name].items[item_name] lazr.enum-1.1.4/src/lazr/enum/configure.zcml0000664000175000017500000000146011743424744020360 0ustar ianian00000000000000 lazr.enum-1.1.4/src/lazr/enum/NEWS.txt0000664000175000017500000000135311743441123017014 0ustar ianian00000000000000================== NEWS for lazr.enum ================== 1.1.4 (2012-04-18) ================== - Support for serialising enums to/from json (lp:984549) - Items which are not in an enumerator always compare as False (lp:524259) - Fix the licence statement in _enum.py to be LGPLv3 not LGPLv3+ (lp:526484) 1.1.3 (2011-04-20) ================== - added case insensitivity to getting the term by the token value (lp:154556) 1.1.2 (2009-08-31) ================== - removed unnecessary build dependencies 1.1.1 (2009-08-06) ================== - Removed sys.path hack from setup.py. 1.1 (2009-06-08) ================ - Added `url` argument to the BaseItem and DBItem constructors. 1.0 (2009-03-24) ================ - Initial release on PyPI lazr.enum-1.1.4/src/lazr/enum/interfaces.py0000664000175000017500000000257111743424744020211 0ustar ianian00000000000000# Copyright 2004-2009 Canonical Ltd. All rights reserved. # # This file is part of lazr.enum # # lazr.enum 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.enum 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.enum. If not, see . __all__ = [ 'IEnumeratedType', ] from zope.interface import Attribute, Interface class IEnumeratedType(Interface): """Defines the attributes that EnumeratedTypes have.""" name = Attribute( "The name of the EnumeratedType is the same as the name of the class.") description = Attribute( "The description is the docstring of the EnumeratedType class.") sort_order = Attribute( "A tuple of Item names that is used to determine the ordering of the " "Items.") items = Attribute( "An instance of `EnumItems` which allows access to the enumerated " "types items by either name of database value if the items are " "DBItems.") lazr.enum-1.1.4/src/lazr/enum/version.txt0000664000175000017500000000000611743427356017733 0ustar ianian000000000000001.1.4 lazr.enum-1.1.4/src/lazr/enum/tests/0000775000175000017500000000000011743442313016641 5ustar ianian00000000000000lazr.enum-1.1.4/src/lazr/enum/tests/__init__.py0000664000175000017500000000130711743424744020763 0ustar ianian00000000000000# Copyright 2009 Canonical Ltd. All rights reserved. # # This file is part of lazr.enum # # lazr.enum 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.enum 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.enum. If not, see . "The lazr.enum tests." lazr.enum-1.1.4/src/lazr/enum/tests/test_proxy.py0000664000175000017500000000337511743424744021453 0ustar ianian00000000000000# Copyright 2009 Canonical Ltd. All rights reserved. # # This file is part of lazr.enum # # lazr.enum 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.enum 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.enum. If not, see . "Tests that depend on zope.security." __all__ = [ 'test_suite', ] import doctest import unittest def test_proxy_isinstance(): """ Proxies such as the zope.security proxy can mask whether an object is an instance of a class. ``proxy_isinstance`` allows you to ask whether a proxied object is an instance of another class. >>> from lazr.enum import proxy_isinstance >>> class C1(object): ... pass >>> c = C1() >>> proxy_isinstance(c, C1) True >>> from zope.security.checker import ProxyFactory >>> isinstance(ProxyFactory(c), C1) False >>> proxy_isinstance(ProxyFactory(c), C1) True >>> class C2(C1): ... pass >>> c = C2() >>> proxy_isinstance(ProxyFactory(c), C1) True """ # this differs from the usual name we use for tests, which makes it possible # for us to only run these tests with the buildout's ./bin/test_proxy command, # since the tests depend on zope.security, while the package itself does not. def test_suite(): return unittest.TestSuite(( doctest.DocTestSuite())) lazr.enum-1.1.4/src/lazr/enum/tests/test_docs.py0000664000175000017500000000331711743424744021216 0ustar ianian00000000000000# Copyright 2009 Canonical Ltd. All rights reserved. # # This file is part of lazr.enum # # lazr.enum 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.enum 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.enum. If not, see . "Test harness for doctests." __metaclass__ = type __all__ = [ 'additional_tests', ] import atexit import doctest import os import pkg_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( pkg_resources.resource_filename('lazr.enum', 'README.txt'))] if pkg_resources.resource_exists('lazr.enum', 'docs'): for name in pkg_resources.resource_listdir('lazr.enum', 'docs'): if name.endswith('.txt'): doctest_files.append( os.path.abspath( pkg_resources.resource_filename( 'lazr.enum', 'docs/%s' % name))) kwargs = dict(module_relative=False, optionflags=DOCTEST_FLAGS) atexit.register(pkg_resources.cleanup_resources) return unittest.TestSuite(( doctest.DocFileSuite(*doctest_files, **kwargs))) lazr.enum-1.1.4/src/lazr/enum/README.txt0000664000175000017500000003255411743442012017202 0ustar ianian00000000000000.. This file is part of lazr.enum. lazr.enum 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.enum 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.enum. If not, see . Enumerated Types **************** Enumerated types are used primarily in two distinct places in the Launchpad code: selector types; and database types. Simple enumerated types do not have values, whereas database enumerated types are a mapping from an integer value to something meaningful in the code. >>> from lazr.enum import ( ... EnumeratedType, DBEnumeratedType, Item, DBItem, use_template) The `enum` values of EnumeratedTypes are instances of Item. >>> class Fruit(EnumeratedType): ... "A choice of fruit." ... APPLE = Item('Apple') ... PEAR = Item('Pear') ... ORANGE = Item('Orange') =================== IVocabulary support =================== Enumerated types support IVocabularyTokenized. >>> from zope.interface.verify import verifyObject >>> from zope.schema.interfaces import ( ... ITitledTokenizedTerm, IVocabularyTokenized) >>> verifyObject(IVocabularyTokenized, Fruit) True The items themselves do not support any interface. Items returned by the methods for vocabularies return wrapped items that support the ITitledTokenizedTerm interface. The token used to identify terms in the vocabulary is the name of the Item variable. >>> item = Fruit.getTermByToken('APPLE') >>> type(item) >>> verifyObject(ITitledTokenizedTerm, item) True TokenizedItems have three attributes (in order to support ITitledTokenizedTerm): >>> item.value >>> item.token 'APPLE' >>> item.title 'Apple' >>> Fruit.getTermByToken('apple').value The length of an EnumeratedType returns the number of items it has. >>> print len(Fruit) 3 =========================== The EnumeratedType registry =========================== All enumerated types that are created are added to the enumerated_type_registry. >>> from lazr.enum import enumerated_type_registry The enumerated_type_registry maps the name of the enumerated type to the type itself. >>> 'Fruit' in enumerated_type_registry True >>> enumerated_type_registry['Fruit'] You cannot redefine an existing enumerated type, nor create another enumerated type with the same name as an existing type. >>> class BranchType(EnumeratedType): ... BAR = Item('Bar') ... >>> BranchType.name = 'AltBranchType' >>> class BranchType(EnumeratedType): ... FOO = Item('Foo') Traceback (most recent call last): ... TypeError: An enumerated type already exists with the name BranchType (...AltBranchType). ====================== Enumerated Type basics ====================== An EnumeratedType has a name and a description. The name is the same as the class name, and the description is the docstring for the class. >>> print Fruit.name Fruit >>> print Fruit.description A choice of fruit. If you do not specify an explicit sort_order for the items of the EnumeratedType one is created for you. This is tuple of the tokens. >>> print Fruit.sort_order ('APPLE', 'PEAR', 'ORANGE') The items of an enumerated type can be iterated over. However the type that is returned by the iteration is the TokenizedItem, not the item itself. >>> for item in Fruit: ... print item.token, item.title APPLE Apple PEAR Pear ORANGE Orange Items can also optionally have a url associated with them. >>> class Guitar(EnumeratedType): ... FENDER = Item('Fender', url='http://www.fender.com') ... RICK = Item('Rickenbacker', url='http://www.rickenbacker.com') ... GIBSON = Item('Gibson', url='http://www.gibson.com') ... FRANKENBASS = Item('Home built') >>> print Guitar.FENDER.url http://www.fender.com >>> print Guitar.FRANKENBASS.url None Items in an enumerator support comparison and equality checks. Comparison is based on the sort order of the items. >>> apple = Fruit.APPLE >>> pear = Fruit.PEAR >>> orange = Fruit.ORANGE >>> apple < pear True >>> apple == pear False >>> apple == apple True >>> apple != pear True >>> apple > pear False >>> pear < orange True >>> apple < orange True Items which are not in an enumerator always compare as False. >>> from lazr.enum import Item >>> Item('a') == Item('b') False The string representation of an Item is the title, and the representation also shows the enumeration that the Item is from. >>> print apple Apple >>> print repr(apple) The `items` attribute of an enumerated type is not a list, but a class that provides iteration over the items, and access to the Item attributes through either the name of the Item, or the database value if there is one. The primary use of this is to provide a backwards compatible accessor for items, but it also provides a suitable alternative to getattr. >>> name = 'APPLE' >>> Fruit.items[name] >>> getattr(Fruit, name) ========================= Database Enumerated Types ========================= A very common use of enumerated types are to give semantic meaning to integer values stored in database columns. EnumeratedType Items themselves don't have any integer values. The DBEnumeratedType provides the semantic framework for a type that is used to map integer values to python enumerated values. >>> # Remove the existing reference to BranchType from the registry >>> del enumerated_type_registry['BranchType'] >>> class BranchType(DBEnumeratedType): ... HOSTED = DBItem(1, """ ... Hosted ... ... Hosted braches use the supermirror as the main repository ... for the branch.""") ... ... MIRRORED = DBItem(2, """ ... Mirrored ... ... Mirrored branches are "pulled" from a remote location.""") ... ... IMPORTED = DBItem(3, """ ... Imported ... ... Imported branches are natively maintained in CVS or SVN""") Note carefully that the value of a DBItem is the integer representation. But the value of the TokenizedItem is the DBItem itself. >>> hosted = BranchType.HOSTED >>> hosted.value 1 >>> hosted == BranchType.HOSTED True >>> tokenized_item = BranchType.getTermByToken('HOSTED') >>> tokenized_item.value DBEnumeratedTypes also support IVocabularyTokenized >>> verifyObject(IVocabularyTokenized, BranchType) True The items attribute of DBEnumeratedTypes provide a mapping from the database values to the DBItems. >>> BranchType.items[3] The items also support the url field. >>> class Bass(DBEnumeratedType): ... FENDER = DBItem(10, 'Fender', url='http://www.fender.com') ... RICK = DBItem(20, 'Rickenbacker', ... url='http://www.rickenbacker.com') ... GIBSON = DBItem(30, 'Gibson', url='http://www.gibson.com') ... FRANKENBASS = DBItem(40, 'Home built') >>> print Bass.FENDER.url http://www.fender.com >>> print Bass.FRANKENBASS.url None Items in a DBEnumeratedType class must be of type DBItem. >>> class BadItemType(DBEnumeratedType): ... TESTING = Item("Testing") Traceback (most recent call last): ... TypeError: Items must be of the appropriate type for the DBEnumeratedType, __builtin__.BadItemType.TESTING You are not able to define a DBEnumeratedType that has two different DBItems that map to the same numeric value. >>> class TwoMapping(DBEnumeratedType): ... FIRST = DBItem(42, 'First') ... SECOND = DBItem(42, 'Second') Traceback (most recent call last): ... TypeError: Two DBItems with the same value 42 (FIRST, SECOND) ========================= Overriding the sort order ========================= By default the sort order of the items in an enumerated type is defined by the order in which the Items are declared. This my be overridden by specifying a sort_order attribute in the class. If a sort_order is specified, it has to specify every item in the enumeration. >>> class AnimalClassification(EnumeratedType): ... sort_order = "REPTILE", "INSECT", "MAMMAL" ... INSECT = Item("Insect") ... MAMMAL = Item("Mammal") ... FISH = Item("Fish") ... REPTILE = Item("Reptile") Traceback (most recent call last): ... TypeError: sort_order for EnumeratedType must contain all and only Item instances ... The sort_order may also appear anywhere in the definition of the class, although convention has it that it appears first, before the Item instances. >>> class AnimalClassification(EnumeratedType): ... sort_order = "REPTILE", "FISH", "INSECT", "MAMMAL" ... INSECT = Item("Insect") ... MAMMAL = Item("Mammal") ... FISH = Item("Fish") ... REPTILE = Item("Reptile") The items attribute of the enumerated type is ordered based on the sort_order. The items attribute is also used to control iteration using __iter__. >>> for item in AnimalClassification.items: ... print item.title Reptile Fish Insect Mammal The sort order also drives the comparison operations. >>> reptile, fish, insect, mammal = AnimalClassification.items >>> reptile < fish < insect < mammal True ========================== Extending enumerated types ========================== The simplest way to extend a class is to derive from it. >>> class AnimalClassificationExtended(AnimalClassification): ... INVERTEBRATE = Item("Invertebrate") >>> for item in AnimalClassificationExtended: ... print item.title Reptile Fish Insect Mammal Invertebrate The use_template function inserts the items from the specified enumerated type into the new enumerated type. The default case is to take all the enumerated items. >>> class UIBranchType(EnumeratedType): ... use_template(BranchType) >>> for item in UIBranchType: ... print item.title Hosted Mirrored Imported You can also specify items to be excluded by referring to the attribute name in the exclude parameter. This can be either a string referring to one name or a tuple or list that refers to multiple attribute names. >>> class UIBranchType2(EnumeratedType): ... use_template(BranchType, exclude='IMPORTED') >>> for item in UIBranchType2: ... print item.title Hosted Mirrored Or limit the items to those specified: >>> class UIBranchType3(EnumeratedType): ... use_template(BranchType, include=('HOSTED', 'MIRRORED')) >>> for item in UIBranchType3: ... print item.title Hosted Mirrored ================================================ Getting from an item back to the enumerated type ================================================ Each Item in an EnumeratedType has a reference back to the EnumeratedType. >>> print repr(apple) >>> print repr(apple.enum) >>> for item in apple.enum: ... print item.title Apple Pear Orange ============ Item.sortkey ============ The sortkey attribute of the Items are defined by the sort_order that is defined for the enumerated type. The value is often used as a hidden value in columns in order to ensure appropriate sorting. >>> for item in Fruit.items: ... print item.title, item.sortkey Apple 0 Pear 1 Orange 2 >>> for item in BranchType.items: ... print item.title, item.sortkey Hosted 0 Mirrored 1 Imported 2 ============ JSON Support ============ Enumerated types instances can be serialised to/from json. This library provides the necessary encode and decode classes which can be used directly or as part of the lazr.json package where they are registered as default handlers for lazr enums. A enum instance is serialised as a dict containing: - the enumerated type name as per the enumerated_type_registry - the enum instance name >>> import json >>> from lazr.enum import EnumJSONEncoder >>> encoded_enum = json.dumps(Fruit.APPLE, cls=EnumJSONEncoder) >>> print encoded_enum {"type": "Fruit", "name": "APPLE"} To deserialse, we can specify a json object_hook as follows. This is done transparently when using the lazr.json package. >>> def fruit_enum_decoder(value_dict): ... return EnumJSONDecoder.from_dict(Fruit, value_dict) >>> from lazr.enum import EnumJSONDecoder >>> json.loads(encoded_enum, object_hook=fruit_enum_decoder) .. pypi description ends here =============== Other Documents =============== .. toctree:: :glob: * docs/* lazr.enum-1.1.4/src/lazr.enum.egg-info/0000775000175000017500000000000011743442313017170 5ustar ianian00000000000000lazr.enum-1.1.4/src/lazr.enum.egg-info/SOURCES.txt0000664000175000017500000000115411743430167021061 0ustar ianian00000000000000README.txt ez_setup.py setup.py src/lazr/__init__.py src/lazr.enum.egg-info/PKG-INFO src/lazr.enum.egg-info/SOURCES.txt src/lazr.enum.egg-info/dependency_links.txt src/lazr.enum.egg-info/namespace_packages.txt src/lazr.enum.egg-info/not-zip-safe src/lazr.enum.egg-info/requires.txt src/lazr.enum.egg-info/top_level.txt src/lazr/enum/NEWS.txt src/lazr/enum/README.txt src/lazr/enum/__init__.py src/lazr/enum/_enum.py src/lazr/enum/_json.py src/lazr/enum/configure.zcml src/lazr/enum/interfaces.py src/lazr/enum/version.txt src/lazr/enum/tests/__init__.py src/lazr/enum/tests/test_docs.py src/lazr/enum/tests/test_proxy.pylazr.enum-1.1.4/src/lazr.enum.egg-info/top_level.txt0000664000175000017500000000000511743430167021721 0ustar ianian00000000000000lazr lazr.enum-1.1.4/src/lazr.enum.egg-info/dependency_links.txt0000664000175000017500000000000111743430167023242 0ustar ianian00000000000000 lazr.enum-1.1.4/src/lazr.enum.egg-info/requires.txt0000664000175000017500000000014011743430167021567 0ustar ianian00000000000000setuptools zope.interface zope.schema [docs] Sphinx z3c.recipe.sphinxdoc [proxy] zope.securitylazr.enum-1.1.4/src/lazr.enum.egg-info/namespace_packages.txt0000664000175000017500000000000511743430167023522 0ustar ianian00000000000000lazr lazr.enum-1.1.4/README.txt0000664000175000017500000000133511743424744014504 0ustar ianian00000000000000Enums with zope.schema vocabulary support and database-friendly conveniences. .. This file is part of lazr.enum. lazr.enum 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.enum 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.enum. If not, see .