forked from pyinstaller/pyinstaller
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
main: Add informative message what do to if RecurrsionError occurs.
See pyinstaller#4406
- Loading branch information
Showing
5 changed files
with
130 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#----------------------------------------------------------------------------- | ||
# Copyright (c) 2013-2020, PyInstaller Development Team. | ||
# | ||
# Distributed under the terms of the GNU General Public License (version 2 | ||
# or later) with exception for distributing the bootloader. | ||
# | ||
# The full license is in the file COPYING.txt, distributed with this software. | ||
# | ||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception) | ||
#----------------------------------------------------------------------------- | ||
|
||
|
||
msg = """ | ||
============================================================= | ||
A RecursionError (maximum recursion depth exceeded) occurred. | ||
For working around please follow these instructions | ||
============================================================= | ||
1. In your program's .spec file add this line near the top:: | ||
import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5) | ||
2. Build your program by running PyInstaller with the .spec file as | ||
argument:: | ||
pyinstaller myprog.spec | ||
3. If this fails, you most probably hit an endless recursion in | ||
PyInstaller. Please try to track this down has far as possible, | ||
create a minimal example so we can reproduce and open an issue at | ||
https://github.com/pyinstaller/pyinstaller/issues following the | ||
instructions in the issue template. Many thanks. | ||
Explanation: Python's stack-limit is a safety-belt against endless recursion, | ||
eating up memory. PyInstaller imports modules recursively. If the structure | ||
how modules are imported within your program is awkward, this leads to the | ||
nesting being too deep and hitting Python's stack-limit. | ||
With the default recursion limit (1000), the recursion error occurs at about | ||
115 nested imported, with limit 2000 at about 240, with limit 5000 at about | ||
660. | ||
""" | ||
|
||
|
||
def raise_with_msg(): | ||
raise SystemExit(msg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add informative message what do to if RecurrsionError occurs. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add informative message what do to if RecurrsionError occurs. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#----------------------------------------------------------------------------- | ||
# Copyright (c) 2005-2020, PyInstaller Development Team. | ||
# | ||
# Distributed under the terms of the GNU General Public License (version 2 | ||
# or later) with exception for distributing the bootloader. | ||
# | ||
# The full license is in the file COPYING.txt, distributed with this software. | ||
# | ||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception) | ||
#----------------------------------------------------------------------------- | ||
|
||
import pytest | ||
|
||
from PyInstaller.lib.modulegraph import modulegraph | ||
from PyInstaller import configure | ||
from PyInstaller import __main__ as pyi_main | ||
|
||
|
||
@pytest.fixture | ||
def large_import_chain(tmpdir): | ||
pkg = tmpdir.join('pkg') | ||
pkg.join('__init__.py').ensure().write('from . import a') | ||
mod = None | ||
for alpha in "abcdefg": | ||
if mod: | ||
# last module of prior sub-pkg imports this package | ||
mod.write("import pkg.%s" % alpha) | ||
subpkg = pkg.join(alpha).mkdir() | ||
subpkg.join('__init__.py').write('from . import %s000' % alpha) | ||
for num in range(250): | ||
# module importing its next sibling | ||
mod = subpkg.join("%s%03i.py" % (alpha, num)) | ||
mod.write("from . import %s%03i" % (alpha, num + 1)) | ||
script = tmpdir.join('script.py') | ||
script.write('import pkg') | ||
return [str(tmpdir)], str(script) | ||
|
||
|
||
def test_recursion_to_deep(large_import_chain): | ||
""" | ||
modulegraph is recursive and thus triggers RecursionError if | ||
nesting of imported modules is too deep. This can be worked around | ||
by increasing recursion limit. | ||
With the default recursion limit (1000), the recursion error | ||
occurs at about 115 modules, with limit 2000 (as tested below) at | ||
about 240 modules, with limit 5000 at about 660 modules. | ||
""" | ||
path, script = large_import_chain | ||
mg = modulegraph.ModuleGraph(path) | ||
# Increase recursion limit to 5 times of the default. Given the | ||
# module import chain created above this still should fail. | ||
with pytest.raises(RecursionError): | ||
mg.run_script(str(script)) | ||
|
||
|
||
def test_RecursionError_prints_message(tmpdir, large_import_chain, | ||
monkeypatch): | ||
""" | ||
modulegraph is recursive and thus triggers RecursionError if | ||
nesting of imported modules is too deep. Ensure a respective | ||
informative message is printed if recursion error occurs. | ||
""" | ||
path, script = large_import_chain | ||
|
||
default_args = [ | ||
'--specpath', str(tmpdir), | ||
'--distpath', str(tmpdir.join("dist")), | ||
'--workpath', str(tmpdir.join("build")), | ||
'--path', str(tmpdir), | ||
] | ||
|
||
pyi_args = [script] + default_args | ||
PYI_CONFIG = configure.get_config(upx_dir=None) | ||
PYI_CONFIG['cachedir'] = str(tmpdir) | ||
|
||
with pytest.raises(SystemExit) as execinfo: | ||
pyi_main.run(pyi_args, PYI_CONFIG) | ||
assert "sys.setrecursionlimit" in str(execinfo.value) |