Skip to content

Fix Tcl inside a virtualenv on Windows (issue #93) #627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,9 @@ def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear, sy
site_filename_dst = change_prefix(site_filename, home_dir)
site_dir = os.path.dirname(site_filename_dst)
writefile(site_filename_dst, SITE_PY)
if is_win:
fixtk_filename = join(site_dir, 'FixTk.py')
writefile(fixtk_filename, FIXTK_PY)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to write a new file, but never use it. How is this functionality activated?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, wait. I see there's a Lib\lib-tk\FixTk.py in Python 2.7. So this fix doesn't work on Python 3 (where there's no lib-tk directory). Can it be updated to cover that as well?

Also, if this is a copy/paste and edit of the core FixTk.py, won't any changes core Python makes in later versions be lost? That doesn't sound good - I'd prefer a fix that properly monkeypatched whatever was in the core distribution, if we're going down this route at all.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if this is a copy/paste and edit of the core FixTk.py, won't any changes core Python makes in later versions be lost?

That's actually already the case for virtualenv's site.py that it has vendored / modified - it's missing functions that were introduced in 2.7 See #355

But since this is for 2.X only and we already know there's no 2.8, I can't imagine that is a worry for this particular file

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it meant to be for 2.x only? If so, the file should only be written on 2.x. And if it's not, the point remains for the equivalent file on 3.x - copying the file has risks. (OTOH, it may be that the 3.x version fixes this issue as it presumably supports venv...)

But yeah, it's a relatively minor point.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, wait. I see there's a Lib\lib-tk\FixTk.py in Python 2.7. So this fix doesn't work on Python 3 (where there's no lib-tk directory). Can it be updated to cover that as well?

The fix file also exist in Python 3, here: "C:\Python34\Lib\tkinter_fix.py"
it's simmilar content to "C:\Python27\Lib\lib-tk\FixTk.py"

writefile(join(site_dir, 'orig-prefix.txt'), prefix)
site_packages_filename = join(site_dir, 'no-global-site-packages.txt')
if not site_packages:
Expand Down Expand Up @@ -2001,6 +2004,31 @@ def convert(s):
AVijEPwfucjncQ==
""")

##file FixTk.py
FIXTK_PY = convert("""
eJy9VltP4zgUfq/U/3AUHkg12dCB1e4ILbMqpTARTEGlMLOiKHJTl3rr2pXtlObf77GTtAlQlofV
+qFK7XP9zpUtllIZ0JkOQOpmo9nYgzPKSQYsf4nNnAlDFaTCMA7PFGZkRUFTA8PuVXwVnQ46g78C
y6clmBnB+4TH50xMemuapIaMueXRQCCZEZFQMBK4TIihwIy2jFQkcsLEE0yYoomRKgtzS+7EFG1I
BdLyLLDKEyKENDAX8hmV0aoNW27LyqaWfCLFfoXaJBxWVGkmBUqbsWRWkTlFk0GmxnI/MzPDzxIE
9CiE86opeINEnFsXKJ+iQ3IOTFjePyo2fR2F4Qi1urv73uA2uu5/DSxSRGRmZn1OuNQWE8tqbVSU
cCees7EiKsvVTGQBSW+9JGinzhZjyVmCVGKuQQq4Z9qQZsOo7LjZADyF7YnJllTnV/l3+Iyech7O
qRKUHx2GF9RgvAi/IWbWJwt6mn1DHZz+aDboOqFLA37khPWUkiqAjjGKjVND3f9WoW9Cp5BIgfia
eImSfF2+2KOoSZUANIRyTT/EQrTGB2CaCXQNM8fHJNVGtfBtz6ZsuFR0ytZWhCFIhDDwDMaZKf21
J8W0gBPQ4YRillHfW4wT7bW2BLOcYAc2oy2hPV0Mj6HnDMHxreQA2usv7QDtOY+uevGg1zmLO8Ph
IDq9G/Zu67zV8znYfhe8t986g1zCbra+FDSAoyBnu77p9ePez+h2GPUvdjO11+3DtjsbO8+vOhfx
aad7eXcT3/a+d/rDqPuOtVZtBTGsrRy0E/jl83GdbQ+6eTnJJRXbkgzgiWHbSJd16m1WlDfjdLoN
RuLQjlPBbOhifJtS5aeehxgc/v7bl1Y1v/SHg7gz3/2ZCynqCYBT4eNHK9gNywuYW+j8/fXV3fde
3O/gz9l1BdIdlnVt9efanfI6ys6pE2i/xtgVje1jMCWYjBPwafgUgqDmWap53l/xtfVvcFvQ0MuH
Y1T1GLpG/EaJ7EH0JKSiCApxjXo0+nNUs1SHWKLKaNs4fW80sgQjr/XCcKtNP/x6/PgO712/+yYf
CvXgE7IfVdm3HjUbRTc4wUEW2m4S/i2Z8G2fsD01zp8DD9ux9Q0VuzQtaOka+6f2c6JS/x52qBVG
5SAMD5DNzA+wKedPH1O2eV0S5VLLc2Iwfz2UVEL8ShYZa9cOC2uKeYbzQuMgwJywhjuDA/yEaapw
cCicyUThREMiQemETpyP7/q3UVxrwhutRXy8ykDznG4mrFwqVkxJUYkVTmsQWFAFAUeV6PULpZXI
W9pa8F1sjl9XHN7nXbqGdgGyldJ6zVNxnmlrRi7kLfH2bB16qDn8iFpzzjInunKxxOEHw8vN+uNW
DFsYbgFixi08dppri0ZRqiU/PuF8rw3pcsvKL5HeFopRfnkfVvaHamAuPxaX1Q7kYN/M9z+hunrb
eZEzNc5VnsORmNC113oJZgXE/a1x+xbDVen+j1dr2RBzcLOW4W6EO9ki1aZMaEtCcftUdFOV201v
A0X08//KUbZ+O0fZ+j/IUSfkQzlacdjlqONsNv4BOP1jtw==
""")

##file activate.sh
ACTIVATE_SH = convert("""
eJytVVFvokAQfudXTLEPtTlLeo9tvMSmJpq02hSvl7u2wRUG2QR2DSxSe7n/frOACEVNLlceRHa+
Expand Down
78 changes: 78 additions & 0 deletions virtualenv_embedded/FixTk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import sys, os

# Delay import _tkinter until we have set TCL_LIBRARY,
# so that Tcl_FindExecutable has a chance to locate its
# encoding directory.

# Unfortunately, we cannot know the TCL_LIBRARY directory
# if we don't know the tcl version, which we cannot find out
# without import Tcl. Fortunately, Tcl will itself look in
# <TCL_LIBRARY>\..\tcl<TCL_VERSION>, so anything close to
# the real Tcl library will do.

# Expand symbolic links on Vista
try:
import ctypes
ctypes.windll.kernel32.GetFinalPathNameByHandleW
except (ImportError, AttributeError):
def convert_path(s):
return s
else:
def convert_path(s):
assert isinstance(s, str) # sys.prefix contains only bytes
udir = s.decode("mbcs")
hdir = ctypes.windll.kernel32.\
CreateFileW(udir, 0x80, # FILE_READ_ATTRIBUTES
1, # FILE_SHARE_READ
None, 3, # OPEN_EXISTING
0x02000000, # FILE_FLAG_BACKUP_SEMANTICS
None)
if hdir == -1:
# Cannot open directory, give up
return s
buf = ctypes.create_unicode_buffer(u"", 32768)
res = ctypes.windll.kernel32.\
GetFinalPathNameByHandleW(hdir, buf, len(buf),
0) # VOLUME_NAME_DOS
ctypes.windll.kernel32.CloseHandle(hdir)
if res == 0:
# Conversion failed (e.g. network location)
return s
s = buf[:res].encode("mbcs")
# Ignore leading \\?\
if s.startswith("\\\\?\\"):
s = s[4:]
if s.startswith("UNC"):
s = "\\" + s[3:]
return s

prefix = os.path.join(sys.real_prefix,"tcl")
if not os.path.exists(prefix):
# devdir/../tcltk/lib
prefix = os.path.join(sys.real_prefix, os.path.pardir, "tcltk", "lib")
prefix = os.path.abspath(prefix)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, how important is it to support in-place development build here? Does the rest of virtualenv work in that situation (I honestly don't know, I've never tried it).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You know this is a straight copy from Python's source, ya?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only after I posted that comment :-)

# if this does not exist, no further search is needed
if os.path.exists(prefix):
prefix = convert_path(prefix)
if "TCL_LIBRARY" not in os.environ:
for name in os.listdir(prefix):
if name.startswith("tcl"):
tcldir = os.path.join(prefix,name)
if os.path.isdir(tcldir):
os.environ["TCL_LIBRARY"] = tcldir
# Compute TK_LIBRARY, knowing that it has the same version
# as Tcl
import _tkinter
ver = str(_tkinter.TCL_VERSION)
if "TK_LIBRARY" not in os.environ:
v = os.path.join(prefix, 'tk'+ver)
if os.path.exists(os.path.join(v, "tclIndex")):
os.environ['TK_LIBRARY'] = v
# We don't know the Tix version, so we must search the entire
# directory
if "TIX_LIBRARY" not in os.environ:
for name in os.listdir(prefix):
if name.startswith("tix"):
tixdir = os.path.join(prefix,name)
if os.path.isdir(tixdir):
os.environ["TIX_LIBRARY"] = tixdir