[PEAK] Circular class adapters..

Bob Ippolito bob at redivi.com
Thu Feb 19 23:34:24 EST 2004


On Feb 19, 2004, at 11:07 PM, Phillip J. Eby wrote:

> At 10:31 PM 2/19/04 -0500, Bob Ippolito wrote:
>> If you want to take a look at what I am using circular class adaption 
>> for, I have made a snapshot of ptypes at 
>> http://undefined.org/python/ptypes-20040219.tgz -- you will need to 
>> install a VERY RECENT (as in hours ago) CVS version of PyProtocols if 
>> you want to play with it.
>>
>> It's basically a toolkit for reading/writing binary formats with an 
>> API sort of like ctypes.  It includes support for an "iterfunc" 
>> reading style (generator abuse), so it can be hooked into Twisted and 
>> read off the wire incrementally without needing Stackless.  The goal 
>> is to be able to define these data structures in such a way that it 
>> does as much of the boilerplate reading and writing as possible.
>
> Interesting.  I'm curious about something, though.  You use 
> BaseObjectsList as a base for a lot of things, but it looks to me as 
> though it'd be simpler to just create an adapter from a 
> sequenceOf(IFunctionWritable) -> IFunctionWritable, and from a 
> sequenceOf(IOutputSize) -> IOutputSize, and so on for any other 
> capabilities that need to be done across a sequence of items.  Then, 
> you could just use sequences of "types" directly.
>
> There seem to be other things that also seem one level removed, like 
> the various 'str' subclasses.  But maybe I'm not following the actual 
> use cases of the library correctly.
>
> It would seem to me that I would define a format as a protocol -- 
> possibly parameterized -- and then use adaptation to define writers 
> directly.  For example, instead of having 'pypackable' create a class, 
> I'd make it a Protocol subclass with parameters, that automatically 
> registered appropriate adapter function(s) for builtin types, or maybe 
> from an IInteger, IString, etc.  And the new protocol object would 
> imply a base protocol for writing or reading.
>
> I think this approach, or something like it, could remove one level of 
> "meta" ness throughout the library, making the code simpler and easier 
> to follow, while possibly expanding the flexibility further.  But, 
> I've only made a very brief pass over the code, so I could be missing 
> your critical use cases.

This is a first pass port from something that was poisoned by 
twisted.python.components which originally didn't use adaptation at 
all... I only spent a couple hours on it over the past two days, and I 
didn't spend a lot of time on either of the previous iterations.  When 
porting it, I did see a lot of opportunities to PyProtocol-ize things, 
but I wanted it to work first :)

The str/int subclasses are to maintain metadata.. you don't know if 
it's supposed to be a pascal string or a c string or a short integer or 
a byte if you don't carry around metadata.

Some code using the original (no adaptation whatsoever) ptypes looked 
like this:

class mach_header(Structure):
     _fields_ = (
         ('magic', p_ulong),
         ('cputype', cpu_type_t),
         ('cpusubtype', cpu_subtype_t),
         ('filetype', p_ulong),
         ('ncmds', p_ulong),
         ('sizeofcmds', p_ulong),
         ('flags', p_ulong),
     )
     def _describe(self):
         bit = 1L
         flags = self.flags
         dflags = []
         while flags and bit < (1<<32L):
             if flags & bit:
                 dflags.append(MH_FLAGS_NAMES.get(bit, str(bit)))
                 flags = flags ^ bit
             bit <<= 1L
         return (
             ('magic', '0x%08X' % self.magic),
             ('cputype', CPU_TYPE_NAMES.get(self.cputype, self.cputype)),
             ('cpusubtype', self.cpusubtype),
             ('filetype', MH_FILETYPE_NAMES.get(self.filetype, 
self.filetype)),
             ('ncmds', self.ncmds),
             ('sizeofcmds', self.sizeofcmds),
             ('flags', dflags),
         )

elsewhere....
         fh = file(filename)
         self.header = mach_header.from_fileobj(fh)
         cmd = self.commands = []

         read_bytes = 0
         low_offset = sys.maxint
         for i in range(header.ncmds):
             # read the load command
             cmd_load = load_command.from_fileobj(fh)

... and it goes on to parse an entire Mach O file into a Python data 
structure, lets you mutate it, and then serialize back to disk.  Mach O 
is the OS X / Darwin equivalent to ELF or PE (I think it was PE on 
win32).  I use it to do dependency walking of python extensions to 
determine what shared libraries they depend on, and then rewrite their 
headers dynamically such that they can live happily inside of a 
standalone application bundle (a use case like py2exe).  It's hard to 
explain why I need to rewrite load commands in the headers because dyld 
is quite different than most linkers, but it's necessary to make things 
work correctly without re-linking everything for standalone 
distribution :)

-bob




More information about the PEAK mailing list