Mark Hammond wrote:
> Your changes look reasonable for the job they are designed to do, but
> doesn't provide a general solution I can try to adopt (which is fine!).
Yeah -- I didn't mean that as a patch for general distribution. 
'Suggested workaround' would've been better in the old subject line.

> Another alternative would be for pywin32 to grow a way to easily indicate
> exact types to be passed in Variant calls.
> 
> For example, your code could say something like:
> 
> v = Variant(VT_DOUBLE, (1.0, 2.0, 3.0))
> ms.AddPoint(v)
> 
> This needn't be that hard.  The "Variant" object could be a class defined in
> a .py file.  The IDispatch::Invoke method would grow support for detecting
> this object and forming the correct Variant.  The only query would be how
> InvokeTypes should handle such an object - ignoring the explicitly passed
> typeinfo seems wrong (but does seem practical!)
I have this working for what *I* need in AutoCAD (yay for me), but I'm 
not sure about the general case that you describe (not fair for you.)  I 
must admit I don't know how to fully test my implementation.  I also 
have at least one bug related to strings, which the test below will 
demonstrate.  I am soliciting any help I can get to solve the general 
case.  Or, failing that, I'm wondering if a less-general patch would be 
accepted (that patch is described at the end of this 
sorry-so-long-again-message.)

Here is an annotated interactive session using my current implementation:

[code]
 >>> class Variant: pass   #Not its final form.  :)

 >>> point = Variant() #This arg is specified as Variant (not byref)
 >>> point.__VT__ = 5
 >>> point.__VAR__ = (1, 1, 0)

#some code removed

 >>> ms.AddPoint(point)
<win32com.gen_py.AutoCAD 2006 Type Library.IAcadPoint instance at 
0x30506280>   #Success!  Point was interpreted as VT_ARRAY | VT_R8!

 >>> numindex = Variant()
 >>> numindex.__VT__ = 2 #VT_I2
 >>> numindex.__VAR__ = 1
 >>> strindex = Variant()
 >>> strindex.__VT__ = 8 #VT_BSTR
 >>> strindex.__VAR__ = 'LAYERNAME'

#some code removed

#doc.Layers() accepts one arg; it is specified only as VARIANT in the
#TYPEDESC.  It can be either an integer index, or a string "map"

 >>> doc.Layers(1).Name
u'layername'

 >>> doc.Layers('layername').Name
u'layername'

 >>> doc.Layers(numindex).Name
u'layername'

 >>> doc.Layers(strindex).Name
TypeError: Objects for SAFEARRAYS must be sequences (of sequences), or a 
buffer object.  #Drat.  Here's my 'string' bug.

[/code]

The implementation is in PyCom_VariantFromPyObject() in oleargs.cpp. 
This code gets called in all cases that use Invoke.  And, in the case of 
InvokeTypes, it gets called if the TYPEDESC calls for VT_VARIANT or 
VT_VARIANT | VT_BYREF (which covers everything for autocad, and the one 
other case I found from a quick google for similar issues.)

So -- I haven't changed too much what InvokeTypes already allows (i.e. 
I'm not ignoring any *more* explicitly passed info from a TYPEDESC).  :)

I added one IF clause to the very top of PyCom_VariantFromPyObject():

[code:  forgive any of my newness that shows thru]
if ( PyInstance_Check(obj) && PyObject_HasAttrString(obj, "__VT__") && 
PyObject_HasAttrString(obj, "__VAR__") )
{
    PyObject* reqdType = PyObject_GetAttrString(obj, "__VT__");
       if (!reqdType) return FALSE;

    VARENUM rawVT = (VARENUM)PyInt_AsLong(reqdType);

    PyObject* obuse = PyObject_GetAttrString(obj, "__VAR__");
    if (!obuse)
    {   
       Py_XDECREF(reqdType);
       return FALSE;
    }

    BOOL ok = FALSE;
    if ( PySequence_Check(obuse) )
    {   
       V_ARRAY(var) = NULL; // not a valid, existing array.
       ok = PyCom_SAFEARRAYFromPyObject(obuse, &V_ARRAY(var), rawVT);
       V_VT(var) = VT_ARRAY | rawVT;
    }
    else
    {
       PythonOleArgHelper helper;
       helper.m_reqdType = rawVT;
       V_VT(var) = rawVT;
       ok = helper.MakeObjToVariant(obuse, var);
    }

    Py_XDECREF(reqdType);
    Py_XDECREF(obuse);
    return ok;
}
//the rest of PyCom_MakeObjToVariant() follows
[/code]

So in the case of InvokeTypes, this can seem strange.  InvokeTypes will 
instantiate a PythonOleArgHelper.  If TYPEDESC calls for VT_VARIANT 
(byref or not), the ArgHelper delegates to PyCom_VariantFromPyObject(). 
  If PyCom_VariantFromPyObject() detects an instance of class Variant, 
it will instantiate a *different* ArgHelper (unless the argument is a 
sequence, in which case it delegates to PyCom_SAFEARRAYFromPyObject()).

It doesn't work for strings (at the very least), and I don't know how to 
test the Invoke case.

It will likely take me awhile to figure through the rest of this on my 
own.  In the meantime, this code could be pared down to solve a less 
general case than Mark describes above:  the Variant() class could be 
used *just* so that a user could build an array of objects typed to 
their choosing.  I think this would still be useful, and it would likely 
be forward-compatible with a solution for the general case.  So...if 
nobody else has a pressing need for this, or time to help with it, (Mark 
& other CVS committers) would you accept a less general patch?

Thanks all!

-Dan Glassman


_______________________________________________
Python-win32 mailing list
Python-win32@python.org
http://mail.python.org/mailman/listinfo/python-win32

Reply via email to