[Chandler-dev] Rough cut of phase 1 EIM API & docs

Phillip J. Eby pje at telecommunity.com
Wed Oct 11 16:37:53 PDT 2006


FYI, I just finished a rough implementation of the phase 1 EIM API 
(primitive types, aliasing, and conversion).  It's not in SVN yet (so as 
not to interfere with a4 work), but here is the current doctest, warts and 
all.  (Most of the warts will be fixed when phase 2 kicks in, adding 
"Record" and "field" types that will produce better examples.)

Review and comments welcomed and requested.


----------------------
The Sharing Record API
----------------------

 >>> import eim as sharing   # API kludge during initial dev & testing


Defining and Using Field Types
==============================

Custom Types
------------

The most basic kinds of field types can be created using the ``BytesType``, 
``TextType``, ``IntType``, ``DateType``, and ``LobType`` constructors::

     >>> my_date = sharing.DateType()
     >>> my_date
     sharing.DateType(None)

Types can be given a unique URI, and they can be looked up by it::

     >>> my_text = sharing.TextType("cid:some_text_type at osaf.us", size=99)
     >>> my_text
     sharing.TextType('cid:some_text_type at osaf.us', 99)

     >>> sharing.typeinfo_for("cid:some_text_type at osaf.us")
     sharing.TextType('cid:some_text_type at osaf.us', 99)

     >>> my_text.uri
     'cid:some_text_type at osaf.us'

     >>> my_text.size
     99

But only one type can exist for a given URI at a given point in time::

     >>> another_text = sharing.TextType("cid:some_text_type at osaf.us", 99)
     Traceback (most recent call last):
       ...
     TypeError: A type already exists for 'cid:some_text_type at osaf.us'

     >>> del my_text     # no conflicting definition now
     >>> another_text = sharing.TextType("cid:some_text_type at osaf.us", 99)


Type Aliasing
-------------

The ``sharing.typedef()`` API lets you register type information for 
arbitrary Python objects, so that you can use existing types, kinds, etc. 
as field types.  For example::

     >>> class my_int(int):
     ...     """This is just a demonstration type"""

     >>> sharing.typedef(my_int, sharing.IntType('cid:my_int_ex at osaf.us'))

Now, ``my_int`` can be used directly as a field type in a 
``sharing.Record`` class, instead of using the ``IntType`` object::

     >>> sharing.typeinfo_for(my_int)    # XXX demo w/field instead of typeinfo
     sharing.IntType('cid:my_int_ex at osaf.us')

You can alias more than one object to the same type, or alias an object to 
an already-registered alias::

     >>> class my_int2(int):
     ...     """Another demonstration type"""

     >>> sharing.typedef(my_int2, my_int)
     >>> sharing.typeinfo_for(my_int2) is sharing.typeinfo_for(my_int)
     True

There are also built-in aliases for anonymous versions of the sizeless 
primitive types (``IntType``, ``LobType``, and ``DateType``), so you can 
use them directly in fields::

     >>> sharing.typeinfo_for(sharing.IntType)
     sharing.IntType(None)

     >>> sharing.typeinfo_for(sharing.LobType)
     sharing.LobType(None)

     >>> sharing.typeinfo_for(sharing.DateType)
     sharing.DateType(None)


Type Conversion
---------------

Because there are only five primitive EIM types (bytes, text, integer, 
date/time, and "lob"), it is usually necessary to convert some application- 
level data types to the corresponding primitive type.

For example, let's say that an application has a value that is normally 
represented as a hexidecimal string, but which for some reason it wants to 
transmit as an integer in its sharing records.  The application would need 
to define a string converter to turn the hex string into an integer.

So let's define a ``hexint`` type that we can use in field definitions 
where we want to be able to supply either integers or hex strings as input 
when creating a record.

     >>> hexint = sharing.IntType('cid:hexint_example at osaf.us')

By default, there is no converter registered to convert strings to 
integers, although there is one for converting integers to integers::

     >>> sharing.get_converter(hexint)(23)
     23

     >>> sharing.get_converter(hexint)("23")
     Traceback (most recent call last):
       ...
     TypeError: No converter registered for values of type <type 'str'>

The ``sharing.add_converter()`` API allows you to register a conversion 
function to be used for a particular field or field type::

     >>> sharing.add_converter(hexint, str, lambda v: int(v,16))
     >>> sharing.get_converter(hexint)("23")
     35


XXX this doc should probably use fields for examples, so as to hide 
the     use of get_converter(type)(value); normally this would only be 
called     by record constructors.

XXX Need more default encoders for primitive types; only int->IntType 
currently works


Creating Subtypes
-----------------

Sometimes, it's useful to create a field type by copying an existing 
type.  The ``sharing.subtype()`` function creates a new type from an 
existing one.  The new type will be of the same primitive type, and it will 
"inherit" any conversion functions defined for the base type::

     >>> hexint2 = sharing.subtype(hexint)
     >>> hexint2
     sharing.IntType(None)

     >>> sharing.get_converter(hexint2)("23")
     35

XXX example of argument pass-through from ``subtype()`` to typeinfo constructor
XXX test generation-skipping in converter registration


---------
Internals
---------

TypeInfo instances are immutable once created::

     >>> t = sharing.IntType()
     >>> t.uri = "fhdsfblah"
     Traceback (most recent call last):
       ...
     TypeError: sharing.IntType instances are immutable

Misc. constructor data validation tests::

     >>> sharing.TypeInfo()
     Traceback (most recent call last):
       ...
     TypeError: sharing.TypeInfo is an abstract type; use a subtype

     >>> sharing.SizedType()
     Traceback (most recent call last):
       ...
     TypeError: size must be specified when creating a sharing.SizedType

     >>> sharing.SizedType(size=53)
     Traceback (most recent call last):
       ...
     TypeError: sharing.SizedType is an abstract type; use a subtype

     >>> sharing.BytesType()
     Traceback (most recent call last):
       ...
     TypeError: size must be specified when creating a sharing.BytesType

     >>> sharing.TextType()
     Traceback (most recent call last):
       ...
     TypeError: size must be specified when creating a sharing.TextType

No such type::

     >>> sharing.typeinfo_for('xyz:abc')
     Traceback (most recent call last):
       ...
     UnknownType: 'xyz:abc'

XXX typeinfo_for(obj) -> TypeInfo
    field -> typeinfo_for(field.type)
    descr -> typeinfo_for(descr.type)



More information about the chandler-dev mailing list