Last active
July 7, 2021 21:43
-
-
Save Alexhuszagh/a114239123b15428140db46b60b2223b to your computer and use it in GitHub Desktop.
Scripts to include and use to bundle Python imports from modules into a single source file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Scripts to convert pure-Python modules into a Python dictionary, which can then be reconverted | |
into Python modules, allowing distribution of code with complex imports in a single script. | |
This code is unlicensed and free and unencumbered code in the public domain. | |
''' | |
import os | |
import sys | |
import types | |
class Module: | |
def __init__(self, name, source=None, path=None, modules=None): | |
self.name = name | |
self.source = source | |
self.path = path | |
self.modules = modules | |
def from_file(name, path): | |
return Module(name, open(path).read(), path) | |
def from_dir(name, path): | |
return Module(name, None, path, []) | |
def __repr__(self): | |
return f'Module(name={repr(self.name)}, source={repr(self.source)}, path={repr(self.path)}, modules={self.modules})' | |
def to_dict(self): | |
data = { 'name': self.name } | |
if self.source is not None: | |
data['source'] = self.source | |
if self.path is not None: | |
data['path'] = self.path | |
if self.modules is not None: | |
data['modules'] = [i.to_dict() for i in self.modules] | |
return data | |
@staticmethod | |
def from_dict(data): | |
modules = None | |
if 'modules' in data: | |
modules = [Module.from_dict(i) for i in data.pop('modules')] | |
return Module(**data, modules=modules) | |
def get_modname(parent, module): | |
if parent is None: | |
return module | |
if module == '__init__': | |
return parent | |
return f'{parent}.{module}' | |
def compile_module(module, parent=None): | |
modname = get_modname(parent, module.name) | |
mod = types.ModuleType(modname) | |
exec(module.source, mod.__dict__) | |
path = os.path.realpath(module.path) | |
mod.__path__ = os.path.dirname(path) | |
mod.__file__ = path | |
sys.modules[modname] = mod | |
globals()[modname] = mod | |
return mod | |
def compile_module_recursive(package, parent=None): | |
# Need to do this recursively. | |
mod = compile_module(package, parent) | |
if not package.modules: | |
return mod | |
for submodule in package.modules: | |
submod = compile_module_recursive(submodule, parent=get_modname(parent, package.name)) | |
if not hasattr(mod, submodule.name): | |
setattr(mod, submodule.name, submod) | |
return mod | |
def read_module_recursive(directory, parent=None): | |
# The module order is first the: | |
# 1. All submodules | |
# 2. Then definitions inside | |
# Then, need to define `__name__`, `__path__` and `__file__`. | |
cwd = os.getcwd() | |
realpath = os.path.realpath(directory) | |
parent_dir = os.path.dirname(realpath) | |
base_dir = os.path.basename(realpath) | |
if parent is None: | |
parent = Module.from_dir(base_dir, realpath) | |
os.chdir(realpath) | |
for entry in os.listdir(realpath): | |
path = os.path.join(realpath, entry) | |
if os.path.isfile(path): | |
name, ext = os.path.splitext(entry) | |
if not ext == '.py': | |
continue | |
if name == '__init__': | |
parent.path = path | |
parent.source = open(path).read() | |
else: | |
parent.modules.append(Module.from_file(name, path)) | |
elif os.path.isdir(path): | |
if entry == '__pycache__': | |
continue | |
path = os.path.join(realpath, entry) | |
# Must have processed __init__.py | |
if not os.path.isfile(f'{path}/__init__.py'): | |
continue | |
module = Module.from_dir(entry, path) | |
parent.modules.append(module) | |
read_module_recursive(entry, module) | |
os.chdir(cwd) | |
return parent | |
# SAMPLE USE | |
# ---------- | |
# module = read_module_recursive('mylib') | |
# data = module.to_dict() # can store as a Python dict. | |
# module = Module.from_dict(data) | |
# compile_module_recursive(module) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment