Mailing List Archive

COM bites - help please
I've got a couple of pyCOM things that are kicking my @$$!=20

1. Trying to wrap a list of instances into a util.Collection. All =
instances have been prepped with _public_methods_ and _public_attrs_ and =
have been wrapped with a call to win32com.server.util.wrap(field) Trying =
to access the Collection from a com instance produces the following =
error.

>>> gm=3Dwin32com.client.Dispatch("Python.GMConnector")
>>> c1=3Dgm.GMTable('contact1')
getting table=3Dgmc.GMConnector.GMTable(self, TableName) =
<gmCOM.GMcomConnector instance at 9373c0> contact1

['lower', 'upper']
Wrapping Table

Looping over fields to set _public_attrs_ for table =
D:\GOLDMINE\DEMO\CONTACT1.DBF

Wrapping Fields
>>> c1.top()
>>> c1.contact # This is a GMField instance
<COMObject <unknown>>
>>> c1.contact.read()
'Karn Smith'
>>> c1.fields # This is a util.Collection(list of GMField =
instances)
Traceback (innermost last):
File "<pyshell#56>", line 1, in ?
c1.fields
File "D:\Python\win32com\client\dynamic.py", line 394, in __getattr__
raise pythoncom.com_error, details
com_error: (-2147467259, 'Unspecified error', None, None)


2. A computed attribute in __getattr__ does not want to be accessed =
through com. It is in the _public_attrs_ list but produces the =
following error when attempting to access it.

>>> c1.contact.value
Traceback (innermost last):
File "<pyshell#55>", line 1, in ?
c1.contact.value
File "D:\Python\win32com\client\dynamic.py", line 400, in __getattr__
raise AttributeError, "%s.%s" % (self._username_, attr)
AttributeError: <unknown>.value

3. PyUnicode objects bit me in the ass when strings are passed into =
methods and manipulated.
class Foo:
<COM stuff>
def bar(self, somestring):
somestring =3D string.upper(somestring)
return somestring

>>>foo =3D win32com.client.Dispatch("Python.Foo")
>>>foo.bar("Hello World")

Some traceback about TypeError PyUnicode.

Causing a need to change bar to
def bar(self, somestring):
somestring=3Dstr(somestring)
somestring =3D string.upper(somestring)
return somestring
This change needs to be made in ALL methods that accept and manipulate =
strings???


Is there a better method than COM for allowing someone who uses VB =
access to Python objects, short of converting all of the VB users to =
Python or resorting to reimplementing in VB or C++? The COM interface =
I've been able to expose is sufficient for now but does not allow some =
of the access that I have in Python. Maybe that's good in the end?

The work I've done in Python is short and sweet. I hate the thought of =
having to redo it all Not that the Python stuff would be thrown out, I =
just want to release some cool stuff to other GoldMine folks before I am =
finished with the complete application.

Any help appreciated.

Karl Putland
kperacles@geocities.com
COM bites - help please [ In reply to ]
> From: Karl Putland [mailto:kperacles@geocities.com]

> I've got a couple of pyCOM things that are kicking my @$$!
>
> 1. Trying to wrap a list of instances into a
> util.Collection. All instances have been prepped with
> _public_methods_ and _public_attrs_ and have been wrapped
> with a call to win32com.server.util.wrap(field) Trying to
> access the Collection from a com instance produces the
> following error.
>
[...]
> >>> c1.fields # This is a util.Collection(list of
> GMField instances)
> Traceback (innermost last):
> File "<pyshell#56>", line 1, in ?
> c1.fields
> File "D:\Python\win32com\client\dynamic.py", line 394, in
> __getattr__
> raise pythoncom.com_error, details
> com_error: (-2147467259, 'Unspecified error', None, None)
>

Not having the source makes it more difficult to help here...
If memory serves given a list of Python instances that can be wrapped as COM
objects, the way to make a collection is:

