device/trezor: python2 compatibility - bundle dependencies

This commit is contained in:
Dusan Klinec 2018-11-29 04:22:52 +01:00
parent 9cf636af69
commit 65b9bca70e
No known key found for this signature in database
GPG key ID: 6337E118CCBCE103
5 changed files with 248 additions and 8 deletions

View file

@ -10,14 +10,28 @@ Install `protoc` for your distribution. Requirements:
Soft requirement: Python 3, can be easily installed with [pyenv]. Soft requirement: Python 3, can be easily installed with [pyenv].
If Python 3 is used there are no additional python dependencies.
### Python 2 Since Cmake 3.12 the `FindPython` module is used to locate the Python
interpreter in your system. It preferably searches for Python 3, if none
is found, it searches for Python 2.
Workaround if there is no Python3 available: Lower version of the cmake uses another module which does not guarantee
ordering. If you want to override the selected python you can do it in
the following way:
```bash ```bash
pip install backports.tempfile export TREZOR_PYTHON=`which python3`
``` ```
### Python 2.7+
Python 3 has `tempfile.TemporaryDirectory` available but Python 2 lacks
this class so the message generation code uses `backports.tempfile` package
bundled in the repository.
The minimal Python versions are 2.7 and 3.4
### Regenerate messages ### Regenerate messages

View file

@ -14,12 +14,18 @@ import hashlib
try: try:
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
except: except:
# Py2 backward compatibility, optionally installed by user # Py2 backward compatibility, using bundled sources.
# pip install backports.tempfile # Original source: pip install backports.tempfile
try: try:
from backports.tempfile import TemporaryDirectory # Try bundled python version
import sys
sys.path.append(os.path.dirname(__file__))
from py2backports.tempfile import TemporaryDirectory
except: except:
raise EnvironmentError('TemporaryDirectory could not be imported. Try: pip install backports.tempfile') raise EnvironmentError('Python 2.7+ or 3.4+ is required. '
'TemporaryDirectory is not available in Python 2.'
'Try to specify python to use, e.g.: "export TREZOR_PYTHON=`which python3`"')
AUTO_HEADER = "# Automatically generated by pb2cpp\n" AUTO_HEADER = "# Automatically generated by pb2cpp\n"

View file

@ -0,0 +1,72 @@
"""
https://github.com/pjdelport/backports.tempfile/blob/master/src/backports/tempfile.py
Partial backport of Python 3.5's tempfile module:
TemporaryDirectory
Backport modifications are marked with marked with "XXX backport".
"""
from __future__ import absolute_import
import sys
import warnings as _warnings
from shutil import rmtree as _rmtree
from py2backports.weakref import finalize
# XXX backport: Rather than backporting all of mkdtemp(), we just create a
# thin wrapper implementing its Python 3.5 signature.
if sys.version_info < (3, 5):
from tempfile import mkdtemp as old_mkdtemp
def mkdtemp(suffix=None, prefix=None, dir=None):
"""
Wrap `tempfile.mkdtemp()` to make the suffix and prefix optional (like Python 3.5).
"""
kwargs = {k: v for (k, v) in
dict(suffix=suffix, prefix=prefix, dir=dir).items()
if v is not None}
return old_mkdtemp(**kwargs)
else:
from tempfile import mkdtemp
# XXX backport: ResourceWarning was added in Python 3.2.
# For earlier versions, fall back to RuntimeWarning instead.
_ResourceWarning = RuntimeWarning if sys.version_info < (3, 2) else ResourceWarning
class TemporaryDirectory(object):
"""Create and return a temporary directory. This has the same
behavior as mkdtemp but can be used as a context manager. For
example:
with TemporaryDirectory() as tmpdir:
...
Upon exiting the context, the directory and everything contained
in it are removed.
"""
def __init__(self, suffix=None, prefix=None, dir=None):
self.name = mkdtemp(suffix, prefix, dir)
self._finalizer = finalize(
self, self._cleanup, self.name,
warn_message="Implicitly cleaning up {!r}".format(self))
@classmethod
def _cleanup(cls, name, warn_message):
_rmtree(name)
_warnings.warn(warn_message, _ResourceWarning)
def __repr__(self):
return "<{} {!r}>".format(self.__class__.__name__, self.name)
def __enter__(self):
return self.name
def __exit__(self, exc, value, tb):
self.cleanup()
def cleanup(self):
if self._finalizer.detach():
_rmtree(self.name)

