[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