-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbatch.py
171 lines (144 loc) · 3.95 KB
/
batch.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/env python
'''
shell front end to run a set of batch codes
from python, with variable substitution from
a metedata file
'''
__author__ = "P Lewis"
__copyright__ = "Copyright 2020 P Lewis"
__license__ = "GPLv3"
__email__ = "p.lewis@ucl.ac.uk"
# this should be robust to OS
# https://stackoverflow.com/questions/14894993/running-windows-shell-commands-with-python
from subprocess import check_output
import yaml
import json
import os
import sys
import getopt
from pathlib import Path
from pandas.io.json._normalize import nested_to_record
from get_env import getEnv
class Batch():
'''
run batch recipe from python
with variable substitution from meta
'''
def __init__(self,scripts=['start',('conda-recipe.sh',),'postBuild'],
meta='meta.yaml',debug=False,\
env={'pyver':'3.7'},
verbose=True):
self.verbose = verbose
self.meta = meta
try:
self.env = getEnv(filename=meta).info
except:
if self.verbose:
print(f'unable to load environment from {meta}')
self.env = {}
self.env.update(env)
self.run_info = []
for s in scripts:
# tuple for fatal
# exit if log is None
log = self.run_recipe(s,debug=debug)
if log is None:
break
self.run_info.append(log)
def run_recipe(self,recipe_file,debug=False,fatal=True):
'''
read commands recipe from recipe_file
'''
run = not debug
if not Path(recipe_file).expanduser().is_file():
print('*'*40)
print(f'{recipe_file} not a file')
print('*'*40)
return (not fatal)
error = None
retval = []
env = nested_to_record(self.env,sep="_")
try:
if(self.verbose):
print('*'*40)
print(recipe_file)
print('*'*40)
with open(recipe_file,'r') as f:
lines = [m for m in [l.strip().split('#')[0] \
for l in f.readlines()] if len(m)]
# the lines may reference variables from
# meta.yaml
# e.g. requirements.build.python
for i,m in enumerate(lines):
# format string them
m = m.format(**env)
self.verbose and print(f'** {i+1}/{len(lines)}: {m}')
if run:
try:
retval.append([m,check_output(m,shell=True)])
except:
pass
except:
error = f'error running {recipe_file}'
if(fatal):
for r in retval:
if(type(r) is str):
print(r)
else:
for rr in r:
print(rr)
return not fatal
if(self.verbose):
print('#'*40)
print(f'done {recipe_file}')
if(error): print(error)
for r in retval:
print(r)
print('#'*40)
self.error = error
return retval
def main(argv):
verbose = False
help = False
recipe = ['start','conda-recipe.sh','postBuild']
pyver = '3.7'
env = {"recipe": ["start","conda-recipe.sh","postBuild"]}
env.update({'pyver':'3.7'})
env.update({'meta':'meta.yaml'})
json_env = json.dumps(env)
helpstr = f"{sys.argv[0]} [-v|--verbose] [-h|--help]" + "\n" + \
f"[-e|--env={json_env}]"
try:
opts, args = getopt.getopt(argv,"vhe:",["env="])
except getopt.GetoptError:
print(helpstr)
sys.exit(2)
for opt, arg in opts:
if opt in ("-v", "--verbose"):
verbose = True
elif opt in ('-h','--help'):
print(helpstr)
sys.exit()
elif opt in ('-e','--env'):
this_env=json.loads(arg)
env.update(this_env)
scripts = env['recipe']
meta = env['meta']
del env['recipe']
del env['meta']
if (verbose):
print('scripts',scripts)
print('meta',meta)
print('env',env)
# convert back json env
Batch(scripts=scripts,env=env,meta=meta,verbose=verbose)
def test(tdir='.test'):
'''
a test
'''
import deepdiff
tdir = Path(tdir).expanduser()
tdir.mkdir(parents=True, exist_ok=True)
return True
if __name__ == "__main__":
main(sys.argv[1:])