[Python-Dev] PEP 246, Feedback Request

Clark C. Evans cce at clarkevans.com
Sun Jan 16 05:04:24 CET 2005


I started to edit the PEP, but found that we really don't have any
consensus on a great many items. The following is a bunch of topics,
and a proposed handling of those topics.  A bulk of this comes from
a phone chat I had with Alex this past afternoon, and other items
come from my understanding of the mailing list, or prior conversations
with Phillip, among others.  It's a strawman.

I'd really very much like feedback on each topic, preferably only
one post per person summarizing your position/suggestions.  I'd
rather not have a run-away discussion on this post.

---
-
  topic: a glossary
  overview:
    It seems that we are having difficulty with words that have shifting
    definitions.  The next PEP edit will need to add a glossary that 
    nails down some meanings of these words.  Following are a few 
    proposed terms/meanings.
  proposal:
  - protocol means any object, usually a type or class or interface,
    which guides the construction of an adapter
  - adaptee is the object which is to be adapted, the original object
  - adaptee-class refers to the adaptee's class
  - adapter refers to the result of adapting an adaptee to a protocol
  - factory refers to a function, f(adaptee) -> adapter, where 
    the resulting adapter complies with a given protocol
  feedback:
    Much help is needed here; either respond to this thread with
    your words and definitions, or email them directly to Clark 
    and he will use your feedback when creating the PEP's glossary.
-
  topic: a registry mechanism
  overview:
    It has become very clear from the conversations the past few
    days that a registry is absolutely needed for the kind of adapt()
    behavior people are currently using in Zope, Twisted, and Peak.
  proposal:
  - The PEP will define a simple and flexible registry mechanism.
  - The registry will be a mapping from a (adaptee-class, protocol) 
    pair to a corresponding factory.
  - Only one active registration per pair (see below)
  feedback:
    We welcome/encourage experiences and concreate suggestions from
    existing registries.  Our goal is to be minimal, extensible,
    and sufficient.  See other topics for more specific concerns
    before you comment on this more general topic.
