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