Skip to content
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

Issue 3037 scripts must not share globals #3038

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
2 changes: 1 addition & 1 deletion PyInstaller/archive/writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ def add(self, entry):
# the object so it can be unmarshalled by the bootloader.

code = get_code_object(nm, pathnm)
code = strip_paths_in_code(code)
code = strip_paths_in_code(code, os.path.basename(pathnm))

code_data = marshal.dumps(code)
ulen = len(code_data)
Expand Down
1 change: 1 addition & 0 deletions PyInstaller/depend/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,7 @@ def get_bootstrap_modules():
('pyimod01_os_path', os.path.join(loaderpath, 'pyimod01_os_path.pyc'), 'PYMODULE'),
('pyimod02_archive', os.path.join(loaderpath, 'pyimod02_archive.pyc'), 'PYMODULE'),
('pyimod03_importers', os.path.join(loaderpath, 'pyimod03_importers.pyc'), 'PYMODULE'),
('pyimod04_ctypes', os.path.join(loaderpath, 'pyimod04_ctypes.pyc'), 'PYMODULE'),
('pyiboot01_bootstrap', os.path.join(loaderpath, 'pyiboot01_bootstrap.py'), 'PYSOURCE'),
]
# TODO Why is here the call to TOC()?
Expand Down
83 changes: 3 additions & 80 deletions PyInstaller/loader/pyiboot01_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,86 +123,9 @@ def isatty(self):
if sys.warnoptions:
import warnings

try:
import ctypes
import os
from ctypes import LibraryLoader, DEFAULT_MODE

def _frozen_name(name):
if name:
frozen_name = os.path.join(sys._MEIPASS, os.path.basename(name))
if os.path.exists(frozen_name):
name = frozen_name
return name

class PyInstallerImportError(OSError):
def __init__(self, name):
self.msg = ("Failed to load dynlib/dll %r. "
"Most probably this dynlib/dll was not found "
"when the application was frozen.") % name
self.args = (self.msg,)

class PyInstallerCDLL(ctypes.CDLL):
def __init__(self, name, *args, **kwargs):
name = _frozen_name(name)
try:
super(PyInstallerCDLL, self).__init__(name, *args, **kwargs)
except Exception as base_error:
raise PyInstallerImportError(name)

ctypes.CDLL = PyInstallerCDLL
ctypes.cdll = LibraryLoader(PyInstallerCDLL)

class PyInstallerPyDLL(ctypes.PyDLL):
def __init__(self, name, *args, **kwargs):
name = _frozen_name(name)
try:
super(PyInstallerPyDLL, self).__init__(name, *args, **kwargs)
except Exception as base_error:
raise PyInstallerImportError(name)

ctypes.PyDLL = PyInstallerPyDLL
ctypes.pydll = LibraryLoader(PyInstallerPyDLL)

if sys.platform.startswith('win'):
class PyInstallerWinDLL(ctypes.WinDLL):
def __init__(self, name,*args, **kwargs):
name = _frozen_name(name)
try:
super(PyInstallerWinDLL, self).__init__(name, *args, **kwargs)
except Exception as base_error:
raise PyInstallerImportError(name)

ctypes.WinDLL = PyInstallerWinDLL
ctypes.windll = LibraryLoader(PyInstallerWinDLL)

class PyInstallerOleDLL(ctypes.OleDLL):
def __init__(self, name,*args, **kwargs):
name = _frozen_name(name)
try:
super(PyInstallerOleDLL, self).__init__(name, *args, **kwargs)
except Exception as base_error:
raise PyInstallerImportError(name)

ctypes.OleDLL = PyInstallerOleDLL
ctypes.oledll = LibraryLoader(PyInstallerOleDLL)
except ImportError:
pass

# On Mac OS X insert sys._MEIPASS in the first position of the list of paths
# that ctypes uses to search for libraries.
#
# Note: 'ctypes' module will NOT be bundled with every app because code in this
# module is not scanned for module dependencies. It is safe to wrap
# 'ctypes' module into 'try/except ImportError' block.
if sys.platform.startswith('darwin'):
try:
from ctypes.macholib import dyld
dyld.DEFAULT_LIBRARY_FALLBACK.insert(0, sys._MEIPASS)
except ImportError:
# Do nothing when module 'ctypes' is not available.
pass

# Install the hooks for ctypes
import pyimod04_ctypes
pyimod04_ctypes.install()

# Make .eggs and zipfiles available at runtime
d = "eggs"
Expand Down
103 changes: 103 additions & 0 deletions PyInstaller/loader/pyimod04_ctypes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2017, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License with exception
# for distributing bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------

"""
Hooks to make ctypes.CDLL, .PyDLL, etc. look in sys._MEIPASS first.
"""

import sys

def install():
"""Install the hooks.

This can not be done at module-level, since the import machinery is not
setup completely when this module is executed.
"""

import os
try:
import ctypes
from ctypes import LibraryLoader, DEFAULT_MODE

def _frozen_name(name):
if name:
frozen_name = os.path.join(sys._MEIPASS, os.path.basename(name))
if os.path.exists(frozen_name):
name = frozen_name
return name

class PyInstallerImportError(OSError):
def __init__(self, name):
self.msg = ("Failed to load dynlib/dll %r. "
"Most probably this dynlib/dll was not found "
"when the application was frozen.") % name
self.args = (self.msg,)

class PyInstallerCDLL(ctypes.CDLL):
def __init__(self, name, *args, **kwargs):
name = _frozen_name(name)
try:
super(PyInstallerCDLL, self).__init__(name, *args, **kwargs)
except Exception as base_error:
raise PyInstallerImportError(name)

ctypes.CDLL = PyInstallerCDLL
ctypes.cdll = LibraryLoader(PyInstallerCDLL)

class PyInstallerPyDLL(ctypes.PyDLL):
def __init__(self, name, *args, **kwargs):
name = _frozen_name(name)
try:
super(PyInstallerPyDLL, self).__init__(name, *args, **kwargs)
except Exception as base_error:
raise PyInstallerImportError(name)

ctypes.PyDLL = PyInstallerPyDLL
ctypes.pydll = LibraryLoader(PyInstallerPyDLL)

if sys.platform.startswith('win'):
class PyInstallerWinDLL(ctypes.WinDLL):
def __init__(self, name,*args, **kwargs):
name = _frozen_name(name)
try:
super(PyInstallerWinDLL, self).__init__(name, *args, **kwargs)
except Exception as base_error:
raise PyInstallerImportError(name)

ctypes.WinDLL = PyInstallerWinDLL
ctypes.windll = LibraryLoader(PyInstallerWinDLL)

class PyInstallerOleDLL(ctypes.OleDLL):
def __init__(self, name,*args, **kwargs):
name = _frozen_name(name)
try:
super(PyInstallerOleDLL, self).__init__(name, *args, **kwargs)
except Exception as base_error:
raise PyInstallerImportError(name)

ctypes.OleDLL = PyInstallerOleDLL
ctypes.oledll = LibraryLoader(PyInstallerOleDLL)

except ImportError:
# ctypes is not frozen in this application
pass

