Discussion:
[python-win32] Issues with tkinter, COM servers and Python 3.x
Diego Vélez Torres
2015-05-16 17:49:12 UTC
Permalink
Hi:

I'm having issues with tkinter, COM servers and Python 3.4. I can't call
tkinter inside a COM server. I tried many examples of tkinter, but every
time I try to init a tkniter instance ( tk = tkinter.Tk() ) inside a
running COM server, the same following error arises:

'''
....
....
tk = Tk()
File "C:\Python34\Lib\tkinter\__init__.py", line 1851, in __init__
self.tk = _tkinter.create(screenName, baseName, className, interactive,
wantobjects, useTk, sync, use)
TypeError: must be str, not bytes
'''
Of course the Traceback is much larger, but I intentionally posted the same
5 last lines in order to show where in the __init__ file raises the error.
I have tried outside the COM server the same tkinter examples and they work
perfectly.

However, this is not the case with Python 2.7.9. All tkinter examples work
perfectly inside and outside the COM server.

I've intensely searched in the web about this specific subject with no luck.

Question: Is perhaps needed some setup in order to use tkinter inside a
COM server while in Python 3.x? ...or this may be a bug specifically of
Python 3.4?

Best regards,
--
Diego Vélez Torres
***@gmail.com
Teléfono: 098 28 57 58
Cuenca, Ecuador
Tim Roberts
2015-05-18 17:26:23 UTC
Permalink
Post by Diego Vélez Torres
I'm having issues with tkinter, COM servers and Python 3.4. I can't
call tkinter inside a COM server. I tried many examples of tkinter,
but every time I try to init a tkniter instance ( tk = tkinter.Tk()
tk = Tk()
File "C:\Python34\Lib\tkinter\__init__.py", line 1851, in __init__
self.tk <http://self.tk> = _tkinter.create(screenName, baseName,
className, interactive, wantobjects, useTk, sync, use)
TypeError: must be str, not bytes
Interesting. The signature in the source code requires screenName,
baseName, className, and use to be strings, but only screenName and use
are optional. className is not actually used -- the source overrides
it. As an experiment, can you change your code to this:
tk = Tk( baseName='xxx' )
and see what happens?
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Diego Vélez Torres
2015-05-18 19:49:43 UTC
Permalink
Hi Tim:

I just fixed the issue by applying your advise. But (...and here comes a
big BUT !!!), why do I have to do this when calling tkinter inside a COM
server, meanwhile this is not necessarily when outside a COM? If my COM
Server were using tkinter directly I could rely on this trick. However, my
originally problem came up by trying to use pyplot from Matplotlib. I
found that pyplot.figure() launches a tk instance. Therefore, in order to
use this library (and any other that uses tkinter) within COMs, I must
modify the source codes. It doesn't seem this to be a feasible solution.
Is there any other solution?

Best regards,
Post by Tim Roberts
Post by Diego Vélez Torres
I'm having issues with tkinter, COM servers and Python 3.4. I can't
call tkinter inside a COM server. I tried many examples of tkinter,
but every time I try to init a tkniter instance ( tk = tkinter.Tk()
tk = Tk()
File "C:\Python34\Lib\tkinter\__init__.py", line 1851, in __init__
self.tk <http://self.tk> = _tkinter.create(screenName, baseName,
className, interactive, wantobjects, useTk, sync, use)
TypeError: must be str, not bytes
Interesting. The signature in the source code requires screenName,
baseName, className, and use to be strings, but only screenName and use
are optional. className is not actually used -- the source overrides
tk = Tk( baseName='xxx' )
and see what happens?
--
Providenza & Boekelheide, Inc.
_______________________________________________
python-win32 mailing list
https://mail.python.org/mailman/listinfo/python-win32
--
Diego Vélez Torres
***@gmail.com
Teléfono: 098 28 57 58
Cuenca, Ecuador
Tim Roberts
2015-05-18 20:14:02 UTC
Permalink
Post by Diego Vélez Torres
I just fixed the issue by applying your advise. But (...and here comes
a big BUT !!!), why do I have to do this when calling tkinter inside a
COM server, meanwhile this is not necessarily when outside a COM?
You shouldn't. I'm sure this is a bug, but it would take some digging
to assign blame and come up with a fix. If you don't pass a baseName,
the code in tkinter.__init__.py tries to create one from sys.argv[0].
In a COM server, there may not be a clear value in sys.argv[0]. The
call will fail is baseName ends up None; my guess is that's what is
happening.
Post by Diego Vélez Torres
If my COM Server were using tkinter directly I could rely on this
trick. However, my originally problem came up by trying to use pyplot
from Matplotlib. I found that pyplot.figure() launches a tk
instance. Therefore, in order to use this library (and any other that
uses tkinter) within COMs, I must modify the source codes. It doesn't
seem this to be a feasible solution. Is there any other solution?
You should be able to modify lib\tkinter\__init.py, and just before the
call at line 1851 insert this:
if baseName is None:
baseName = 'xxx'
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Diego Vélez Torres
2015-05-18 22:06:49 UTC
Permalink
Hi Tim:

