diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..530504e7d80e9830fef5e947688735bee7235d21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +/test/build \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..ac5755a61d2db376b634b405a962f397d22537dd --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +all: + $(MAKE) -C test --no-print-directory + +.PHONY: SRPM +SRPM: + rpmbuild --define "_sourcedir $$(pwd)" -bs apa.spec diff --git a/apa b/apa3 similarity index 84% rename from apa rename to apa3 index 78945153c9d7da2d3ccac70f4d65fc450fd66b9a..f9fcfa3a1741491807ceee1baa8c290989c7501b 100755 --- a/apa +++ b/apa3 @@ -2,7 +2,7 @@ """ Anders Python Archiver -Copyright (C) 2004-2019 Anders Blomdell <anders.blomdell@control.lth.se> +Copyright (C) 2004-2023 Anders Blomdell <anders.blomdell@control.lth.se> A small utility program to join a number of python modules into a single executable python program. @@ -139,37 +139,45 @@ To extract as individual files: print("}", file=apa) print(""" -class Importer: - \"\"\"Loads modules from local code dictionary - \"\"\" - def __init__(self, code): - self.code = code; - self.path = "<%s>" % str(self.__class__) - if not self.path in sys.path: - sys.path.insert(0, self.path) +import sys +import os +import importlib.abc +import importlib.util + +class Finder(importlib.abc.MetaPathFinder): - def __call__(self, path): - if path == self.path: - return self + def __init__(self, loader, code): + self.loader = loader + self.code = code + pass + + def find_spec(self, fullname, path=None, target=None): + if fullname in self.code: + return importlib.util.spec_from_loader(fullname, self.loader) return None + + pass + +class Loader(importlib.abc.Loader): + + def __init__(self, code): + self.code = code; + self._path = "APA[%s]" % os.path.realpath(sys.argv[0]) + pass - def find_module(self, fullname): - try: - self.code[fullname] - return self - except: - return None - - def load_module(self, fullname): - mod = sys.modules.setdefault(fullname, types.ModuleType(fullname)) - (filename, src) = self.code[fullname] + def create_module(self, spec): + return None + + def exec_module(self, module): + filename,src = self.code[module.__name__] + mod = sys.modules.setdefault(module.__name__, module) mod.__file__ = filename mod.__loader__ = self - mod.__path__ = [ self.path ] + mod.__path__ = [ self._path ] global BAD BAD = None try: - code = compile(src, "%s/%s" % (self.path, filename), 'exec') + code = compile(src, "%s/%s" % (self._path, filename), 'exec') exec(code, mod.__dict__) except SystemExit as e: # Silently propagate exit @@ -182,7 +190,8 @@ class Importer: def is_package(self, fullname): (filename, src) = self.code[fullname] - return True + (name, extension) = os.path.splitext(os.path.basename(filename)) + return name == '__init__' def get_code(self, fullname): (filename, src) = self.code[fullname] @@ -192,11 +201,13 @@ class Importer: def get_source(self, fullname): (filename, src) = self.code[fullname] return src - + def get_filename(self, fullname): (filename, src) = self.code[fullname] return filename - + + pass + def extract(dest='.'): \"\"\"Extracts archived files in dest directory ('.' is default)\"\"\" import os @@ -215,12 +226,14 @@ def extract(dest='.'): if __name__ == '__main__': import sys - importer = Importer(code) - sys.path_hooks.append(importer) - import types + loader = Loader(code) + finder = Finder(loader, code) + sys.meta_path.append(finder) try: # This will start actual execution - importer.load_module("__main__") + spec = finder.find_spec("__main__") + m = importlib.util.module_from_spec(spec) + loader.exec_module(m) except SystemExit as e: raise e except: diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..8402b7679fce526e442b4aa4d9263e4272923a16 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,20 @@ +all: TEST_TRACEBACK TEST_MODULE + +.PHONY: TEST_TRACEBACK +TEST_TRACEBACK: + -mkdir -p build + ../apa -o build/test_traceback test_traceback.py + ./build/test_traceback \ + && echo "Expected failure not gotten, so BAD" && exit 1 \ + || echo "Expected failure gotten, so OK" + + +.PHONY: TEST_MODULE +TEST_MODULE: + -mkdir -p build + ../apa -o build/test_module \ + test_module.py \ + module/__init__.py \ + module/a.py \ + module/b.py + ./build/test_module diff --git a/test/module/__init__.py b/test/module/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d8bb652afdb296b175a2390336c910de7e8d64a6 --- /dev/null +++ b/test/module/__init__.py @@ -0,0 +1,3 @@ +print(__name__) + +from . import a diff --git a/test/module/a.py b/test/module/a.py new file mode 100644 index 0000000000000000000000000000000000000000..3f8ff89d3ad345e04ca065cebaf954c85d2cd1ed --- /dev/null +++ b/test/module/a.py @@ -0,0 +1,2 @@ +print(__name__) +from . import b diff --git a/test/module/b.py b/test/module/b.py new file mode 100644 index 0000000000000000000000000000000000000000..559a4c5a8675adb533493d0c4824e0378c04085b --- /dev/null +++ b/test/module/b.py @@ -0,0 +1,2 @@ +print(__name__) +from . import a diff --git a/test/test_module.py b/test/test_module.py new file mode 100644 index 0000000000000000000000000000000000000000..392b71878de772a5e22af49756b479507148c169 --- /dev/null +++ b/test/test_module.py @@ -0,0 +1,3 @@ +#!/usr/bin/python3 + +import module diff --git a/test/test_traceback.py b/test/test_traceback.py new file mode 100755 index 0000000000000000000000000000000000000000..11906cdc6299d91d80a5e4df90801ced9f72531e --- /dev/null +++ b/test/test_traceback.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +MAX_DEPTH = 5 + +def a(depth): + if depth > MAX_DEPTH: + raise Exception("Max depth exceeded") + b(depth+1) + pass + +def b(depth): + if depth > MAX_DEPTH: + raise Exception("Max depth exceeded") + c(depth+1) + pass + +def c(depth): + if depth > MAX_DEPTH: + raise Exception("Max depth exceeded") + a(depth+1) + pass + +a(1)