Coverage for src / layout / process.py: 39%
71 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-21 16:36 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-21 16:36 +0000
1import os
2import shutil
3import sys
6def _if(item, env, ctx):
7 retval = item["if"]
8 if retval not in [True, False]:
9 retval = env.from_string(retval).render(**ctx)
10 return retval
13def _ensure_is_relative(orig_name, path, validpath):
14 # path must be a relative sub path of validpath
15 real_validpath = os.path.realpath(validpath)
16 real_path = os.path.realpath(path)
17 if not real_path.startswith(real_validpath): 17 ↛ 18line 17 didn't jump to line 18 because the condition on line 17 was never true
18 sys.exit(f'error: File "{orig_name}" is not subpath of the output path "{validpath}"')
21def _action_move(item, env, ctx, output_dir):
23 src = str(env.from_string(item["src"]).render(**ctx))
24 src = os.path.join(output_dir, src)
26 dst = str(env.from_string(item["dst"]).render(**ctx))
27 dst = os.path.join(output_dir, dst)
29 # if file does not exist, we don't care about subpath check
30 if _if(item, env, ctx):
31 if not os.path.exists(src):
32 return
33 _ensure_is_relative(src, src, output_dir)
34 _ensure_is_relative(dst, dst, output_dir)
35 shutil.move(src, dst)
36 elif item.get("else") == "remove":
37 if not os.path.exists(src):
38 return
39 _ensure_is_relative(src, src, output_dir)
40 os.remove(src)
43def _action_remove(item, env, ctx, output_dir):
44 if not _if(item, env, ctx):
45 return
47 for path in item["path"]:
48 orig_path = path
49 path = str(env.from_string(path).render(**ctx))
50 path = os.path.join(output_dir, path)
52 # if file does not exist, we don't care about subpath check
53 if os.path.islink(path) or os.path.isfile(path):
54 _ensure_is_relative(orig_path, path, output_dir)
55 os.remove(path)
56 elif os.path.isdir(path):
57 _ensure_is_relative(orig_path, path, output_dir)
58 shutil.rmtree(path)
61def _action_remove_nl(item, env, ctx, output_dir):
62 if not _if(item, env, ctx):
63 return
65 for path in item["path"]:
66 orig_path = path
67 path = str(env.from_string(path).render(**ctx))
68 path = os.path.join(output_dir, path)
70 _ensure_is_relative(orig_path, path, output_dir)
71 with open(path, encoding="UTF-8") as fd:
72 data = fd.read()
73 data = data.rstrip()
74 with open(path, mode="w", encoding="UTF-8") as fd:
75 fd.write(data)
78def _process(tpl, env, ctx, output_dir):
79 for item in tpl.actions:
80 action = item["action"]
81 if action == "move":
82 _action_move(item, env, ctx, output_dir)
83 elif action == "remove":
84 _action_remove(item, env, ctx, output_dir)
85 elif action == "remove-trailing-newline":
86 _action_remove_nl(item, env, ctx, output_dir)
87 # pragma: cover: off
88 else:
89 raise NotImplementedError("deadcode reached")
90 # pragma: cover: on
91 return tpl
94def process(tpl, env, ctx, **kwargs):
95 output_dir = kwargs["output_dir"]
97 items = reversed(tpl.includes)
98 for item in items:
99 _process(item, env, ctx, output_dir)
101 return tpl