I finally was able to fix this issue in my code. Thanks for your advise.
Now I want to share with the community exactly what I did by posting the
whole "__init__" method of tkinter's "__init__.py" file:

def __init__(self, screenName=None, baseName=None, className='Tk',
useTk=1, sync=0, use=None):
"""Return a new Toplevel widget on screen SCREENNAME. A new Tcl
interpreter will
be created. BASENAME will be used for the identification of the
profile file (see
readprofile).
It is constructed from sys.argv[0] without extensions if None is
given. CLASSNAME
is the name of the widget class."""
self.master = None
self.children = {}
self._tkloaded = 0
# to avoid recursions in the getattr code in case of failure, we
# ensure that self.tk is always _something_.
self.tk = None
if baseName is None:
import os
baseName = os.path.basename(sys.argv[0])

# Condition added by Diego Velez so 'tkinter' can be called
from COM Servers. --May 18th, 2015
if isinstance(baseName, bytes):
baseName = baseName.decode()

baseName, ext = os.path.splitext(baseName)
if ext not in ('.py', '.pyc', '.pyo'):
baseName = baseName + ext
interactive = 0
self.tk = _tkinter.create(screenName, baseName, className,
interactive, wantobjects, useTk, sync, use)
if useTk:
self._loadtk()
if not sys.flags.ignore_environment:
# Issue #16248: Honor the -E flag to avoid code injection.
self.readprofile(baseName, className)

As you can see, I added a condition as soon baseName gets sys.argv[0]. If
its of a bytes type it encodes to a string type. I'm sure that my COM
servers will work. However, I wonder if this should be the final solution
to this "bug"; since I don't know if tkinter should deal with these cases,
or the COM servers should not send byte types to python codes. Shouldn't
we inform to the guys of Python?

