reSTify PEP 246 (#409)
This commit is contained in:
parent
cd6642d2f2
commit
c510df9ac8
717
pep-0246.txt
717
pep-0246.txt
|
@ -6,361 +6,373 @@ Author: aleaxit@gmail.com (Alex Martelli),
|
||||||
cce@clarkevans.com (Clark C. Evans)
|
cce@clarkevans.com (Clark C. Evans)
|
||||||
Status: Rejected
|
Status: Rejected
|
||||||
Type: Standards Track
|
Type: Standards Track
|
||||||
|
Content-Type: text/x-rst
|
||||||
Created: 21-Mar-2001
|
Created: 21-Mar-2001
|
||||||
Python-Version: 2.5
|
Python-Version: 2.5
|
||||||
Post-History: 29-Mar-2001, 10-Jan-2005
|
Post-History: 29-Mar-2001, 10-Jan-2005
|
||||||
|
|
||||||
|
|
||||||
Rejection Notice
|
Rejection Notice
|
||||||
|
================
|
||||||
|
|
||||||
I'm rejecting this PEP. Something much better is about to happen;
|
I'm rejecting this PEP. Something much better is about to happen;
|
||||||
it's too early to say exactly what, but it's not going to resemble
|
it's too early to say exactly what, but it's not going to resemble
|
||||||
the proposal in this PEP too closely so it's better to start a new
|
the proposal in this PEP too closely so it's better to start a new
|
||||||
PEP. GvR.
|
PEP. GvR.
|
||||||
|
|
||||||
|
|
||||||
Abstract
|
Abstract
|
||||||
|
========
|
||||||
|
|
||||||
This proposal puts forth an extensible cooperative mechanism for
|
This proposal puts forth an extensible cooperative mechanism for
|
||||||
the adaptation of an incoming object to a context which expects an
|
the adaptation of an incoming object to a context which expects an
|
||||||
object supporting a specific protocol (say a specific type, class,
|
object supporting a specific protocol (say a specific type, class,
|
||||||
or interface).
|
or interface).
|
||||||
|
|
||||||
This proposal provides a built-in "adapt" function that, for any
|
This proposal provides a built-in "adapt" function that, for any
|
||||||
object X and any protocol Y, can be used to ask the Python
|
object X and any protocol Y, can be used to ask the Python
|
||||||
environment for a version of X compliant with Y. Behind the
|
environment for a version of X compliant with Y. Behind the
|
||||||
scenes, the mechanism asks object X: "Are you now, or do you know
|
scenes, the mechanism asks object X: "Are you now, or do you know
|
||||||
how to wrap yourself to provide, a supporter of protocol Y?".
|
how to wrap yourself to provide, a supporter of protocol Y?".
|
||||||
And, if this request fails, the function then asks protocol Y:
|
And, if this request fails, the function then asks protocol Y:
|
||||||
"Does object X support you, or do you know how to wrap it to
|
"Does object X support you, or do you know how to wrap it to
|
||||||
obtain such a supporter?" This duality is important, because
|
obtain such a supporter?" This duality is important, because
|
||||||
protocols can be developed after objects are, or vice-versa, and
|
protocols can be developed after objects are, or vice-versa, and
|
||||||
this PEP lets either case be supported non-invasively with regard
|
this PEP lets either case be supported non-invasively with regard
|
||||||
to the pre-existing component[s].
|
to the pre-existing component[s].
|
||||||
|
|
||||||
Lastly, if neither the object nor the protocol know about each
|
Lastly, if neither the object nor the protocol know about each
|
||||||
other, the mechanism may check a registry of adapter factories,
|
other, the mechanism may check a registry of adapter factories,
|
||||||
where callables able to adapt certain objects to certain protocols
|
where callables able to adapt certain objects to certain protocols
|
||||||
can be registered dynamically. This part of the proposal is
|
can be registered dynamically. This part of the proposal is
|
||||||
optional: the same effect could be obtained by ensuring that
|
optional: the same effect could be obtained by ensuring that
|
||||||
certain kinds of protocols and/or objects can accept dynamic
|
certain kinds of protocols and/or objects can accept dynamic
|
||||||
registration of adapter factories, for example via suitable custom
|
registration of adapter factories, for example via suitable custom
|
||||||
metaclasses. However, this optional part allows adaptation to be
|
metaclasses. However, this optional part allows adaptation to be
|
||||||
made more flexible and powerful in a way that is not invasive to
|
made more flexible and powerful in a way that is not invasive to
|
||||||
either protocols or other objects, thereby gaining for adaptation
|
either protocols or other objects, thereby gaining for adaptation
|
||||||
much the same kind of advantage that Python standard library's
|
much the same kind of advantage that Python standard library's
|
||||||
"copy_reg" module offers for serialization and persistence.
|
"copy_reg" module offers for serialization and persistence.
|
||||||
|
|
||||||
This proposal does not specifically constrain what a protocol
|
This proposal does not specifically constrain what a protocol
|
||||||
_is_, what "compliance to a protocol" exactly _means_, nor what
|
*is*, what "compliance to a protocol" exactly *means*, nor what
|
||||||
precisely a wrapper is supposed to do. These omissions are
|
precisely a wrapper is supposed to do. These omissions are
|
||||||
intended to leave this proposal compatible with both existing
|
intended to leave this proposal compatible with both existing
|
||||||
categories of protocols, such as the existing system of type and
|
categories of protocols, such as the existing system of type and
|
||||||
classes, as well as the many concepts for "interfaces" as such
|
classes, as well as the many concepts for "interfaces" as such
|
||||||
which have been proposed or implemented for Python, such as the
|
which have been proposed or implemented for Python, such as the
|
||||||
one in PEP 245 [1], the one in Zope3 [2], or the ones discussed in
|
one in PEP 245 [1]_, the one in Zope3 [2]_, or the ones discussed in
|
||||||
the BDFL's Artima blog in late 2004 and early 2005 [3]. However,
|
the BDFL's Artima blog in late 2004 and early 2005 [3]_. However,
|
||||||
some reflections on these subjects, intended to be suggestive and
|
some reflections on these subjects, intended to be suggestive and
|
||||||
not normative, are also included.
|
not normative, are also included.
|
||||||
|
|
||||||
|
|
||||||
Motivation
|
Motivation
|
||||||
|
==========
|
||||||
|
|
||||||
Currently there is no standardized mechanism in Python for
|
Currently there is no standardized mechanism in Python for
|
||||||
checking if an object supports a particular protocol. Typically,
|
checking if an object supports a particular protocol. Typically,
|
||||||
existence of certain methods, particularly special methods such as
|
existence of certain methods, particularly special methods such as
|
||||||
__getitem__, is used as an indicator of support for a particular
|
``__getitem__``, is used as an indicator of support for a particular
|
||||||
protocol. This technique works well for a few specific protocols
|
protocol. This technique works well for a few specific protocols
|
||||||
blessed by the BDFL (Benevolent Dictator for Life). The same can
|
blessed by the BDFL (Benevolent Dictator for Life). The same can
|
||||||
be said for the alternative technique based on checking
|
be said for the alternative technique based on checking
|
||||||
'isinstance' (the built-in class "basestring" exists specifically
|
'isinstance' (the built-in class "basestring" exists specifically
|
||||||
to let you use 'isinstance' to check if an object "is a [built-in]
|
to let you use 'isinstance' to check if an object "is a \[built-in\]
|
||||||
string"). Neither approach is easily and generally extensible to
|
string"). Neither approach is easily and generally extensible to
|
||||||
other protocols, defined by applications and third party
|
other protocols, defined by applications and third party
|
||||||
frameworks, outside of the standard Python core.
|
frameworks, outside of the standard Python core.
|
||||||
|
|
||||||
Even more important than checking if an object already supports a
|
Even more important than checking if an object already supports a
|
||||||
given protocol can be the task of obtaining a suitable adapter
|
given protocol can be the task of obtaining a suitable adapter
|
||||||
(wrapper or proxy) for the object, if the support is not already
|
(wrapper or proxy) for the object, if the support is not already
|
||||||
there. For example, a string does not support the file protocol,
|
there. For example, a string does not support the file protocol,
|
||||||
but you can wrap it into a StringIO instance to obtain an object
|
but you can wrap it into a StringIO instance to obtain an object
|
||||||
which does support that protocol and gets its data from the string
|
which does support that protocol and gets its data from the string
|
||||||
it wraps; that way, you can pass the string (suitably wrapped) to
|
it wraps; that way, you can pass the string (suitably wrapped) to
|
||||||
subsystems which require as their arguments objects that are
|
subsystems which require as their arguments objects that are
|
||||||
readable as files. Unfortunately, there is currently no general,
|
readable as files. Unfortunately, there is currently no general,
|
||||||
standardized way to automate this extremely important kind of
|
standardized way to automate this extremely important kind of
|
||||||
"adaptation by wrapping" operations.
|
"adaptation by wrapping" operations.
|
||||||
|
|
||||||
Typically, today, when you pass objects to a context expecting a
|
Typically, today, when you pass objects to a context expecting a
|
||||||
particular protocol, either the object knows about the context and
|
particular protocol, either the object knows about the context and
|
||||||
provides its own wrapper or the context knows about the object and
|
provides its own wrapper or the context knows about the object and
|
||||||
wraps it appropriately. The difficulty with these approaches is
|
wraps it appropriately. The difficulty with these approaches is
|
||||||
that such adaptations are one-offs, are not centralized in a
|
that such adaptations are one-offs, are not centralized in a
|
||||||
single place of the users code, and are not executed with a common
|
single place of the users code, and are not executed with a common
|
||||||
technique, etc. This lack of standardization increases code
|
technique, etc. This lack of standardization increases code
|
||||||
duplication with the same adapter occurring in more than one place
|
duplication with the same adapter occurring in more than one place
|
||||||
or it encourages classes to be re-written instead of adapted. In
|
or it encourages classes to be re-written instead of adapted. In
|
||||||
either case, maintainability suffers.
|
either case, maintainability suffers.
|
||||||
|
|
||||||
It would be very nice to have a standard function that can be
|
It would be very nice to have a standard function that can be
|
||||||
called upon to verify an object's compliance with a particular
|
called upon to verify an object's compliance with a particular
|
||||||
protocol and provide for a wrapper if one is readily available --
|
protocol and provide for a wrapper if one is readily available --
|
||||||
all without having to hunt through each library's documentation
|
all without having to hunt through each library's documentation
|
||||||
for the incantation appropriate to that particular, specific case.
|
for the incantation appropriate to that particular, specific case.
|
||||||
|
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
|
============
|
||||||
|
|
||||||
When considering an object's compliance with a protocol, there are
|
When considering an object's compliance with a protocol, there are
|
||||||
several cases to be examined:
|
several cases to be examined:
|
||||||
|
|
||||||
a) When the protocol is a type or class, and the object has
|
a) When the protocol is a type or class, and the object has
|
||||||
exactly that type or is an instance of exactly that class (not
|
exactly that type or is an instance of exactly that class (not
|
||||||
a subclass). In this case, compliance is automatic.
|
a subclass). In this case, compliance is automatic.
|
||||||
|
|
||||||
b) When the object knows about the protocol, and either considers
|
b) When the object knows about the protocol, and either considers
|
||||||
itself compliant, or knows how to wrap itself suitably.
|
itself compliant, or knows how to wrap itself suitably.
|
||||||
|
|
||||||
c) When the protocol knows about the object, and either the object
|
c) When the protocol knows about the object, and either the object
|
||||||
already complies or the protocol knows how to suitably wrap the
|
already complies or the protocol knows how to suitably wrap the
|
||||||
object.
|
object.
|
||||||
|
|
||||||
d) When the protocol is a type or class, and the object is a
|
d) When the protocol is a type or class, and the object is a
|
||||||
member of a subclass. This is distinct from the first case (a)
|
member of a subclass. This is distinct from the first case (a)
|
||||||
above, since inheritance (unfortunately) does not necessarily
|
above, since inheritance (unfortunately) does not necessarily
|
||||||
imply substitutability, and thus must be handled carefully.
|
imply substitutability, and thus must be handled carefully.
|
||||||
|
|
||||||
e) When the context knows about the object and the protocol and
|
e) When the context knows about the object and the protocol and
|
||||||
knows how to adapt the object so that the required protocol is
|
knows how to adapt the object so that the required protocol is
|
||||||
satisfied. This could use an adapter registry or similar
|
satisfied. This could use an adapter registry or similar
|
||||||
approaches.
|
approaches.
|
||||||
|
|
||||||
The fourth case above is subtle. A break of substitutability can
|
The fourth case above is subtle. A break of substitutability can
|
||||||
occur when a subclass changes a method's signature, or restricts
|
occur when a subclass changes a method's signature, or restricts
|
||||||
the domains accepted for a method's argument ("co-variance" on
|
the domains accepted for a method's argument ("co-variance" on
|
||||||
arguments types), or extends the co-domain to include return
|
arguments types), or extends the co-domain to include return
|
||||||
values which the base class may never produce ("contra-variance"
|
values which the base class may never produce ("contra-variance"
|
||||||
on return types). While compliance based on class inheritance
|
on return types). While compliance based on class inheritance
|
||||||
_should_ be automatic, this proposal allows an object to signal
|
*should* be automatic, this proposal allows an object to signal
|
||||||
that it is not compliant with a base class protocol.
|
that it is not compliant with a base class protocol.
|
||||||
|
|
||||||
If Python gains some standard "official" mechanism for interfaces,
|
If Python gains some standard "official" mechanism for interfaces,
|
||||||
however, then the "fast-path" case (a) can and should be extended
|
however, then the "fast-path" case (a) can and should be extended
|
||||||
to the protocol being an interface, and the object an instance of
|
to the protocol being an interface, and the object an instance of
|
||||||
a type or class claiming compliance with that interface. For
|
a type or class claiming compliance with that interface. For
|
||||||
example, if the "interface" keyword discussed in [3] is adopted
|
example, if the "interface" keyword discussed in [3]_ is adopted
|
||||||
into Python, the "fast path" of case (a) could be used, since
|
into Python, the "fast path" of case (a) could be used, since
|
||||||
instantiable classes implementing an interface would not be
|
instantiable classes implementing an interface would not be
|
||||||
allowed to break substitutability.
|
allowed to break substitutability.
|
||||||
|
|
||||||
|
|
||||||
Specification
|
Specification
|
||||||
|
=============
|
||||||
|
|
||||||
This proposal introduces a new built-in function, adapt(), which
|
This proposal introduces a new built-in function, ``adapt()``, which
|
||||||
is the basis for supporting these requirements.
|
is the basis for supporting these requirements.
|
||||||
|
|
||||||
The adapt() function has three parameters:
|
The ``adapt()`` function has three parameters:
|
||||||
|
|
||||||
- `obj', the object to be adapted
|
- ``obj``, the object to be adapted
|
||||||
|
|
||||||
- `protocol', the protocol requested of the object
|
- ``protocol``, the protocol requested of the object
|
||||||
|
|
||||||
- `alternate', an optional object to return if the object could
|
- ``alternate``, an optional object to return if the object could
|
||||||
not be adapted
|
not be adapted
|
||||||
|
|
||||||
A successful result of the adapt() function returns either the
|
A successful result of the ``adapt()`` function returns either the
|
||||||
object passed `obj', if the object is already compliant with the
|
object passed ``obj``, if the object is already compliant with the
|
||||||
protocol, or a secondary object `wrapper', which provides a view
|
protocol, or a secondary object ``wrapper``, which provides a view
|
||||||
of the object compliant with the protocol. The definition of
|
of the object compliant with the protocol. The definition of
|
||||||
wrapper is deliberately vague, and a wrapper is allowed to be a
|
wrapper is deliberately vague, and a wrapper is allowed to be a
|
||||||
full object with its own state if necessary. However, the design
|
full object with its own state if necessary. However, the design
|
||||||
intention is that an adaptation wrapper should hold a reference to
|
intention is that an adaptation wrapper should hold a reference to
|
||||||
the original object it wraps, plus (if needed) a minimum of extra
|
the original object it wraps, plus (if needed) a minimum of extra
|
||||||
state which it cannot delegate to the wrapper object.
|
state which it cannot delegate to the wrapper object.
|
||||||
|
|
||||||
An excellent example of adaptation wrapper is an instance of
|
An excellent example of adaptation wrapper is an instance of
|
||||||
StringIO which adapts an incoming string to be read as if it was a
|
StringIO which adapts an incoming string to be read as if it was a
|
||||||
textfile: the wrapper holds a reference to the string, but deals
|
textfile: the wrapper holds a reference to the string, but deals
|
||||||
by itself with the "current point of reading" (from _where_ in the
|
by itself with the "current point of reading" (from *where* in the
|
||||||
wrapped strings will the characters for the next, e.g., "readline"
|
wrapped strings will the characters for the next, e.g., "readline"
|
||||||
call come from), because it cannot delegate it to the wrapped
|
call come from), because it cannot delegate it to the wrapped
|
||||||
object (a string has no concept of "current point of reading" nor
|
object (a string has no concept of "current point of reading" nor
|
||||||
anything else even remotely related to that concept).
|
anything else even remotely related to that concept).
|
||||||
|
|
||||||
A failure to adapt the object to the protocol raises an
|
A failure to adapt the object to the protocol raises an
|
||||||
AdaptationError (which is a subclass of TypeError), unless the
|
``AdaptationError`` (which is a subclass of ``TypeError``), unless the
|
||||||
alternate parameter is used, in this case the alternate argument
|
alternate parameter is used, in this case the alternate argument
|
||||||
is returned instead.
|
is returned instead.
|
||||||
|
|
||||||
To enable the first case listed in the requirements, the adapt()
|
To enable the first case listed in the requirements, the ``adapt()``
|
||||||
function first checks to see if the object's type or the object's
|
function first checks to see if the object's type or the object's
|
||||||
class are identical to the protocol. If so, then the adapt()
|
class are identical to the protocol. If so, then the ``adapt()``
|
||||||
function returns the object directly without further ado.
|
function returns the object directly without further ado.
|
||||||
|
|
||||||
To enable the second case, when the object knows about the
|
To enable the second case, when the object knows about the
|
||||||
protocol, the object must have a __conform__() method. This
|
protocol, the object must have a ``__conform__()`` method. This
|
||||||
optional method takes two arguments:
|
optional method takes two arguments:
|
||||||
|
|
||||||
- `self', the object being adapted
|
- ``self``, the object being adapted
|
||||||
|
|
||||||
- `protocol, the protocol requested
|
- ``protocol``, the protocol requested
|
||||||
|
|
||||||
Just like any other special method in today's Python, __conform__
|
Just like any other special method in today's Python, ``__conform__``
|
||||||
is meant to be taken from the object's class, not from the object
|
is meant to be taken from the object's class, not from the object
|
||||||
itself (for all objects, except instances of "classic classes" as
|
itself (for all objects, except instances of "classic classes" as
|
||||||
long as we must still support the latter). This enables a
|
long as we must still support the latter). This enables a
|
||||||
possible 'tp_conform' slot to be added to Python's type objects in
|
possible 'tp_conform' slot to be added to Python's type objects in
|
||||||
the future, if desired.
|
the future, if desired.
|
||||||
|
|
||||||
The object may return itself as the result of __conform__ to
|
The object may return itself as the result of ``__conform__`` to
|
||||||
indicate compliance. Alternatively, the object also has the
|
indicate compliance. Alternatively, the object also has the
|
||||||
option of returning a wrapper object compliant with the protocol.
|
option of returning a wrapper object compliant with the protocol.
|
||||||
If the object knows it is not compliant although it belongs to a
|
If the object knows it is not compliant although it belongs to a
|
||||||
type which is a subclass of the protocol, then __conform__ should
|
type which is a subclass of the protocol, then ``__conform__`` should
|
||||||
raise a LiskovViolation exception (a subclass of AdaptationError).
|
raise a ``LiskovViolation`` exception (a subclass of ``AdaptationError``).
|
||||||
Finally, if the object cannot determine its compliance, it should
|
Finally, if the object cannot determine its compliance, it should
|
||||||
return None to enable the remaining mechanisms. If __conform__
|
return ``None`` to enable the remaining mechanisms. If ``__conform__``
|
||||||
raises any other exception, "adapt" just propagates it.
|
raises any other exception, "adapt" just propagates it.
|
||||||
|
|
||||||
To enable the third case, when the protocol knows about the
|
To enable the third case, when the protocol knows about the
|
||||||
object, the protocol must have an __adapt__() method. This
|
object, the protocol must have an ``__adapt__()`` method. This
|
||||||
optional method takes two arguments:
|
optional method takes two arguments:
|
||||||
|
|
||||||
- `self', the protocol requested
|
- ``self``, the protocol requested
|
||||||
|
|
||||||
- `obj', the object being adapted
|
- ``obj``, the object being adapted
|
||||||
|
|
||||||
If the protocol finds the object to be compliant, it can return
|
If the protocol finds the object to be compliant, it can return
|
||||||
obj directly. Alternatively, the method may return a wrapper
|
obj directly. Alternatively, the method may return a wrapper
|
||||||
compliant with the protocol. If the protocol knows the object is
|
compliant with the protocol. If the protocol knows the object is
|
||||||
not compliant although it belongs to a type which is a subclass of
|
not compliant although it belongs to a type which is a subclass of
|
||||||
the protocol, then __adapt__ should raise a LiskovViolation
|
the protocol, then ``__adapt__`` should raise a ``LiskovViolation``
|
||||||
exception (a subclass of AdaptationError). Finally, when
|
exception (a subclass of ``AdaptationError``). Finally, when
|
||||||
compliance cannot be determined, this method should return None to
|
compliance cannot be determined, this method should return None to
|
||||||
enable the remaining mechanisms. If __adapt__ raises any other
|
enable the remaining mechanisms. If ``__adapt__`` raises any other
|
||||||
exception, "adapt" just propagates it.
|
exception, "adapt" just propagates it.
|
||||||
|
|
||||||
The fourth case, when the object's class is a sub-class of the
|
The fourth case, when the object's class is a sub-class of the
|
||||||
protocol, is handled by the built-in adapt() function. Under
|
protocol, is handled by the built-in ``adapt()`` function. Under
|
||||||
normal circumstances, if "isinstance(object, protocol)" then
|
normal circumstances, if "isinstance(object, protocol)" then
|
||||||
adapt() returns the object directly. However, if the object is
|
``adapt()`` returns the object directly. However, if the object is
|
||||||
not substitutable, either the __conform__() or __adapt__()
|
not substitutable, either the ``__conform__()`` or ``__adapt__()``
|
||||||
methods, as above mentioned, may raise an LiskovViolation (a
|
methods, as above mentioned, may raise an ``LiskovViolation`` (a
|
||||||
subclass of AdaptationError) to prevent this default behavior.
|
subclass of ``AdaptationError``) to prevent this default behavior.
|
||||||
|
|
||||||
If none of the first four mechanisms worked, as a last-ditch
|
If none of the first four mechanisms worked, as a last-ditch
|
||||||
attempt, 'adapt' falls back to checking a registry of adapter
|
attempt, 'adapt' falls back to checking a registry of adapter
|
||||||
factories, indexed by the protocol and the type of `obj', to meet
|
factories, indexed by the protocol and the type of ``obj``, to meet
|
||||||
the fifth case. Adapter factories may be dynamically registered
|
the fifth case. Adapter factories may be dynamically registered
|
||||||
and removed from that registry to provide "third party adaptation"
|
and removed from that registry to provide "third party adaptation"
|
||||||
of objects and protocols that have no knowledge of each other, in
|
of objects and protocols that have no knowledge of each other, in
|
||||||
a way that is not invasive to either the object or the protocols.
|
a way that is not invasive to either the object or the protocols.
|
||||||
|
|
||||||
|
|
||||||
Intended Use
|
Intended Use
|
||||||
|
============
|
||||||
|
|
||||||
The typical intended use of adapt is in code which has received
|
The typical intended use of adapt is in code which has received
|
||||||
some object X "from the outside", either as an argument or as the
|
some object X "from the outside", either as an argument or as the
|
||||||
result of calling some function, and needs to use that object
|
result of calling some function, and needs to use that object
|
||||||
according to a certain protocol Y. A "protocol" such as Y is
|
according to a certain protocol Y. A "protocol" such as Y is
|
||||||
meant to indicate an interface, usually enriched with some
|
meant to indicate an interface, usually enriched with some
|
||||||
semantics constraints (such as are typically used in the "design
|
semantics constraints (such as are typically used in the "design
|
||||||
by contract" approach), and often also some pragmatical
|
by contract" approach), and often also some pragmatical
|
||||||
expectation (such as "the running time of a certain operation
|
expectation (such as "the running time of a certain operation
|
||||||
should be no worse than O(N)", or the like); this proposal does
|
should be no worse than O(N)", or the like); this proposal does
|
||||||
not specify how protocols are designed as such, nor how or whether
|
not specify how protocols are designed as such, nor how or whether
|
||||||
compliance to a protocol is checked, nor what the consequences may
|
compliance to a protocol is checked, nor what the consequences may
|
||||||
be of claiming compliance but not actually delivering it (lack of
|
be of claiming compliance but not actually delivering it (lack of
|
||||||
"syntactic" compliance -- names and signatures of methods -- will
|
"syntactic" compliance -- names and signatures of methods -- will
|
||||||
often lead to exceptions being raised; lack of "semantic"
|
often lead to exceptions being raised; lack of "semantic"
|
||||||
compliance may lead to subtle and perhaps occasional errors
|
compliance may lead to subtle and perhaps occasional errors
|
||||||
[imagine a method claiming to be threadsafe but being in fact
|
[imagine a method claiming to be threadsafe but being in fact
|
||||||
subject to some subtle race condition, for example]; lack of
|
subject to some subtle race condition, for example]; lack of
|
||||||
"pragmatic" compliance will generally lead to code that runs
|
"pragmatic" compliance will generally lead to code that runs
|
||||||
``correctly'', but too slowly for practical use, or sometimes to
|
``correctly``, but too slowly for practical use, or sometimes to
|
||||||
exhaustion of resources such as memory or disk space).
|
exhaustion of resources such as memory or disk space).
|
||||||
|
|
||||||
When protocol Y is a concrete type or class, compliance to it is
|
When protocol Y is a concrete type or class, compliance to it is
|
||||||
intended to mean that an object allows all of the operations that
|
intended to mean that an object allows all of the operations that
|
||||||
could be performed on instances of Y, with "comparable" semantics
|
could be performed on instances of Y, with "comparable" semantics
|
||||||
and pragmatics. For example, a hypothetical object X that is a
|
and pragmatics. For example, a hypothetical object X that is a
|
||||||
singly-linked list should not claim compliance with protocol
|
singly-linked list should not claim compliance with protocol
|
||||||
'list', even if it implements all of list's methods: the fact that
|
'list', even if it implements all of list's methods: the fact that
|
||||||
indexing X[n] takes time O(n), while the same operation would be
|
indexing ``X[n]`` takes time O(n), while the same operation would be
|
||||||
O(1) on a list, makes a difference. On the other hand, an
|
O(1) on a list, makes a difference. On the other hand, an
|
||||||
instance of StringIO.StringIO does comply with protocol 'file',
|
instance of ``StringIO.StringIO`` does comply with protocol 'file',
|
||||||
even though some operations (such as those of module 'marshal')
|
even though some operations (such as those of module 'marshal')
|
||||||
may not allow substituting one for the other because they perform
|
may not allow substituting one for the other because they perform
|
||||||
explicit type-checks: such type-checks are "beyond the pale" from
|
explicit type-checks: such type-checks are "beyond the pale" from
|
||||||
the point of view of protocol compliance.
|
the point of view of protocol compliance.
|
||||||
|
|
||||||
While this convention makes it feasible to use a concrete type or
|
While this convention makes it feasible to use a concrete type or
|
||||||
class as a protocol for purposes of this proposal, such use will
|
class as a protocol for purposes of this proposal, such use will
|
||||||
often not be optimal. Rarely will the code calling 'adapt' need
|
often not be optimal. Rarely will the code calling 'adapt' need
|
||||||
ALL of the features of a certain concrete type, particularly for
|
ALL of the features of a certain concrete type, particularly for
|
||||||
such rich types as file, list, dict; rarely can all those features
|
such rich types as file, list, dict; rarely can all those features
|
||||||
be provided by a wrapper with good pragmatics, as well as syntax
|
be provided by a wrapper with good pragmatics, as well as syntax
|
||||||
and semantics that are really the same as a concrete type's.
|
and semantics that are really the same as a concrete type's.
|
||||||
|
|
||||||
Rather, once this proposal is accepted, a design effort needs to
|
Rather, once this proposal is accepted, a design effort needs to
|
||||||
start to identify the essential characteristics of those protocols
|
start to identify the essential characteristics of those protocols
|
||||||
which are currently used in Python, particularly within the
|
which are currently used in Python, particularly within the
|
||||||
standard library, and to formalize them using some kind of
|
standard library, and to formalize them using some kind of
|
||||||
"interface" construct (not necessarily requiring any new syntax: a
|
"interface" construct (not necessarily requiring any new syntax: a
|
||||||
simple custom metaclass would let us get started, and the results
|
simple custom metaclass would let us get started, and the results
|
||||||
of the effort could later be migrated to whatever "interface"
|
of the effort could later be migrated to whatever "interface"
|
||||||
construct is eventually accepted into the Python language). With
|
construct is eventually accepted into the Python language). With
|
||||||
such a palette of more formally designed protocols, the code using
|
such a palette of more formally designed protocols, the code using
|
||||||
'adapt' will be able to ask for, say, adaptation into "a filelike
|
'adapt' will be able to ask for, say, adaptation into "a filelike
|
||||||
object that is readable and seekable", or whatever else it
|
object that is readable and seekable", or whatever else it
|
||||||
specifically needs with some decent level of "granularity", rather
|
specifically needs with some decent level of "granularity", rather
|
||||||
than too-generically asking for compliance to the 'file' protocol.
|
than too-generically asking for compliance to the 'file' protocol.
|
||||||
|
|
||||||
Adaptation is NOT "casting". When object X itself does not
|
Adaptation is NOT "casting". When object X itself does not
|
||||||
conform to protocol Y, adapting X to Y means using some kind of
|
conform to protocol Y, adapting X to Y means using some kind of
|
||||||
wrapper object Z, which holds a reference to X, and implements
|
wrapper object Z, which holds a reference to X, and implements
|
||||||
whatever operation Y requires, mostly by delegating to X in
|
whatever operation Y requires, mostly by delegating to X in
|
||||||
appropriate ways. For example, if X is a string and Y is 'file',
|
appropriate ways. For example, if X is a string and Y is 'file',
|
||||||
the proper way to adapt X to Y is to make a StringIO(X), *NOT* to
|
the proper way to adapt X to Y is to make a ``StringIO(X)``, **NOT** to
|
||||||
call file(X) [which would try to open a file named by X].
|
call ``file(X)`` [which would try to open a file named by X].
|
||||||
|
|
||||||
Numeric types and protocols may need to be an exception to this
|
Numeric types and protocols may need to be an exception to this
|
||||||
"adaptation is not casting" mantra, however.
|
"adaptation is not casting" mantra, however.
|
||||||
|
|
||||||
|
|
||||||
Guido's "Optional Static Typing: Stop the Flames" Blog Entry
|
Guido's "Optional Static Typing: Stop the Flames" Blog Entry
|
||||||
|
============================================================
|
||||||
|
|
||||||
A typical simple use case of adaptation would be:
|
|
||||||
|
A typical simple use case of adaptation would be::
|
||||||
|
|
||||||
def f(X):
|
def f(X):
|
||||||
X = adapt(X, Y)
|
X = adapt(X, Y)
|
||||||
# continue by using X according to protocol Y
|
# continue by using X according to protocol Y
|
||||||
|
|
||||||
In [4], the BDFL has proposed introducing the syntax:
|
In [4]_, the BDFL has proposed introducing the syntax::
|
||||||
|
|
||||||
def f(X: Y):
|
def f(X: Y):
|
||||||
# continue by using X according to protocol Y
|
# continue by using X according to protocol Y
|
||||||
|
|
||||||
to be a handy shortcut for exactly this typical use of adapt, and,
|
to be a handy shortcut for exactly this typical use of adapt, and,
|
||||||
as a basis for experimentation until the parser has been modified
|
as a basis for experimentation until the parser has been modified
|
||||||
to accept this new syntax, a semantically equivalent decorator:
|
to accept this new syntax, a semantically equivalent decorator::
|
||||||
|
|
||||||
@arguments(Y)
|
@arguments(Y)
|
||||||
def f(X):
|
def f(X):
|
||||||
# continue by using X according to protocol Y
|
# continue by using X according to protocol Y
|
||||||
|
|
||||||
These BDFL ideas are fully compatible with this proposal, as are
|
These BDFL ideas are fully compatible with this proposal, as are
|
||||||
other of Guido's suggestions in the same blog.
|
other of Guido's suggestions in the same blog.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference Implementation and Test Cases
|
Reference Implementation and Test Cases
|
||||||
|
=======================================
|
||||||
|
|
||||||
The following reference implementation does not deal with classic
|
The following reference implementation does not deal with classic
|
||||||
classes: it consider only new-style classes. If classic classes
|
classes: it consider only new-style classes. If classic classes
|
||||||
need to be supported, the additions should be pretty clear, though
|
need to be supported, the additions should be pretty clear, though
|
||||||
a bit messy (x.__class__ vs type(x), getting boundmethods directly
|
a bit messy (``x.__class__`` vs ``type(x)``, getting boundmethods directly
|
||||||
from the object rather than from the type, and so on).
|
from the object rather than from the type, and so on).
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
-----------------------------------------------------------------
|
-----------------------------------------------------------------
|
||||||
adapt.py
|
adapt.py
|
||||||
|
@ -531,27 +543,28 @@ Reference Implementation and Test Cases
|
||||||
|
|
||||||
|
|
||||||
Relationship To Microsoft's QueryInterface
|
Relationship To Microsoft's QueryInterface
|
||||||
|
==========================================
|
||||||
|
|
||||||
Although this proposal has some similarities to Microsoft's (COM)
|
Although this proposal has some similarities to Microsoft's (COM)
|
||||||
QueryInterface, it differs by a number of aspects.
|
QueryInterface, it differs by a number of aspects.
|
||||||
|
|
||||||
First, adaptation in this proposal is bi-directional, allowing the
|
First, adaptation in this proposal is bi-directional, allowing the
|
||||||
interface (protocol) to be queried as well, which gives more
|
interface (protocol) to be queried as well, which gives more
|
||||||
dynamic abilities (more Pythonic). Second, there is no special
|
dynamic abilities (more Pythonic). Second, there is no special
|
||||||
"IUnknown" interface which can be used to check or obtain the
|
"IUnknown" interface which can be used to check or obtain the
|
||||||
original unwrapped object identity, although this could be
|
original unwrapped object identity, although this could be
|
||||||
proposed as one of those "special" blessed interface protocol
|
proposed as one of those "special" blessed interface protocol
|
||||||
identifiers. Third, with QueryInterface, once an object supports
|
identifiers. Third, with QueryInterface, once an object supports
|
||||||
a particular interface it must always there after support this
|
a particular interface it must always there after support this
|
||||||
interface; this proposal makes no such guarantee, since, in
|
interface; this proposal makes no such guarantee, since, in
|
||||||
particular, adapter factories can be dynamically added to the
|
particular, adapter factories can be dynamically added to the
|
||||||
registried and removed again later.
|
registried and removed again later.
|
||||||
|
|
||||||
Fourth, implementations of Microsoft's QueryInterface must support
|
Fourth, implementations of Microsoft's QueryInterface must support
|
||||||
a kind of equivalence relation -- they must be reflexive,
|
a kind of equivalence relation -- they must be reflexive,
|
||||||
symmetrical, and transitive, in specific senses. The equivalent
|
symmetrical, and transitive, in specific senses. The equivalent
|
||||||
conditions for protocol adaptation according to this proposal
|
conditions for protocol adaptation according to this proposal
|
||||||
would also represent desirable properties:
|
would also represent desirable properties::
|
||||||
|
|
||||||
# given, to start with, a successful adaptation:
|
# given, to start with, a successful adaptation:
|
||||||
X_as_Y = adapt(X, Y)
|
X_as_Y = adapt(X, Y)
|
||||||
|
@ -568,51 +581,52 @@ Relationship To Microsoft's QueryInterface
|
||||||
X_as_Z_as_Y = adapt(X_as_Z, Y, None)
|
X_as_Z_as_Y = adapt(X_as_Z, Y, None)
|
||||||
assert (X_as_Y_as_Z is None) == (X_as_Z_as_Y is None)
|
assert (X_as_Y_as_Z is None) == (X_as_Z_as_Y is None)
|
||||||
|
|
||||||
However, while these properties are desirable, it may not be
|
However, while these properties are desirable, it may not be
|
||||||
possible to guarantee them in all cases. QueryInterface can
|
possible to guarantee them in all cases. QueryInterface can
|
||||||
impose their equivalents because it dictates, to some extent, how
|
impose their equivalents because it dictates, to some extent, how
|
||||||
objects, interfaces, and adapters are to be coded; this proposal
|
objects, interfaces, and adapters are to be coded; this proposal
|
||||||
is meant to be not necessarily invasive, usable and to "retrofit"
|
is meant to be not necessarily invasive, usable and to "retrofit"
|
||||||
adaptation between two frameworks coded in mutual ignorance of
|
adaptation between two frameworks coded in mutual ignorance of
|
||||||
each other without having to modify either framework.
|
each other without having to modify either framework.
|
||||||
|
|
||||||
Transitivity of adaptation is in fact somewhat controversial, as
|
Transitivity of adaptation is in fact somewhat controversial, as
|
||||||
is the relationship (if any) between adaptation and inheritance.
|
is the relationship (if any) between adaptation and inheritance.
|
||||||
|
|
||||||
The latter would not be controversial if we knew that inheritance
|
The latter would not be controversial if we knew that inheritance
|
||||||
always implies Liskov substitutability, which, unfortunately we
|
always implies Liskov substitutability, which, unfortunately we
|
||||||
don't. If some special form, such as the interfaces proposed in
|
don't. If some special form, such as the interfaces proposed in
|
||||||
[4], could indeed ensure Liskov substitutability, then for that
|
[4]_, could indeed ensure Liskov substitutability, then for that
|
||||||
kind of inheritance, only, we could perhaps assert that if X
|
kind of inheritance, only, we could perhaps assert that if X
|
||||||
conforms to Y and Y inherits from Z then X conforms to Z... but
|
conforms to Y and Y inherits from Z then X conforms to Z... but
|
||||||
only if substitutability was taken in a very strong sense to
|
only if substitutability was taken in a very strong sense to
|
||||||
include semantics and pragmatics, which seems doubtful. (For what
|
include semantics and pragmatics, which seems doubtful. (For what
|
||||||
it's worth: in QueryInterface, inheritance does not require nor
|
it's worth: in QueryInterface, inheritance does not require nor
|
||||||
imply conformance). This proposal does not include any "strong"
|
imply conformance). This proposal does not include any "strong"
|
||||||
effects of inheritance, beyond the small ones specifically
|
effects of inheritance, beyond the small ones specifically
|
||||||
detailed above.
|
detailed above.
|
||||||
|
|
||||||
Similarly, transitivity might imply multiple "internal" adaptation
|
Similarly, transitivity might imply multiple "internal" adaptation
|
||||||
passes to get the result of adapt(X, Z) via some intermediate Y,
|
passes to get the result of ``adapt(X, Z)`` via some intermediate Y,
|
||||||
intrinsically like adapt(adapt(X, Y), Z), for some suitable and
|
intrinsically like ``adapt(adapt(X, Y), Z)``, for some suitable and
|
||||||
automatically chosen Y. Again, this may perhaps be feasible under
|
automatically chosen Y. Again, this may perhaps be feasible under
|
||||||
suitably strong constraints, but the practical implications of
|
suitably strong constraints, but the practical implications of
|
||||||
such a scheme are still unclear to this proposal's authors. Thus,
|
such a scheme are still unclear to this proposal's authors. Thus,
|
||||||
this proposal does not include any automatic or implicit
|
this proposal does not include any automatic or implicit
|
||||||
transitivity of adaptation, under whatever circumstances.
|
transitivity of adaptation, under whatever circumstances.
|
||||||
|
|
||||||
For an implementation of the original version of this proposal
|
For an implementation of the original version of this proposal
|
||||||
which performs more advanced processing in terms of transitivity,
|
which performs more advanced processing in terms of transitivity,
|
||||||
and of the effects of inheritance, see Phillip J. Eby's
|
and of the effects of inheritance, see Phillip J. Eby's
|
||||||
PyProtocols [5]. The documentation accompanying PyProtocols is
|
``PyProtocols`` [5]_. The documentation accompanying ``PyProtocols`` is
|
||||||
well worth studying for its considerations on how adapters should
|
well worth studying for its considerations on how adapters should
|
||||||
be coded and used, and on how adaptation can remove any need for
|
be coded and used, and on how adaptation can remove any need for
|
||||||
typechecking in application code.
|
typechecking in application code.
|
||||||
|
|
||||||
|
|
||||||
Questions and Answers
|
Questions and Answers
|
||||||
|
=====================
|
||||||
|
|
||||||
Q: What benefit does this proposal provide?
|
* Q: What benefit does this proposal provide?
|
||||||
|
|
||||||
A: The typical Python programmer is an integrator, someone who is
|
A: The typical Python programmer is an integrator, someone who is
|
||||||
connecting components from various suppliers. Often, to
|
connecting components from various suppliers. Often, to
|
||||||
|
@ -625,7 +639,7 @@ Questions and Answers
|
||||||
and figuring out how to deploy the adapter takes time.
|
and figuring out how to deploy the adapter takes time.
|
||||||
|
|
||||||
This technique enables suppliers to work with each other
|
This technique enables suppliers to work with each other
|
||||||
directly, by implementing __conform__ or __adapt__ as
|
directly, by implementing ``__conform__`` or ``__adapt__`` as
|
||||||
necessary. This frees the integrator from making their own
|
necessary. This frees the integrator from making their own
|
||||||
adapters. In essence, this allows the components to have a
|
adapters. In essence, this allows the components to have a
|
||||||
simple dialogue among themselves. The integrator simply
|
simple dialogue among themselves. The integrator simply
|
||||||
|
@ -654,7 +668,7 @@ Questions and Answers
|
||||||
requiring SAX2 are unaware of each other.
|
requiring SAX2 are unaware of each other.
|
||||||
|
|
||||||
|
|
||||||
Q: Why does this have to be built-in, can't it be standalone?
|
* Q: Why does this have to be built-in, can't it be standalone?
|
||||||
|
|
||||||
A: Yes, it does work standalone. However, if it is built-in, it
|
A: Yes, it does work standalone. However, if it is built-in, it
|
||||||
has a greater chance of usage. The value of this proposal is
|
has a greater chance of usage. The value of this proposal is
|
||||||
|
@ -672,74 +686,79 @@ Questions and Answers
|
||||||
and even be of some help to a type inference system.
|
and even be of some help to a type inference system.
|
||||||
|
|
||||||
|
|
||||||
Q: Why the verbs __conform__ and __adapt__?
|
* Q: Why the verbs ``__conform__`` and ``__adapt__``?
|
||||||
|
|
||||||
A: conform, verb intransitive
|
A: conform, verb intransitive
|
||||||
|
|
||||||
1. To correspond in form or character; be similar.
|
1. To correspond in form or character; be similar.
|
||||||
2. To act or be in accord or agreement; comply.
|
2. To act or be in accord or agreement; comply.
|
||||||
3. To act in accordance with current customs or modes.
|
3. To act in accordance with current customs or modes.
|
||||||
|
|
||||||
adapt, verb transitive
|
adapt, verb transitive
|
||||||
1. To make suitable to or fit for a specific use or
|
|
||||||
situation.
|
1. To make suitable to or fit for a specific use or situation.
|
||||||
|
|
||||||
Source: The American Heritage Dictionary of the English
|
Source: The American Heritage Dictionary of the English
|
||||||
Language, Third Edition
|
Language, Third Edition
|
||||||
|
|
||||||
|
|
||||||
Backwards Compatibility
|
Backwards Compatibility
|
||||||
|
=======================
|
||||||
|
|
||||||
There should be no problem with backwards compatibility unless
|
There should be no problem with backwards compatibility unless
|
||||||
someone had used the special names __conform__ or __adapt__ in
|
someone had used the special names ``__conform__`` or ``__adapt__`` in
|
||||||
other ways, but this seems unlikely, and, in any case, user code
|
other ways, but this seems unlikely, and, in any case, user code
|
||||||
should never use special names for non-standard purposes.
|
should never use special names for non-standard purposes.
|
||||||
|
|
||||||
This proposal could be implemented and tested without changes to
|
This proposal could be implemented and tested without changes to
|
||||||
the interpreter.
|
the interpreter.
|
||||||
|
|
||||||
|
|
||||||
Credits
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
This proposal was created in large part by the feedback of the
|
This proposal was created in large part by the feedback of the
|
||||||
talented individuals on the main Python mailing lists and the
|
talented individuals on the main Python mailing lists and the
|
||||||
type-sig list. To name specific contributors (with apologies if
|
type-sig list. To name specific contributors (with apologies if
|
||||||
we missed anyone!), besides the proposal's authors: the main
|
we missed anyone!), besides the proposal's authors: the main
|
||||||
suggestions for the proposal's first versions came from Paul
|
suggestions for the proposal's first versions came from Paul
|
||||||
Prescod, with significant feedback from Robin Thomas, and we also
|
Prescod, with significant feedback from Robin Thomas, and we also
|
||||||
borrowed ideas from Marcin 'Qrczak' Kowalczyk and Carlos Ribeiro.
|
borrowed ideas from Marcin 'Qrczak' Kowalczyk and Carlos Ribeiro.
|
||||||
|
|
||||||
Other contributors (via comments) include Michel Pelletier, Jeremy
|
Other contributors (via comments) include Michel Pelletier, Jeremy
|
||||||
Hylton, Aahz Maruch, Fredrik Lundh, Rainer Deyke, Timothy Delaney,
|
Hylton, Aahz Maruch, Fredrik Lundh, Rainer Deyke, Timothy Delaney,
|
||||||
and Huaiyu Zhu. The current version owes a lot to discussions
|
and Huaiyu Zhu. The current version owes a lot to discussions
|
||||||
with (among others) Phillip J. Eby, Guido van Rossum, Bruce Eckel,
|
with (among others) Phillip J. Eby, Guido van Rossum, Bruce Eckel,
|
||||||
Jim Fulton, and Ka-Ping Yee, and to study and reflection of their
|
Jim Fulton, and Ka-Ping Yee, and to study and reflection of their
|
||||||
proposals, implementations, and documentation about use and
|
proposals, implementations, and documentation about use and
|
||||||
adaptation of interfaces and protocols in Python.
|
adaptation of interfaces and protocols in Python.
|
||||||
|
|
||||||
|
|
||||||
References and Footnotes
|
References and Footnotes
|
||||||
|
========================
|
||||||
|
|
||||||
[1] PEP 245, Python Interface Syntax, Pelletier
|
.. [1] PEP 245, Python Interface Syntax, Pelletier
|
||||||
http://www.python.org/dev/peps/pep-0245/
|
http://www.python.org/dev/peps/pep-0245/
|
||||||
|
|
||||||
[2] http://www.zope.org/Wikis/Interfaces/FrontPage
|
.. [2] http://www.zope.org/Wikis/Interfaces/FrontPage
|
||||||
|
|
||||||
[3] http://www.artima.com/weblogs/index.jsp?blogger=guido
|
.. [3] http://www.artima.com/weblogs/index.jsp?blogger=guido
|
||||||
|
|
||||||
[4] http://www.artima.com/weblogs/viewpost.jsp?thread=87182
|
.. [4] http://www.artima.com/weblogs/viewpost.jsp?thread=87182
|
||||||
|
|
||||||
[5] http://peak.telecommunity.com/PyProtocols.html
|
.. [5] http://peak.telecommunity.com/PyProtocols.html
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
|
=========
|
||||||
|
|
||||||
This document has been placed in the public domain.
|
This document has been placed in the public domain.
|
||||||
|
|
||||||
|
|
||||||
|
..
|
||||||
Local Variables:
|
Local Variables:
|
||||||
mode: indented-text
|
mode: indented-text
|
||||||
indent-tabs-mode: nil
|
indent-tabs-mode: nil
|
||||||
sentence-end-double-space: t
|
sentence-end-double-space: t
|
||||||
fill-column: 70
|
fill-column: 70
|
||||||
End:
|
End:
|
||||||
|
|
Loading…
Reference in New Issue