Skip to content

Commit

Permalink
Merge pull request #390 from itamarst/386.pyinstaller
Browse files Browse the repository at this point in the history
Support for PyInstaller
  • Loading branch information
itamarst authored Mar 20, 2019
2 parents 87c6f02 + 92e1e2f commit 760f885
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 9 deletions.
1 change: 1 addition & 0 deletions docs/source/news.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Documentation:
Features:

* Generating messages is much faster.
* Eliot now works with PyInstaller. Thanks to Jean-Paul Calderone for the bug report. Fixes issue #386.
* The testing infrastructure now has slightly more informative error messages. Thanks to Jean-Paul Calderone for the bug report. Fixes issue #373.
* 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.
* ``eliot.ValidationError``, as raised by e.g. ``capture_logging``, is now part of the public API. Fixed issue #146.
Expand Down
6 changes: 5 additions & 1 deletion eliot/_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ def _get_traceback_no_io():
"""
Return a version of L{traceback} that doesn't do I/O.
"""
module = load_module(str("_traceback_no_io"), traceback)
try:
module = load_module(str("_traceback_no_io"), traceback)
except NotImplementedError:
# Can't fix the I/O problem, oh well:
return traceback

class FakeLineCache(object):
def checkcache(self, *args, **kwargs):
Expand Down
21 changes: 15 additions & 6 deletions eliot/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

from __future__ import unicode_literals

import sys
from types import ModuleType

from six import exec_, text_type as unicode
from six import exec_, text_type as unicode, PY3


def safeunicode(o):
Expand Down Expand Up @@ -52,9 +53,17 @@ def load_module(name, original_module):
@return: A new, distinct module.
"""
module = ModuleType(name)
path = original_module.__file__
if path.endswith(".pyc") or path.endswith(".pyo"):
path = path[:-1]
with open(path) as f:
exec_(f.read(), module.__dict__, module.__dict__)
if PY3:
import importlib.util
spec = importlib.util.find_spec(original_module.__name__)
source = spec.loader.get_code(original_module.__name__)
else:
if getattr(sys, "frozen", False):
raise NotImplementedError("Can't load modules on Python 2 with PyInstaller")
path = original_module.__file__
if path.endswith(".pyc") or path.endswith(".pyo"):
path = path[:-1]
with open(path) as f:
source = f.read()
exec_(source, module.__dict__, module.__dict__)
return module
9 changes: 7 additions & 2 deletions eliot/prettyprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@ def _nicer_unicode_repr(o, original_repr=repr):
else:
return original_repr(o)

pprint = load_module(b"unicode_pprint", pprint)
pprint.repr = _nicer_unicode_repr
try:
pprint = load_module(b"unicode_pprint", pprint)
pprint.repr = _nicer_unicode_repr
except NotImplementedError:
# Oh well won't have nicer output.
import pprint


# Fields that all Eliot messages are expected to have:
REQUIRED_FIELDS = {TASK_LEVEL_FIELD, TASK_UUID_FIELD, TIMESTAMP_FIELD}
Expand Down
34 changes: 34 additions & 0 deletions eliot/tests/test_pyinstaller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Test for pyinstaller compatibility."""

from __future__ import absolute_import

from unittest import TestCase, SkipTest
from tempfile import mkdtemp, NamedTemporaryFile
from subprocess import check_call, CalledProcessError
import os

from six import PY2
if PY2:
FileNotFoundError = OSError


class PyInstallerTests(TestCase):
"""Make sure PyInstaller doesn't break Eliot."""

def setUp(self):
try:
check_call(["pyinstaller", "--help"])
except (CalledProcessError, FileNotFoundError):
raise SkipTest("Can't find pyinstaller.")

def test_importable(self):
"""The Eliot package can be imported inside a PyInstaller packaged binary."""
output_dir = mkdtemp()
with NamedTemporaryFile(mode="w") as f:
f.write("import eliot; import eliot.prettyprint\n")
f.flush()
check_call(
["pyinstaller", "--distpath", output_dir,
"-F", "-n", "importeliot", f.name]
)
check_call([os.path.join(output_dir, "importeliot")])
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ basepython = pypy

[testenv:py27]
deps = cffi
pyinstaller==3.3
basepython = python2.7

[testenv:py34]
Expand All @@ -38,6 +39,7 @@ deps = numpy
[testenv:py36]
basepython = python3.6
deps = cffi
pyinstaller

[testenv:py37]
basepython = python3.7
Expand Down

0 comments on commit 760f885

Please sign in to comment.