Coverage for dirimport/gen.py: 89%
77 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-05 14:22 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-05 14:22 +0000
1import difflib
2import importlib
3import os
4import sys
5import types
6from logging import getLogger
8from jinja2 import Template
10log = getLogger(__name__)
12tmpl = Template("""# Code generated by dirimport - DO NOT EDIT.
13{%- for d in dirs %}
14from . import {{d}} # noqa
15{%- endfor %}
16{%- for f in files %}
17from .{{f}} import * # noqa
18{%- endfor %}
19""")
22def dig(dirname):
23 resd = {}
24 resf = []
25 for f in sorted(os.listdir(dirname)):
26 if f.startswith(("_", ".")):
27 continue
28 fpath = os.path.join(dirname, f)
29 if os.path.isdir(fpath):
30 x = dig(fpath)
31 if len(x[0]) == 0 and len(x[1]) == 0:
32 continue
33 resd[f] = x
34 elif os.path.isfile(fpath) and f.endswith(".py"): 34 ↛ 25line 34 didn't jump to line 25 because the condition on line 34 was always true
35 resf.append(f[:-3])
36 return resd, resf
39def generate(data, basename, filename="__init__.py"):
40 dirs, files = data
41 ofn = os.path.join(basename, filename)
42 if len(dirs) == 0 and len(files) == 0: 42 ↛ 43line 42 didn't jump to line 43 because the condition on line 42 was never true
43 return
44 with open(ofn, "w") as ofp:
45 print(tmpl.render(dirs=dirs.keys(), files=files).strip(), file=ofp)
46 for k, v in dirs.items():
47 generate(v, os.path.join(basename, k), filename)
50def diff(data, basename, filename="__init__.py"):
51 dirs, files = data
52 ofn = os.path.join(basename, filename)
53 if os.path.exists(ofn):
54 with open(ofn) as ofp:
55 orig = [line for line in (raw.strip() for raw in ofp) if line != ""]
56 else:
57 orig = []
58 newtxt = tmpl.render(dirs=dirs.keys(), files=files)
59 newdata = list(filter(lambda f: f.strip() != "", newtxt.split("\n")))
60 res = list(difflib.unified_diff(orig, newdata, fromfile=ofn + ".orig", tofile=ofn))
61 for k, v in dirs.items(): 61 ↛ 62line 61 didn't jump to line 62 because the loop on line 61 never started
62 res.extend(diff(v, os.path.join(basename, k), filename))
63 return res
66def clear(basename, filename="__init__.py"):
67 for root, dirs, files in os.walk(basename):
68 if filename in files: 68 ↛ 67line 68 didn't jump to line 67 because the condition on line 68 was always true
69 os.unlink(os.path.join(root, filename))
72def importdata(data, basename, rootdir="."):
73 dirs, files = data
74 res = types.ModuleType(basename)
75 sys.path.insert(0, rootdir)
76 log.debug("path %s", rootdir)
77 for f in files:
78 # from .f import *
79 log.debug("loading(f) %s %s", basename, f)
80 tmp = importlib.import_module(basename + "." + f)
81 log.debug("loaded: %s, file=%s", tmp, tmp.__file__)
82 for v in dir(tmp):
83 if v.startswith("_"):
84 continue
85 if hasattr(res, v): 85 ↛ 86line 85 didn't jump to line 86 because the condition on line 85 was never true
86 log.info("duplicate symbol %s %s %s", basename, f, v)
87 continue
88 obj = getattr(tmp, v)
89 log.debug("set attribute: %s.%s = %s", res, v, obj)
90 setattr(res, v, obj)
91 sys.path.pop(0)
92 for k, v in dirs.items(): 92 ↛ 93line 92 didn't jump to line 93 because the loop on line 92 never started
93 log.debug("loading(d) %s %s %s", basename, k, v)
94 setattr(res, k, importdata(v, basename + "." + k, rootdir))
95 return res
98def importall(basename):
99 data = dig(basename)
100 log.debug("dig %s: %s", basename, data)
101 return importdata(data, os.path.basename(basename), os.path.dirname(basename))