# On Mac OS X insert sys._MEIPASS in the first position of the list of paths
# that ctypes uses to search for libraries.
#
# Note: 'ctypes' module will NOT be bundled with every app because code in this
# module is not scanned for module dependencies. It is safe to wrap
# 'ctypes' module into 'try/except ImportError' block.
if sys.platform.startswith('darwin'):
try:
from ctypes.macholib import dyld
dyld.DEFAULT_LIBRARY_FALLBACK.insert(0, sys._MEIPASS)
except ImportError:
# Do nothing when module 'ctypes' is not available.
pass
70 changes: 30 additions & 40 deletions bootloader/src/pyi_launch.c
Original file line number Diff line number Diff line change
Expand Up @@ -363,25 +363,20 @@ int
pyi_launch_run_scripts(ARCHIVE_STATUS *status)
{
unsigned char *data;
char buf[PATH_MAX];
size_t namelen;
TOC * ptoc = status->tocbuff;
PyObject *__main__;
PyObject *__file__;
PyObject *main_dict;
PyObject *code, *retval;
PyObject *__main__name;
PyObject *sys_modules;
PyObject *code, *module;

__main__ = PI_PyImport_AddModule("__main__");

if (!__main__) {
FATALERROR("Could not get __main__ module.");
return -1;
sys_modules = PI_PyImport_GetModuleDict(); /* borrowed reference */
if (is_py2) {
__main__name = PI_PyString_FromString("__main__");
}

main_dict = PI_PyModule_GetDict(__main__);

if (!main_dict) {
FATALERROR("Could not get __main__ module's dict.");
else {
__main__name = PI_PyUnicode_FromString("__main__");
}
if (__main__name == NULL) {
FATALERROR("Could not get object for string '__main__'.");
return -1;
}

Expand All @@ -390,53 +385,48 @@ pyi_launch_run_scripts(ARCHIVE_STATUS *status)
if (ptoc->typcd == ARCHIVE_ITEM_PYSOURCE) {
/* Get data out of the archive. */
data = pyi_arch_extract(status, ptoc);
/* Set the __file__ attribute within the __main__ module,
* for full compatibility with normal execution. */
namelen = strnlen(ptoc->name, PATH_MAX);
if (namelen >= PATH_MAX-strlen(".py")-1) {
FATALERROR("Name exceeds PATH_MAX\n");
return -1;
}

strcpy(buf, ptoc->name);
strcat(buf, ".py");
VS("LOADER: Running %s\n", buf);

if (is_py2) {
__file__ = PI_PyString_FromString(buf);
}
else {
__file__ = PI_PyUnicode_FromString(buf);
};
PI_PyObject_SetAttrString(__main__, "__file__", __file__);
Py_DECREF(__file__);
VS("LOADER: Running %s.py\n", ptoc->name);

/* Unmarshall code object */
code = PI_PyMarshal_ReadObjectFromString((const char *) data, ntohl(ptoc->ulen));

if (!code) {
FATALERROR("Failed to unmarshal code object for %s\n", ptoc->name);
PI_PyErr_Print();
return -1;
}
/* Run it */
retval = PI_PyEval_EvalCode(code, main_dict, main_dict);
Copy link

@ghost ghost Nov 30, 2017

Choose a reason for hiding this comment

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

Do we really need to remove this function? Seems like each iteration of the loop can be this:

main_dict_copy = PyDict_Copy(main_dict)
Py_INCREF(main_dict_copy)
retval = PI_PyEval_EvalCode(code, main_dict_copy, main_dict_copy);

Copy link
Member Author

Choose a reason for hiding this comment

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

This would be an idea to try. Thanks.

Copy link
Member Author

Choose a reason for hiding this comment

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

This does not work either :-( Same effect as with my code.


/* Run the code */
/* For full compatibility with normal execution, the __file__
* attribute within the __main__ module needs to be set.
* PyImport_ExecCodeModule() will set it based on
* code.co_filename. PyInstaller takes care that co_filename is
* correct. */
module = PI_PyImport_ExecCodeModule("__main__", code);

/* If retval is NULL, an error occured. Otherwise, it is a Python object.
* (Since we evaluate module-level code, which is not allowed to return an
* object, the Python object returned is always None.) */
if (!retval) {
if (!module) {
PI_PyErr_Print();
/* If the error was SystemExit, PyErr_Print calls exit() without
* returning. So don't print "Failed to execute" on SystemExit. */
FATALERROR("Failed to execute script %s\n", ptoc->name);
return -1;
}
Py_DECREF(module);
Copy link

Choose a reason for hiding this comment

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

Wait, how can you decref the module and then complain when you can no longer access its attributes?

Copy link
Member Author

Choose a reason for hiding this comment

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

I would expect the code to reference the dict, too. But anyway, I also tried not decrementing the reference count here with no success. But I'll double-check.


free(data);

/* remove '__main__' from sys.modules */
if (PI_PyObject_DelItem(sys_modules, __main__name) != 0) {
FATALERROR("Failed to remove '__main__' from sys.modules\n");
return -1;
}
}

ptoc = pyi_arch_increment_toc_ptr(status, ptoc);
}
Py_DECREF(__main__name);
return 0;
}

Expand Down
4 changes: 4 additions & 0 deletions bootloader/src/pyi_python.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ DECLPROC(PyErr_Print);

DECLPROC(PyImport_AddModule);
DECLPROC(PyImport_ExecCodeModule);
DECLPROC(PyImport_GetModuleDict);
DECLPROC(PyImport_ImportModule);
DECLPROC(PyList_Append);
DECLPROC(PyList_New);
DECLPROC(PyLong_AsLong);
DECLPROC(PyModule_GetDict);
DECLPROC(PyObject_CallFunction);
DECLPROC(PyObject_DelItem);
DECLPROC(PyObject_SetAttrString);
DECLPROC(PyRun_SimpleString);
DECLPROC(PyString_FromString);
Expand Down Expand Up @@ -121,12 +123,14 @@ pyi_python_map_names(HMODULE dll, int pyvers)
GETPROC(dll, PyErr_Print);
GETPROC(dll, PyImport_AddModule);
GETPROC(dll, PyImport_ExecCodeModule);
GETPROC(dll, PyImport_GetModuleDict);
GETPROC(dll, PyImport_ImportModule);
GETPROC(dll, PyList_Append);
GETPROC(dll, PyList_New);
GETPROC(dll, PyLong_AsLong);
GETPROC(dll, PyModule_GetDict);
GETPROC(dll, PyObject_CallFunction);
GETPROC(dll, PyObject_DelItem);
GETPROC(dll, PyObject_SetAttrString);
GETPROC(dll, PyRun_SimpleString);

Expand Down
2 changes: 2 additions & 0 deletions bootloader/src/pyi_python.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,13 @@ EXTDECLPROC(void, PySys_SetPath, (wchar_t *));
EXTDECLPROC(int, PySys_SetArgvEx, (int, wchar_t **, int));
EXTDECLPROC(int, PyRun_SimpleString, (char *)); /* Py3: UTF-8 encoded string */

EXTDECLPROC(PyObject *, PyImport_GetModuleDict, (void)); /* get sys.modules */
/* In Python 3 for these the first argument has to be a UTF-8 encoded string: */
EXTDECLPROC(PyObject *, PyImport_ExecCodeModule, (char *, PyObject *));
EXTDECLPROC(PyObject *, PyImport_ImportModule, (char *));
EXTDECLPROC(PyObject *, PyImport_AddModule, (char *));

EXTDECLPROC(int, PyObject_DelItem, (PyObject *, PyObject *));
EXTDECLPROC(int, PyObject_SetAttrString, (PyObject *, char *, PyObject *));
EXTDECLPROC(PyObject *, PyList_New, (int));
EXTDECLPROC(int, PyList_Append, (PyObject *, PyObject *));
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/scripts/pyi_filename.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------


# Filename nust not contain a directory
if __file__ != 'pyi_filename.py':
raise ValueError(__file__)
1 change: 1 addition & 0 deletions tests/functional/scripts/pyi_module_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import copy
import os
import sys
import subprocess
import xml.etree.ElementTree as ET
import xml.etree.cElementTree as cET
Expand Down
1 change: 1 addition & 0 deletions tests/functional/specs/several-scripts/check___name__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
assert __name__ == '__main__'
2 changes: 1 addition & 1 deletion tests/functional/specs/several-scripts1.spec
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name=app_name,
debug=False,
debug=True,
console=True)
coll = COLLECT(exe,
a.binaries,
Expand Down