-
  topic: should 'object' be impacted by PEP 246
  overview:
    The semantics of exceptions depend if 'object' is given a
    default __conform__ method (which would do isinstance), in which
    case, returning None in a subclass could be used to prevent
    Liskov violations. However, by requiring a change to 'object',
    it may hinder adoption or slow-down testing.
  proposal:
  - We will not ask/require changes to `object'.
  - Liskov violations will be managed via the registry, see below.
  - This is probably faster for isinstance cases?
  feedback:
    If you really think we should move isinstance() into
    object.__conform__, then here is your chance to make a final
    stand. ;)
-
  topic: adaption stages
  overview:
    There are several stages for adaptation.  It was recommended
    that the 'registry' be the first stop in the chain. 
  proposal:
  - First, the registry is checked for a suitable adapter
  - Second, isinstance() is checked, the adaptee is an instance
    of the protocol, adaptation ends and adaptee is returned.
  - Third, __conform__ on the adaptee is called with the given
    protocol being requested.
  - Fourth, __adapt__ on the protocol is called, with the given
    adaptee.
  feedback:
    This largely dependent upon the previous topic, but if something
    isn't obvious (mod exceptions below), please say something.
-
  topic: module vs built-in
  overview:
    Since we will be adding a registry, exceptions, and other items,
    it probably makes sense to use a module for 'adapt'.
  proposal:
  - PEP 246 will ask for a `adapt' module, with an `adapt' function.
  - The registry will be contained in this module, 'adapt.register'
  - The `adapt' module can provide commonly-used adapter factories,
    such as adapt.Identity.
  - With a standardized signature, frameworks can provide their own
    'local' registry/adapt overrides.
  feedback:
    Please discuss the merits of a module approach, and if having
    local registries is important (or worth the added complexity).
    Additional suggestions on how the module should be structured
    are welcome. 
-
  topic: exception handling
  overview:
    How should adaption stages progress and exceptions be handled.
    There were problems with swallowed TypeError exceptions in 
    the 2001 version of the PEP, type errors are not swallowed.
  proposal:
  - The 'adapt' module will define an adapt.AdaptError(TypeError).
  - At any stage of adaptation, if None is returned, the adaptation
    continues to the next stage.
  - Any exception other than adapt.AdaptException(TypeError)
    causes the adapt() call to fail, and the exception to be 
    raised to the caller of adapt(); the 'default' value is not
    returned in this case.
  - At any stage of adaption, if adapt.AdaptException(TypeError) is
    raised, then the adaptation process stops, as if None had been
    returned from each stage.
  - If all adaption stages return None, there are two cases.  If the
    call to adapt() had a 'default' value, then this is returned;
    otherwise, an adapt.AdaptException is raised.
  feedback:
    I think this is the same as the current PEP, and different
    from the first PEP.  Comments?  Anything that was missed?
-
  topic: transitivity
  overview:
    A case for allowing A->C to work when A->B and B->C is
    available; an equally compelling case to forbid this was also
    given.  There are numerous reasons for not allowing transitive
    adapters, mostly that 'lossy' adapters or 'stateful' adapters
    are usually the problem cases.  However, a hard-and-fast rule
    for knowing when transitivity exists wasn't found.
  proposal:
  - When registering an adapter factory, from A->B, an additional
    flag 'transitive' will be available.  
  - This flag defaults to False, so specific care is needed when
    registering adapters which one considers to be transitive.
  - If there exist two adapter factories, X: A->B, and Y: B->C,
    the path factory Z: A->C will be considered registered
    if and only if both X and Y were registered 'Transitive'.
  - It is an error for a registration to cause two path factories
    from A to C to be constructed; thus the registry will never have a
    case where two transitive adaptation paths exist at a single time.
  - An explicit registration always has precedent over an
    a transitive path.
  - One can also register a None factory from A->B for the
    purpose of marking it transitive.  In this circumstance,
    the composite adapter is built through __conform__ and
    __adapt__.  The None registration is just a place holder
    to signal that a given path exists.
  feedback:
    I'm looking for warts in this plan, and verification if
    something like this has been done -- comments how well
    it works.  Alternative approaches?
-
  topic: substitutability
  overview:
    There is a problem with the default isinstance() behavior when
    someone derives a class from another to re-use implementation,
    but with a different 'concept'.  A mechanism to disable
    isinstance() is needed for this particular case.
  proposal:
  - The 'adapt' module will define a 'LiskovAdaptionError', which
    has as a text description something like:
     "Although the given class '%s' derives from '%s', it has been
     marked as not being substitutable; although it is a subclass,
     the intent has changed so that one should not assume an 'is-a'
     relationship." % (adaptee.__class__, protocol)
  - The 'adapt' module will provide an 'NotSubstitutable' adaption
    factory, which, by default, raises LiskovAdaptionError.
  - If someone is concerned that their subclass should not be 
    adapted to the superclass automatically, they should register
    the NotSubstitutable adapter to the superclass, recursively.
  feedback:
    I'm not sure how this would work for the adaptee-class's
    grandparent; perhaps a helper function that recursively
    marks super classes is needed?  Other comments?
-
  topic: declaration (aka Guido's syntax) and intrinsic adaption
  overview:
    Guido would like his type declaration syntax (see blog entry) to
    be equivalent to a call to adapt() without any additional
    arguments.  However, not all adapters should be created in the
    context of a declaration -- some should be created more
    explicitly.  We propose a mechanism where an adapter factory can
    register itself as not suitable for the declaration syntax.
  proposal:
  - The adapt.register method has an optional argument, 'intrinsic',
    that defaults to True.
  - The adapt() function has an optional argument, 'intrinsic_only' which
    defaults to True; and thus is the default for the declaration syntax.
  - If an adapter factory is registered with intrinsic = False, then
    it is _not_ used by default calls to adapt().
  - adapt( , intrinsic_only = False) will enable both sorts of adapters,
    intrinsic or not; enabling the use of adapters which should not
    be used by default in a declaration syntax.
  - all adapters created through __conform__ and __adapt__ are
    by default intrinsic since this parameter is not part of the
    function signature
  feedback:
    This is the simplest solution I heard on the list; the word
    'intrinsic' was given by Alex.  Is there a better word?  Should
    we even worry about this case?  Any other ways to view this issue?
-
  topic: adaptee (aka origin)
  overview:
    There was discussion as to how to get back to the original
    object from an adapter.  Is this in scope of PEP 246?
  proposal:
  - we specify an __adaptee__ property, to be optionally implemented 
    by an adapter that provides a reference adaptee
  - the adapt.register method has an optional argument, 'adaptee',
    that defaults to False; if it is True, adapt() calls will stuff
    away into a weak-reference mapping from adapter to adaptee.
  - an adapt.adaptee(adaptor) function which returns the given
    adaptee for the adaptor; this first checks the weak-reference
    table, and then checks for an __adaptee_ 
  feedback:
    Is this useful, worth the complexity?
-
  topic: sticky
  overview:
    Sticky adapters, that is, ones where there is only one instance
    per adaptee is a common use case.  Should the registry of PEP 246
    provide this feature?
  proposal:
  - the adapt.register method has an optional argument, 'sticky',
    that defaults to False
  - if the given adapter factory is marked sticky, then a call
    to adapt() will first check to see if a given adapter (keyed
    by protocol) has been created for the adaptee; if so, then
    that adapter is returned, otherwise the factory is asked to
    produce an adapter and that adapter is cashed.
  feedback:
    Is this useful, worth the complexity?  It seems like an easy
    operation.  The advantage to this approach (over each factory
    inheriting from a StickyFactory) is that registry queries can be
    done, to list sticky adapters and other bookkeeping chores.

Ok.  That's it.

Cheers,

Clark
--
Clark C. Evans                      Prometheus Research, LLC.
                                    http://www.prometheusresearch.com/
    o                               office: +1.203.777.2550 
  ~/ ,                              mobile: +1.203.444.0557 
 //
((   Prometheus Research: Transforming Data Into Knowledge
 \\  ,
   \/    - Research Exchange Database
   /\    - Survey & Assessment Technologies
   ` \   - Software Tools for Researchers
    ~ *


More information about the Python-Dev mailing list