collection = util.Collection(map(lambda x: util.wrap(x), list)

> 3. PyUnicode objects bit me in the ass when strings are
> passed into methods and manipulated.
> class Foo:
> <COM stuff>
> def bar(self, somestring):
> somestring = string.upper(somestring)
> return somestring
>
> >>>foo = win32com.client.Dispatch("Python.Foo")
> >>>foo.bar("Hello World")
>

PyUnicode strings have an upper and a lower method so this becomes:
def bar(self, somestring):
somestring = somestring.upper()
return somestring

> Some traceback about TypeError PyUnicode.
>
> Causing a need to change bar to
> def bar(self, somestring):
> somestring=str(somestring)
> somestring = string.upper(somestring)
> return somestring
> This change needs to be made in ALL methods that accept and
> manipulate strings???
>

This is the bane of Unicode, the string module can't handle them.
You either have to pass Unicode into functions that like Unicode, or convert
the Unicode into a normal Python string. Not much else you can do about
this.
It's been rumored that Unicode support will get better in Python 1.6.

> Is there a better method than COM for allowing someone who
> uses VB access to Python objects, short of converting all of
> the VB users to Python or resorting to reimplementing in VB
> or C++?

Using Python is good. :) But yes, this is the easiest way for Python and VB
to talk to each other.

> The COM interface I've been able to expose is
> sufficient for now but does not allow some of the access that
> I have in Python. Maybe that's good in the end?
>

I think so. :)

Hope this helps out a little bit...

Bill
COM bites - help please [ In reply to ]
Karl Putland is frustrated:

> I've got a couple of pyCOM things that are kicking my @$$!

Bill Tutt got most of 'em...

> 2. A computed attribute in __getattr__ does not want to be accessed
> through com. It is in the _public_attrs_ list but produces the
> following error when attempting to access it.

Both you and the COM support are playing attribute games, and not
agreeing on how the game is played. Can you expose Get methods to
COM, and play your games there? That way, COM will find something
real (a method object), and you can still do lazy evalutaion.

BTW, I find it very helpful to use a separate (wrapper) object:

class RealWork:
...

class COMRealWorkWrap:
_public_methods_ = [ ... ]
def __init__(self, real_obj):
...

That also means I can put all my Unicode conversion in one place.

- Gordon
COM bites - help please [ In reply to ]
Bill Tutt <billtut@microsoft.com> wrote in message =
news:4D0A23B3F74DD111ACCD00805F31D8100DB90F9C@RED-MSG-50...
>=20
>=20
> > From: Karl Putland [mailto:kperacles@geocities.com]
>=20
> > I've got a couple of pyCOM things that are kicking my @$$!=20
> >=20
> > 1. Trying to wrap a list of instances into a=20
> > util.Collection. All instances have been prepped with=20
> > _public_methods_ and _public_attrs_ and have been wrapped=20
> > with a call to win32com.server.util.wrap(field) Trying to=20
> > access the Collection from a com instance produces the=20
> > following error.
> >=20
> [...]
> > >>> c1.fields # This is a util.Collection(list of=20
> > GMField instances)
> > Traceback (innermost last):
> > File "<pyshell#56>", line 1, in ?
> > c1.fields
> > File "D:\Python\win32com\client\dynamic.py", line 394, in=20
> > __getattr__
> > raise pythoncom.com_error, details
> > com_error: (-2147467259, 'Unspecified error', None, None)
> >=20
>=20
> Not having the source makes it more difficult to help here...
> If memory serves given a list of Python instances that can be wrapped =
as COM
> objects, the way to make a collection is:
>=20

I can e-mail the source if you would like to take a look. I thought it =
was going to be to large. There are four tightly connected modules and =
most of it is specific to GoldMine. I couldn't really reduce it beyond =
what it is.