View file

@ -0,0 +1,148 @@
"""
https://github.com/pjdelport/backports.weakref/blob/master/src/backports/weakref.py
Partial backport of Python 3.6's weakref module:
finalize (new in Python 3.4)
Backport modifications are marked with "XXX backport".
"""
from __future__ import absolute_import
import itertools
import sys
from weakref import ref
__all__ = ['finalize']
class finalize(object):
"""Class for finalization of weakrefable objects
finalize(obj, func, *args, **kwargs) returns a callable finalizer
object which will be called when obj is garbage collected. The
first time the finalizer is called it evaluates func(*arg, **kwargs)
and returns the result. After this the finalizer is dead, and
calling it just returns None.
When the program exits any remaining finalizers for which the
atexit attribute is true will be run in reverse order of creation.
By default atexit is true.
"""
# Finalizer objects don't have any state of their own. They are
# just used as keys to lookup _Info objects in the registry. This
# ensures that they cannot be part of a ref-cycle.
__slots__ = ()
_registry = {}
_shutdown = False
_index_iter = itertools.count()
_dirty = False
_registered_with_atexit = False
class _Info(object):
__slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
def __init__(self, obj, func, *args, **kwargs):
if not self._registered_with_atexit:
# We may register the exit function more than once because
# of a thread race, but that is harmless
import atexit
atexit.register(self._exitfunc)
finalize._registered_with_atexit = True
info = self._Info()
info.weakref = ref(obj, self)
info.func = func
info.args = args
info.kwargs = kwargs or None
info.atexit = True
info.index = next(self._index_iter)
self._registry[self] = info
finalize._dirty = True
def __call__(self, _=None):
"""If alive then mark as dead and return func(*args, **kwargs);
otherwise return None"""
info = self._registry.pop(self, None)
if info and not self._shutdown:
return info.func(*info.args, **(info.kwargs or {}))
def detach(self):
"""If alive then mark as dead and return (obj, func, args, kwargs);
otherwise return None"""
info = self._registry.get(self)
obj = info and info.weakref()
if obj is not None and self._registry.pop(self, None):
return (obj, info.func, info.args, info.kwargs or {})
def peek(self):
"""If alive then return (obj, func, args, kwargs);
otherwise return None"""
info = self._registry.get(self)
obj = info and info.weakref()
if obj is not None:
return (obj, info.func, info.args, info.kwargs or {})
@property
def alive(self):
"""Whether finalizer is alive"""
return self in self._registry
@property
def atexit(self):
"""Whether finalizer should be called at exit"""
info = self._registry.get(self)
return bool(info) and info.atexit
@atexit.setter
def atexit(self, value):
info = self._registry.get(self)
if info:
info.atexit = bool(value)
def __repr__(self):
info = self._registry.get(self)
obj = info and info.weakref()
if obj is None:
return '<%s object at %#x; dead>' % (type(self).__name__, id(self))
else:
return '<%s object at %#x; for %r at %#x>' % \
(type(self).__name__, id(self), type(obj).__name__, id(obj))
@classmethod
def _select_for_exit(cls):
# Return live finalizers marked for exit, oldest first
L = [(f,i) for (f,i) in cls._registry.items() if i.atexit]
L.sort(key=lambda item:item[1].index)
return [f for (f,i) in L]
@classmethod
def _exitfunc(cls):
# At shutdown invoke finalizers for which atexit is true.
# This is called once all other non-daemonic threads have been
# joined.
reenable_gc = False
try:
if cls._registry:
import gc
if gc.isenabled():
reenable_gc = True
gc.disable()
pending = None
while True:
if pending is None or finalize._dirty:
pending = cls._select_for_exit()
finalize._dirty = False
if not pending:
break
f = pending.pop()
try:
# gc is disabled, so (assuming no daemonic
# threads) the following is the only line in
# this function which might trigger creation
# of a new finalizer
f()
except Exception:
sys.excepthook(*sys.exc_info())
assert f not in cls._registry
finally:
# prevent any more finalizers from executing during shutdown
finalize._shutdown = True
if reenable_gc:
gc.enable()