[pypy-dev] Re: "Unwrap" Concidered Harmful

Armin Rigo arigo at tunes.org
Wed Sep 3 15:50:29 CEST 2003


Hello Michael,

On Tue, Sep 02, 2003 at 03:10:06PM +0100, Michael Hudson wrote:
> >  * wrap(x) -> create a blackboxed reference for the interpreter object x
> >  * unwrap(w_x,ExpectedType) -> inverse of the previous operation
> >  * newint(i), newstr(s)... -> create simple object space objects
> 
> (note this is like a bit like Py_BuildValue in the C API)
> 
> I'd *much* rather write space.build(1) than space.newint(1).

Ok. Maybe the whole issue should be sorted out in the general context of how 
to declare "gateways" between interpreter- and app-level. For example, given 
an interpreter-level class

class X:
  def __init__(self, frame):
    self.w_stuff = space.newdict([])
    self.n = 5
    self.frame = frame
  def dosomething(self, w_x, i):
    return self.n

how could we cleanly specify that we want 'n' and 'dosomething' be
app-level-visible as, respectively, an integer object and a method taking two
arguments the second of which must be an integer? In other words the whole
Py_BuildValue / Py_BuildTuple / PyArg_ParseTuple business, plus
structmember.c.

The point of giving the same name to wrap/unwrap and to the proposed 
build/unbuild is to have a uniform way of exposing attributes of different 
kinds, as shown on the above example:

 * w_stuff is a wrapped app-level object all along, no conversion required.
 * n is an integer, must use build/unbuild.
 * frame is another interp-level internal object, must use wrap/unwrap.

This is the reason why we might want a single function pair for all three 
cases. Alternatively we could use 'type descriptor' objects:

 * t_wrapped.wrap(space, w_stuff)   -> w_stuff
 * t_int.wrap(space, 5)             -> w_5
 * t_interplevel.wrap(space, frame) -> wrapped frame

 * t_wrapped.unwrap(space, w_stuff)     -> w_stuff
 * t_int.unwrap(space, w_5)             -> 5
 * t_interplevel.unwrap(space, w_frame) -> frame

The advantage is that we can declare function signatures explicitely, say
appmethod([t_wrapped, t_int], t_int) for the above dosomething() method, and
it is clear that the wrap/unwrap methods of the specified type descriptors
will be used for the conversion at appropriate times. (Type descriptors are
also a good place to store extra information and functionality if needed, e.g. 
the corresponding C type. The idea comes from Thomas Heller's ctypes module.)

The t_xxx objects above could also be classes instead, with wrap/unwrap class 
or static methods. This would let us use the interpreter-level class hierarchy 
directly instead of the t_interplevel trick:

  Frame.unwrap(space, w_frame) -> frame

This is more explicit because it can check that we actually have, not just an 
interpreter-level object, but a Frame instance.

We could also put the space argument at the end, so that we can write

  Frame.wrap(frame, space) -> w_frame

or

  frame.wrap(space) -> w_frame

and thus wrap (but not unwrap) could actually be a regular method.

As a final note, there would then be a lot of different wrap/unwrap methods
defined all around which handle different types -- certainly a good thing for 
flexibility and also for the annotation object space, which would not get 
confused by the fact that a single build() or unwrap() could accept a whole 
lot of different types.


A bientôt,

Armin.



More information about the Pypy-dev mailing list