> collection =3D util.Collection(map(lambda x: util.wrap(x), list)
>=20

Thanks,
Kind of what I've done, but this is how I had it done with the error =
being produced.

def gm_com_table_prep(table_inst):
"""Preppare for COM wrapper of gmtasble.GMTable

Need to see if we can generate the COM specific attributes on a per
instance basis.

The GMTable class is not pulicly accesable. GMTables are only =
created by
the factory in GMcomConnector. They get wrapped in the COM =
interface
there. This just gives each instance the appropriate COM attributes

"""

# Changes the date format to a string from a date tuple
table_inst.COM =3D 1
=20
table_inst._public_methods_ =3D [.'append',
'bottom',
'close',
'delete',
'filter',
'goto',
'is_sql',
'new',
'open',
'range',
'read',
'replace',
'search',
'seek',
'set_order',
'skip',
'tables',
'top',
'unlock']

table_inst._public_attrs_ =3D ['name',
'handle',
'records',
'rec_no',
'fields',
'fieldnames']
if debug: print "\nLooping over fields to set _public_attrs_ for =
table", table_inst.name
for name in table_inst.fieldnames:
table_inst._public_attrs_.append(string.lower(name))
if debug: print "\nWrapping Fields"
tmpfields=3D[]
for field in table_inst.fields:
field._public_methods_ =3D ['read',
'write',
'append']
field._public_attrs_ =3D ['name',
'size',
'type'
'value']
name =3D string.lower(field.name)
field =3D win32com.server.util.wrap(field)
tmpfields.append(field)
setattr(table_inst, name, field)
table_inst.fields=3Dtmpfields


> > 3. PyUnicode objects bit me in the ass when strings are=20
> > passed into methods and manipulated.
> > class Foo:
> > <COM stuff>
> > def bar(self, somestring):
> > somestring =3D string.upper(somestring)
> > return somestring
> >=20
> > >>>foo =3D win32com.client.Dispatch("Python.Foo")
> > >>>foo.bar("Hello World")
> >=20
>=20
> PyUnicode strings have an upper and a lower method so this becomes:
> def bar(self, somestring):
> somestring =3D somestring.upper()
> return somestring
>=20

Sorry simplistic example. Don't remember the exact error but I had to =
do it here and I don't even manipulate the string.

def seek(self, indexvalue=3D''):
"""Not quite sure how to handle this.
=20
Currently no err is exact match.
0 =3D=3D Error
1 =3D=3D Exact match - no error
2 =3D=3D Close match - no error???
"""

# String conversion for COM
indexvalue =3D str(indexvalue)
=20
res =3D self.gmdll.db_seek(self.handle, indexvalue)
if res =3D=3D 0:
raise GMSeekError, 0
elif res =3D=3D 2:
raise GMMatchError, indexvalue

> > Some traceback about TypeError PyUnicode.
> >=20
> > Causing a need to change bar to
> > def bar(self, somestring):
> > somestring=3Dstr(somestring)
> > somestring =3D string.upper(somestring)
> > return somestring
> > This change needs to be made in ALL methods that accept and=20
> > manipulate strings???
> >=20
>=20
> This is the bane of Unicode, the string module can't handle them.
> You either have to pass Unicode into functions that like Unicode, or =
convert
> the Unicode into a normal Python string. Not much else you can do =
about
> this.
> It's been rumored that Unicode support will get better in Python 1.6.
>=20

That would be good.

> > Is there a better method than COM for allowing someone who=20
> > uses VB access to Python objects, short of converting all of=20
> > the VB users to Python or resorting to reimplementing in VB=20
> > or C++? =20
>=20
> Using Python is good. :) But yes, this is the easiest way for Python =
and VB
> to talk to each other.
>=20
> > The COM interface I've been able to expose is=20
> > sufficient for now but does not allow some of the access that=20
> > I have in Python. Maybe that's good in the end?
> >=20
>=20
> I think so. :)
>=20
> Hope this helps out a little bit...
>=20
> Bill

Thanks Bill. Just got a little frustrated having to pollute the Python =
too much. I would love it if you or someone else could look over what I =
have so far, but I know all to well the time constraints of family and =
job. =20

I know that I have a couple things to work out. Like right now each =
field holds a reference to it's parent table. And there might be =
another reference floating around that doesn't let it die. TP's Cyclops =
might help me here. Other than that most of it is pretty solid.

Thanks for the help.

Karl Putland
kperacles@geocities.com
COM bites - help please [ In reply to ]
Gordon McMillan <gmcm@hypernet.com> wrote in message =
news:1279389887-25608327@hypernet.com...
> Karl Putland is frustrated:
>=20
> > I've got a couple of pyCOM things that are kicking my @$$!=20
>=20
> Bill Tutt got most of 'em...
>=20
> > 2. A computed attribute in __getattr__ does not want to be accessed
> > through com. It is in the _public_attrs_ list but produces the
> > following error when attempting to access it.
>=20
> Both you and the COM support are playing attribute games, and not=20
> agreeing on how the game is played. Can you expose Get methods to=20
> COM, and play your games there? That way, COM will find something=20
> real (a method object), and you can still do lazy evalutaion.
>=20

Kind of works this way already. field.value was just aliased to =
field.read. No shift key and it was easier to read.


> BTW, I find it very helpful to use a separate (wrapper) object:
>=20
> class RealWork:
> ...
>=20
> class COMRealWorkWrap:
> _public_methods_ =3D [ ... ]
> def __init__(self, real_obj):
> ...
>=20
> That also means I can put all my Unicode conversion in one place.

Hmmm... I might have to try this. Does your method require that you =
mirror all of the functions and attributes of the RealWork in =
COMRealWorkWrap??? Right now I'm just using a procedure to add the =
attributes to the objects. And I tried subclassing the root object that =
I wanted to expose.

