Skip to content

Commit 760f885

Browse files
authored
Merge pull request #390 from itamarst/386.pyinstaller
Support for PyInstaller
2 parents 87c6f02 + 92e1e2f commit 760f885

File tree

6 files changed

+64
-9
lines changed

6 files changed

+64
-9
lines changed

docs/source/news.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Documentation:
1111
Features:
1212

1313
* Generating messages is much faster.
14+
* Eliot now works with PyInstaller. Thanks to Jean-Paul Calderone for the bug report. Fixes issue #386.
1415
* The testing infrastructure now has slightly more informative error messages. Thanks to Jean-Paul Calderone for the bug report. Fixes issue #373.
1516
* Added lower-level testing infrastructure—``eliot.testing.swap_logger`` and ``eliot.testing.check_for_errors``—which is useful for cases when the ``@capture_logging`` decorator is insufficient. For example, test methods that are async, or return Twisted ``Deferred``. See the :doc:`testing documentation<generating/testing>` for details. Thanks to Jean-Paul Calderone for the feature request. Fixes #364.
1617
* ``eliot.ValidationError``, as raised by e.g. ``capture_logging``, is now part of the public API. Fixed issue #146.

eliot/_traceback.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ def _get_traceback_no_io():
5454
"""
5555
Return a version of L{traceback} that doesn't do I/O.
5656
"""
57-
module = load_module(str("_traceback_no_io"), traceback)
57+
try:
58+
module = load_module(str("_traceback_no_io"), traceback)
59+
except NotImplementedError:
60+
# Can't fix the I/O problem, oh well:
61+
return traceback
5862

5963
class FakeLineCache(object):
6064
def checkcache(self, *args, **kwargs):

eliot/_util.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
from __future__ import unicode_literals
66

7+
import sys
78
from types import ModuleType
89

9-
from six import exec_, text_type as unicode
10+
from six import exec_, text_type as unicode, PY3
1011

1112

1213
def safeunicode(o):
@@ -52,9 +53,17 @@ def load_module(name, original_module):
5253
@return: A new, distinct module.
5354
"""
5455
module = ModuleType(name)
55-
path = original_module.__file__
56-
if path.endswith(".pyc") or path.endswith(".pyo"):
57-
path = path[:-1]
58-
with open(path) as f:
59-
exec_(f.read(), module.__dict__, module.__dict__)
56+
if PY3:
57+
import importlib.util
58+
spec = importlib.util.find_spec(original_module.__name__)
59+
source = spec.loader.get_code(original_module.__name__)
60+
else:
61+
if getattr(sys, "frozen", False):
62+
raise NotImplementedError("Can't load modules on Python 2 with PyInstaller")
63+
path = original_module.__file__
64+
if path.endswith(".pyc") or path.endswith(".pyo"):
65+
path = path[:-1]
66+
with open(path) as f:
67+
source = f.read()
68+
exec_(source, module.__dict__, module.__dict__)
6069
return module

eliot/prettyprint.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@ def _nicer_unicode_repr(o, original_repr=repr):
3333
else:
3434
return original_repr(o)
3535

36-
pprint = load_module(b"unicode_pprint", pprint)
37-
pprint.repr = _nicer_unicode_repr
36+
try:
37+
pprint = load_module(b"unicode_pprint", pprint)
38+
pprint.repr = _nicer_unicode_repr
39+
except NotImplementedError:
40+
# Oh well won't have nicer output.
41+
import pprint
42+
3843

3944
# Fields that all Eliot messages are expected to have:
4045
REQUIRED_FIELDS = {TASK_LEVEL_FIELD, TASK_UUID_FIELD, TIMESTAMP_FIELD}

eliot/tests/test_pyinstaller.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Test for pyinstaller compatibility."""
2+
3+
from __future__ import absolute_import
4+
5+
from unittest import TestCase, SkipTest
6+
from tempfile import mkdtemp, NamedTemporaryFile
7+
from subprocess import check_call, CalledProcessError
8+
import os
9+
10+
from six import PY2
11+
if PY2:
12+
FileNotFoundError = OSError
13+
14+
15+
class PyInstallerTests(TestCase):
16+
"""Make sure PyInstaller doesn't break Eliot."""
17+
18+
def setUp(self):
19+
try:
20+
check_call(["pyinstaller", "--help"])
21+
except (CalledProcessError, FileNotFoundError):
22+
raise SkipTest("Can't find pyinstaller.")
23+
24+
def test_importable(self):
25+
"""The Eliot package can be imported inside a PyInstaller packaged binary."""
26+
output_dir = mkdtemp()
27+
with NamedTemporaryFile(mode="w") as f:
28+
f.write("import eliot; import eliot.prettyprint\n")
29+
f.flush()
30+
check_call(
31+
["pyinstaller", "--distpath", output_dir,
32+
"-F", "-n", "importeliot", f.name]
33+
)
34+
check_call([os.path.join(output_dir, "importeliot")])

tox.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ basepython = pypy
2424

2525
[testenv:py27]
2626
deps = cffi
27+
pyinstaller==3.3
2728
basepython = python2.7
2829

2930
[testenv:py34]
@@ -38,6 +39,7 @@ deps = numpy
3839
[testenv:py36]
3940
basepython = python3.6
4041
deps = cffi
42+
pyinstaller
4143

4244
[testenv:py37]
4345
basepython = python3.7

0 commit comments

Comments
 (0)