lazr.enum-1.1.4/ 0000775 0001750 0001750 00000000000 11743442313 012774 5 ustar ian ian 0000000 0000000 lazr.enum-1.1.4/setup.py 0000775 0001750 0001750 00000004776 11743424744 014537 0 ustar ian ian 0000000 0000000 #!/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.py 0000664 0001750 0001750 00000022467 11743427510 015221 0 ustar ian ian 0000000 0000000 #!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-INFO 0000664 0001750 0001750 00000044544 11743442313 014104 0 ustar ian ian 0000000 0000000 Metadata-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/ 0000775 0001750 0001750 00000000000 11743442313 013563 5 ustar ian ian 0000000 0000000 lazr.enum-1.1.4/src/lazr/ 0000775 0001750 0001750 00000000000 11743442313 014533 5 ustar ian ian 0000000 0000000 lazr.enum-1.1.4/src/lazr/__init__.py 0000664 0001750 0001750 00000001573 11743424744 016662 0 ustar ian ian 0000000 0000000 # 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/ 0000775 0001750 0001750 00000000000 11743442313 015477 5 ustar ian ian 0000000 0000000 lazr.enum-1.1.4/src/lazr/enum/_enum.py 0000664 0001750 0001750 00000043653 11743441413 017167 0 ustar ian ian 0000000 0000000 # 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__.py 0000664 0001750 0001750 00000002412 11743440712 017610 0 ustar ian ian 0000000 0000000 # 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.py 0000664 0001750 0001750 00000003144 11743442244 017166 0 ustar ian ian 0000000 0000000 # 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.zcml 0000664 0001750 0001750 00000001460 11743424744 020360 0 ustar ian ian 0000000 0000000
lazr.enum-1.1.4/src/lazr/enum/NEWS.txt 0000664 0001750 0001750 00000001353 11743441123 017014 0 ustar ian ian 0000000 0000000 ==================
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.py 0000664 0001750 0001750 00000002571 11743424744 020211 0 ustar ian ian 0000000 0000000 # 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.txt 0000664 0001750 0001750 00000000006 11743427356 017733 0 ustar ian ian 0000000 0000000 1.1.4
lazr.enum-1.1.4/src/lazr/enum/tests/ 0000775 0001750 0001750 00000000000 11743442313 016641 5 ustar ian ian 0000000 0000000 lazr.enum-1.1.4/src/lazr/enum/tests/__init__.py 0000664 0001750 0001750 00000001307 11743424744 020763 0 ustar ian ian 0000000 0000000 # 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.py 0000664 0001750 0001750 00000003375 11743424744 021453 0 ustar ian ian 0000000 0000000 # 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.py 0000664 0001750 0001750 00000003317 11743424744 021216 0 ustar ian ian 0000000 0000000 # 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.txt 0000664 0001750 0001750 00000032554 11743442012 017202 0 ustar ian ian 0000000 0000000 ..
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/ 0000775 0001750 0001750 00000000000 11743442313 017170 5 ustar ian ian 0000000 0000000 lazr.enum-1.1.4/src/lazr.enum.egg-info/SOURCES.txt 0000664 0001750 0001750 00000001154 11743430167 021061 0 ustar ian ian 0000000 0000000 README.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.py lazr.enum-1.1.4/src/lazr.enum.egg-info/top_level.txt 0000664 0001750 0001750 00000000005 11743430167 021721 0 ustar ian ian 0000000 0000000 lazr
lazr.enum-1.1.4/src/lazr.enum.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 11743430167 023242 0 ustar ian ian 0000000 0000000
lazr.enum-1.1.4/src/lazr.enum.egg-info/requires.txt 0000664 0001750 0001750 00000000140 11743430167 021567 0 ustar ian ian 0000000 0000000 setuptools
zope.interface
zope.schema
[docs]
Sphinx
z3c.recipe.sphinxdoc
[proxy]
zope.security lazr.enum-1.1.4/src/lazr.enum.egg-info/namespace_packages.txt 0000664 0001750 0001750 00000000005 11743430167 023522 0 ustar ian ian 0000000 0000000 lazr
lazr.enum-1.1.4/README.txt 0000664 0001750 0001750 00000001335 11743424744 014504 0 ustar ian ian 0000000 0000000 Enums 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 .