Yet Another PEP: Query Protocol Interface or __query__
Clark C. Evans
cce at clarkevans.com
Tue Mar 27 09:40:27 EST 2001
I've started to write an example usage of this PEP. It is
far from perfect, however it should give you an idea of
the usage. I could use some help with examples and tightening
this up...
Thanks! Clark
...
Example Usage (written but not checked)
Suppose a user has a function that requires a file-like
object. Here is how adapt can be used to ensure that
an argument does indeed follow the file protocol:
import types
from adapt import adapt
def first_line(file):
file = adapt(file,types.FileType)
print file.readline()
>>> first_line(open("c:/autoexec.bat"))
SET WXWIN=F:\PROGRA~1\wx2
>>> first_line('bad')
...
TypeError: test cannot be adapted to <type 'file'>
Let us assume that the code above becomes the norm, and
now the user needs to re-use this code with a BLOB
stored in a database. Here is how that would be done.
class PseudoFile:
def readline(self): # read line from BLOB
return "This is a new line" # real code goes here
# (other file functions go here)
def __conform__(self,protocol):
if protocol is types.FileType: return self
>>> first_line(PseudoFile())
This is a new line
Let us say that this works for a while, but sooner or later
the concept of a forward-access read-only file is needed.
So, perhaps the following "standard interface" emerges:
class ForwardFile:
def readline(self): pass
def read(self,size=-1): pass
class AdaptRead(ForwardFile):
def readline(self): # implement using read
class AdaptReadline(ForwardFile):
def read(self,size=-1): # implement using readline
class Adapt:
def __call__(self, obj):
# objects adaptable to FileType is are compliant
retval = adapt(obj,types.FileType)
if retval: return retval
# objects that have both readline and read are ok
if getattr(obj,'readline',None) and \
getattr(obj,'read',None): return obj
# wrap objects exposing a readline
if getattr(obj,'readline',None):
retval = AdaptReadline()
retval.readline = obj.readline
return retval
# wrap objects exposing a read
if getattr(obj,'read',None):
retval = AdaptRead()
retval.read = obj.read
return retval
__adapt__= Adapt()
This would allow our first-line program to be re-written to
use the new interface:
def first_line(file):
file = adapt(file,ForwardFile)
print file.readline()
Note that anything which inherits from ForwardFile is a
adaptable as well as anything which considers itself a FileType
More so, do to the __adapt__ class method above, anything
that has read or readline can be adapted to work!
Let's say that time passes on, and a record-set class is
created. And after the above interface is well in use
(and now immutable due to wide distribution), the creator
of the record-set class wishes to make this new class
substitutable for any ForwardFile.
class RecordSet:
def readline(): pass # some intelligent impl
def read(): pass # more intelligent impl
# other record set specific stuff
There are a few choices. First, the RecordSet class
could inherit from ForwardFile. Or, the following
__adapt__ method could be added!
def __conform__(self,protocol):
if protocol is ForwardFile: return self
This has a slight problem in that a dependency is now
created (ForwardFile must be on the user's system). This
can be mitigated with something like the following:
def __conform__(self,protocol):
if protocol.__name__ = 'ForwardFile': return self
Note that readline and read could be put in a wrapper class
instead (so that they don't clutter the RecordSet class!
Regardless, in every case where a ForwardFile is accepted,
the adapter mechanism will also pass off for objects of
this class.
Questions:
1. Is there a way to get a fully-qualified name
(including the package?)
2. What kind of "wrapper helpers" could be made?
(I like Carlos's examples!)
3. Perhaps a __conform__ function would also
like to know who the caller is... I wonder
if this would be useful. Can this be
obtained automatically?
3. Would a __protocol__ item also help?
class ForwardFile:
...
__protocol__ = "com.zoober.protocols.ForwardFile"
This would allow the __conform__ above to be re-written:
def __conform__(self,protocol):
if protocol.__protocol__ =
'com.zoober.protcols.ForwardFile':
return self
In this way, support for multiple protocols could
be put in place without requiring said classes/interface
declarations on the user's box.
4. Is a protcol registry needed, or does the
above suffice? (I think it's good enough)
A proposal would be like:
register_adapter(relevant_protocol, the_handler)
And then, after exhausting the object and the
protcol, it could check in the registry...
Thank you all for your help!
Clark
More information about the Python-list
mailing list