Best regards,
Post by Tim Roberts
Post by Diego Vélez Torres
I just fixed the issue by applying your advise. But (...and here comes
a big BUT !!!), why do I have to do this when calling tkinter inside a
COM server, meanwhile this is not necessarily when outside a COM?
You shouldn't. I'm sure this is a bug, but it would take some digging
to assign blame and come up with a fix. If you don't pass a baseName,
the code in tkinter.__init__.py tries to create one from sys.argv[0].
In a COM server, there may not be a clear value in sys.argv[0]. The
call will fail is baseName ends up None; my guess is that's what is
happening.
Post by Diego Vélez Torres
If my COM Server were using tkinter directly I could rely on this
trick. However, my originally problem came up by trying to use pyplot
from Matplotlib. I found that pyplot.figure() launches a tk
instance. Therefore, in order to use this library (and any other that
uses tkinter) within COMs, I must modify the source codes. It doesn't
seem this to be a feasible solution. Is there any other solution?
You should be able to modify lib\tkinter\__init.py, and just before the
baseName = 'xxx'
--
Providenza & Boekelheide, Inc.
_______________________________________________
python-win32 mailing list
https://mail.python.org/mailman/listinfo/python-win32
--
Diego Vélez Torres
***@gmail.com
Teléfono: 098 28 57 58
Cuenca, Ecuador
Tim Roberts
2015-05-18 23:02:27 UTC
Permalink
Post by Diego Vélez Torres
I finally was able to fix this issue in my code. Thanks for your
advise. Now I want to share with the community exactly what I did by
def __init__(self, screenName=None, baseName=None, className='Tk',
... # Condition added by Diego Velez so 'tkinter' can be called
from COM Servers. --May 18th, 2015
baseName = baseName.decode()
That's very bizarre. Did you print out the value of baseName to see
what it was? I'm wondering if something in the Python COM machinery is
faking os.path.basename and didn't get the memo about strings in Python 3.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Mark Hammond
2015-05-20 06:32:11 UTC
Permalink
Post by Tim Roberts
Post by Diego Vélez Torres
I finally was able to fix this issue in my code. Thanks for your
advise. Now I want to share with the community exactly what I did by
def __init__(self, screenName=None, baseName=None, className='Tk',
... # Condition added by Diego Velez so 'tkinter' can be called
from COM Servers. --May 18th, 2015
baseName = baseName.decode()
That's very bizarre. Did you print out the value of baseName to see
what it was? I'm wondering if something in the Python COM machinery is
faking os.path.basename and didn't get the memo about strings in Python 3.
Yeah - argv[0] is created inappropriately for py3k -
http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/file/b57d0d7444fb/com/win32com/src/dllmain.cpp#l68

Mark

divelez69
2015-05-18 23:28:25 UTC
Permalink
Yes, I did. In normal situations the value is a string, but when I call from a COM is bytes.


Sent from Samsung Mobile
Post by Diego Vélez Torres
I finally was able to fix this issue in my code. Thanks for your
advise. Now I want to share with the community exactly what I did by
def __init__(self, screenName=None, baseName=None, className='Tk',
... # Condition added by Diego Velez so 'tkinter' can be called
from COM Servers. --May 18th, 2015
baseName = baseName.decode()
That's very bizarre. Did you print out the value of baseName to see
what it was? I'm wondering if something in the Python COM machinery is
faking os.path.basename and didn't get the memo about strings in Python 3.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Tim Roberts
2015-05-19 00:33:32 UTC
Permalink
Post by divelez69
Yes, I did. In normal situations the value is a string, but when I
call from a COM is bytes.
I meant, what was the actual VALUE of the string? Was it a fake value,
or was it approximately correct?

If Python itself is generating os.path.basename, it's hard for me to
understand how it could get the wrong type.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Tim Roberts
2015-05-19 17:31:06 UTC
Permalink
The VALUES were an empty string and an empty bytes respectively.
Before applying your advise, I put some 'prints' in order to
understand what is happening to 'baseName' variable. What I found was
that when I initiate an instance of tkinter app: app = tkinter.Tk()
outside a COM object (eg. directly from the interactive window), the
value was '' (I mean an empty string). The same code, but called from
a COM the value was a b'' (an empty variable of the type bytes). I
don't know if I'm answering your question. I hope I do.
Absolutely, yes. Thanks. I have to think this is a bug in the Python
COM interface layer, injecting a value into sys.argv without
implementing the Python 3 string changes. I don't see the problem in 5
minutes of perusal, but we should get this on a bug list.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
divelez69
2015-05-19 20:08:02 UTC
Permalink
Well, at least I feel very proud to have discovered a bug.  I guess someone else might have step with it before.


Sent from Samsung Mobile
The VALUES were an empty string and an empty bytes respectively.
Before applying your advise, I put some 'prints' in order to
understand what is happening to 'baseName' variable. What I found was
that when I initiate an instance of tkinter app: app = tkinter.Tk()
outside a COM object (eg. directly from the interactive window), the
value was '' (I mean an empty string). The same code, but called from
a COM the value was a b'' (an empty variable of the type bytes). I
don't know if I'm answering your question. I hope I do.
Absolutely, yes. Thanks. I have to think this is a bug in the Python
COM interface layer, injecting a value into sys.argv without
implementing the Python 3 string changes. I don't see the problem in 5
minutes of perusal, but we should get this on a bug list.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Loading...