class GMcomConnector(gmc.GMConnector):
"""COM wrapper of gmc.GMConnector"""
_public_methods_ =3D ['Init', 'GMTable','cleanup']
_public_attrs_ =3D ['DefaultDir','User']
_reg_verprogid_ =3D "Python.GMConnector.1"
_reg_progid_ =3D "Python.GMConnector"
_reg_desc_ =3D "Python - GoldMine Connection Manager"
_reg_clsid_ =3D "{8DAF3841-3E9B-11D3-A4DC-820D6CB34D6B}"
_reg_class_spec_ =3D "gmCOM.GMcomConnector"

def GMTable(self, table_name):
""" Override the base to wrap the Table in a COM wrapper"""
if debug:
print "getting table=3Dgmc.GMConnector.GMTable(self, =
TableName)", self, table_name
print '\n', dir(table_name)
try:
# Another Unicode string conversion
TableName=3Dstr(table_name)
table=3Dgmc.GMConnector.GMTable(self, TableName)
except:
import traceback
traceback.print_exc()
raw_input("Hit return to exit...")

=20
# set pythoncom specific attributes
if debug: print "Wrapping Table"
gm_com_table_prep(table)

# turn table.fields into a collection
table.fields=3Dwin32com.server.util.Collection(table.fields)

# Return the COM wrapped table
return win32com.server.util.wrap(table)
def Init(self,
DefaultDir=3D'd:\\goldmine\\demo',
User=3D'MASTER',
Password=3D'ACCESS'):
=20
self.DefaultDir =3D DefaultDir
self.User =3D User
self.Password =3D Password


def gm_com_table_prep(table_inst):
"""Preppare for COM wrapper of gmtasble.GMTable

Need to see if we can generate the COM specific attributes on a per
instance basis.

GMcomTable is not pulicly accesable. GMTables are only created by
the factory in GMcomConnector. They get wrapped in the COM =
interface
there

"""

# Changes the date format to a string from a date tuple
table_inst.COM =3D 1
=20
table_inst._public_methods_ =3D [.'append',
'bottom',
'close',
'delete',
'filter',
'goto',
'is_sql',
'new',
'open',
'range',
'read',
'replace',
'search',
'seek',
'set_order',
'skip',
'tables',
'top',
'unlock']

table_inst._public_attrs_ =3D ['name',
'handle',
'records',
'rec_no',
'fields',
'fieldnames']
if debug: print "\nLooping over fields to set _public_attrs_ for =
table", table_inst.name
for name in table_inst.fieldnames:
table_inst._public_attrs_.append(string.lower(name))
if debug: print "\nWrapping Fields"
tmpfields=3D[]
for field in table_inst.fields:
field._public_methods_ =3D ['read',
'write',
'append']
field._public_attrs_ =3D ['name',
'size',
'type'
'value']
name =3D string.lower(field.name)
field =3D win32com.server.util.wrap(field)
tmpfields.append(field)
setattr(table_inst, name, field)
table_inst.fields =3D tmpfields


>=20
> - Gordon
>=20
>=20

Thanks

Karl Putland
kperacles@geocities.com
COM bites - help please [ In reply to ]
Karl Putland wrote in message ...
> >>> c1.fields # This is a util.Collection(list of GMField
instances)
> Traceback (innermost last):
> File "<pyshell#56>", line 1, in ?
> c1.fields

Have you tried using the debugging flags? This can help alot. If you
register your object with "--debug", you can use either Pythonwin or
win32traceutil.py to see exceptions from the server side of Python COM.

This is likely to show a traceback saying something like "object does not
support the policy" - ie, as Bill said, you need a list of wrapped objects,
not Python classes.

[.However, win32com.server.util really should grow helpers for this - a
reasonable and common requirement - feel free to propose a change!]

> 3. PyUnicode objects bit me in the ass when strings are passed into
methods and manipulated.
> class Foo:

After reading Bills reply and your followup, Im unsure what your remaining
problem is with this. You have discovered that "str()" is the simplest way
to work with them, and we know that Python 1.5 and Unicode integration
suck - but is there some other Unicode problem you are having beyond this?

> >>>foo = win32com.client.Dispatch("Python.Foo")
> >>>foo.bar("Hello World")

> Some traceback about TypeError PyUnicode.

Again, use the "--debug" option, and you will also see a traceback on the
server side. This will point you at the problem.

Mark.