Skip to content

Commit 0cc6915

Browse files
authored
Merge pull request #108 from dx-77/issue-107
fix: Change PYE finder (hook) logic from meta hook to path hook
2 parents 41913e7 + 0d2537f commit 0cc6915

File tree

1 file changed

+32
-60
lines changed

1 file changed

+32
-60
lines changed

src/pyconcrete/__init__.py

+32-60
Original file line numberDiff line numberDiff line change
@@ -16,52 +16,32 @@
1616

1717
import imp
1818
import marshal
19+
import struct
1920
import sys
20-
from os.path import exists, isdir, join
21+
from importlib._bootstrap_external import _get_supported_file_loaders
22+
from importlib.machinery import FileFinder, SourceFileLoader, SOURCE_SUFFIXES
23+
from . import _pyconcrete # noqa: E402
24+
2125

22-
EXT_PY = '.py'
23-
EXT_PYC = '.pyc'
24-
EXT_PYD = '.pyd'
2526
EXT_PYE = '.pye'
2627

2728
__all__ = ["info"]
2829

29-
from . import _pyconcrete # noqa: E402
3030

3131
info = _pyconcrete.info
3232
encrypt_file = _pyconcrete.encrypt_file
3333
decrypt_file = _pyconcrete.decrypt_file
3434
decrypt_buffer = _pyconcrete.decrypt_buffer
3535

36+
# We need to modify SOURCE_SUFFIXES, because it used in importlib.machinery.all_suffixes function which
37+
# called by inspect.getmodulename and we need to be able to detect the module name relative to .pye files
38+
# because .py can be deleted by us
39+
SOURCE_SUFFIXES.append(EXT_PYE)
3640

37-
class PyeLoader(object):
38-
def __init__(self, is_pkg, pkg_path, full_path):
39-
self.is_pkg = is_pkg
40-
self.pkg_path = pkg_path
41-
self.full_path = full_path
42-
with open(full_path, 'rb') as f:
43-
self.data = f.read()
44-
45-
def new_module(self, fullname, path, package_path):
46-
m = imp.new_module(fullname)
47-
m.__file__ = path
48-
m.__loader__ = self
49-
if self.is_pkg:
50-
m.__path__ = [package_path]
51-
52-
if "__name__" not in m.__dict__:
53-
m.__name__ = fullname
54-
55-
return m
56-
57-
def load_module(self, fullname):
58-
if fullname in sys.modules: # skip reload by now ...
59-
return sys.modules[fullname]
60-
61-
data = decrypt_buffer(self.data) # decrypt pye
62-
63-
self._validate_version(data)
6441

42+
class PyeLoader(SourceFileLoader):
43+
@property
44+
def magic(self):
6545
if sys.version_info >= (3, 7):
6646
# reference python source code
6747
# python/Lib/importlib/_bootstrap_external.py _code_to_timestamp_pyc() & _code_to_hash_pyc()
@@ -77,47 +57,39 @@ def load_module(self, fullname):
7757
# reference http://stackoverflow.com/questions/1830727/how-to-load-compiled-python-modules-from-memory
7858
# MAGIC + TIMESTAMP
7959
magic = 8
80-
81-
code = marshal.loads(data[magic:])
82-
83-
m = self.new_module(fullname, self.full_path, self.pkg_path)
84-
sys.modules[fullname] = m
85-
exec(code, m.__dict__)
86-
return m
87-
88-
def is_package(self, fullname):
89-
return self.is_pkg
60+
return magic
9061

9162
@staticmethod
9263
def _validate_version(data):
9364
magic = imp.get_magic()
9465
ml = len(magic)
9566
if data[:ml] != magic:
96-
import struct
97-
9867
# convert little-endian byte string to unsigned short
9968
py_magic = struct.unpack('<H', magic[:2])[0]
10069
pye_magic = struct.unpack('<H', data[:2])[0]
10170
raise ValueError("Python version doesn't match with magic: python(%d) != pye(%d)" % (py_magic, pye_magic))
10271

72+
def get_code(self, fullname):
73+
if not self.path.endswith(EXT_PYE):
74+
return super().get_code(fullname)
75+
76+
path = self.get_filename(fullname)
77+
data = decrypt_buffer(self.get_data(path))
78+
self._validate_version(data)
79+
return marshal.loads(data[self.magic:])
80+
81+
def get_source(self, fullname):
82+
if self.path.endswith(EXT_PYE):
83+
return None
84+
return super().get_source(fullname)
85+
10386

104-
class PyeMetaPathFinder(object):
105-
def find_module(self, fullname, path=None):
106-
mod_name = fullname.split('.')[-1]
107-
paths = path if path else sys.path
87+
loader_details = [(PyeLoader, SOURCE_SUFFIXES)] + _get_supported_file_loaders()
10888

109-
for trypath in paths:
110-
mod_path = join(trypath, mod_name)
111-
is_pkg = isdir(mod_path)
112-
if is_pkg:
113-
full_path = join(mod_path, '__init__' + EXT_PYE)
114-
pkg_path = mod_path
115-
else:
116-
full_path = mod_path + EXT_PYE
117-
pkg_path = trypath
11889

119-
if exists(full_path):
120-
return PyeLoader(is_pkg, pkg_path, full_path)
90+
def install():
91+
sys.path_importer_cache.clear()
92+
sys.path_hooks.insert(0, FileFinder.path_hook(*loader_details))
12193

12294

123-
sys.meta_path.insert(0, PyeMetaPathFinder())
95+
install()

0 commit comments

Comments
 (0)