-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathparam.py
125 lines (100 loc) · 3.41 KB
/
param.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python
# file: param.py
# vim:fileencoding=utf-8:ft=python
#
# Copyright © 2019 R.F. Smith <[email protected]>
# Created: 2019-12-07T13:30:37+0100
# Last modified: 2020-07-18T00:46:55+0200
"""Executes or evaluates Python code between <> on a single line in a file.
Usage: param.py input output [var=value ...]
Values defined on the command line override those given in the input file.
For example, <x=3/4> will be replaced by x=3/4, while <x> will be replaced by
0.75 and <2*x> will be replaced by 1.5.
The math constants e and pi are recognized without having to be imported, as
are some math functions;
* sin, cos, tan (taking degrees as arguments, not radians).
* asin, acos, atan (returning degrees as results).
* log, log2 and log10
* ceil, floor
* pow, sqrt
This script is meant for *relatively simple* parameter substitutions and
calculations.
"""
import logging
import math
import re
import os
import sys
def main(args):
logging.basicConfig(level="INFO", format="%(levelname)s: %(message)s")
if len(args) < 2:
print(__doc__)
exit(0)
# Save file names
infname, outfname = args[0], args[1]
# Process arguments
overrides = getvars(args[2:])
lines = readfile(infname)
globvar = mkglobals()
writefile(outfname, lines, globvar, overrides)
def getvars(args):
vdict = {}
for arg in args:
if "=" not in arg:
logging.warning("no “=” in “{arg}”; skipped")
continue
exec(arg, None, vdict)
return vdict
def readfile(path):
with open(path) as f:
lines = f.readlines()
return list(enumerate(lines, start=1))
def mkglobals():
rv = {"__builtins__": None}
def sin(deg):
return math.sin(math.radians(deg))
def asin(num):
return math.degrees(math.asin(num))
def cos(deg):
return math.cos(math.radians(deg))
def acos(num):
return math.degrees(math.acos(num))
def tan(deg):
return math.tan(math.radians(deg))
def atan(num):
return math.degrees(math.atan(num))
for k in ["e", "pi", "log", "log2", "log10", "floor", "ceil", "pow", "sqrt"]:
rv[k] = eval("math." + k)
for k in ["sin", "asin", "cos", "acos", "tan", "atan", "round"]:
rv[k] = eval(k)
return rv
def writefile(path, lines, globvar, overrides):
locvar = {k: v for k, v in overrides.items()}
if path == "-":
outf = sys.stdout
else:
outf = open(path, "w")
outf.reconfigure(newline=os.linesep)
for num, line in lines:
outline = line
expr = re.findall("<[^@<>]*>", line)
for e in expr:
if "=" in e:
rep = e[1:-1]
exec(e[1:-1], globvar, locvar)
for k in overrides.keys():
if k in locvar and locvar[k] != overrides[k]:
locvar[k] = overrides[k]
logging.info(f"{k} overridden by command line on line {num}")
rep = f"{k} = {overrides[k]}"
outline = outline.replace(e, rep)
else:
try:
res = str(eval(e[1:-1], globvar, locvar))
outline = outline.replace(e, res)
except (NameError, TypeError) as err:
logging.error(f"line {num}, evaluating {e} " + str(err))
outf.write(outline)
outf.close()
if __name__ == "__main__":
main(sys.argv[1:])