-
Notifications
You must be signed in to change notification settings - Fork 0
/
koan_support.py
232 lines (190 loc) · 7.15 KB
/
koan_support.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
import os
import pickle
import shutil
import subprocess
import sys
from collections import deque
import re
class State:
"""Set up koan system. Check for prior use so user can continue without
restart. This will only work if the user hasn't altered the archive state."""
keep = {'counter': 1}
basedir = os.path.abspath("")
cwd = basedir
try:
target = os.path.abspath(".koans_state")
f = open(target, "r")
inval = pickle.load(f)
keep['counter'] = inval['counter']
except (AttributeError, IOError):
# no prior state to return to.
#print "In exception of __init__"
f = open(target, "w")
pickle.dump(keep, f)
f.close()
@classmethod
def abs_path(cls, dir=None):
"""Make an absolute dir path through the git_koans home dir to
location 'dir' (a relative path). Returns path string."""
os.chdir(cls.basedir)
if dir in [None, '']:
out = cls.basedir
else:
out = os.path.abspath(dir)
return out
@classmethod
def cd(cls, dir=None):
"""Change dir to 'dir', relative to the koans home dir. If dir is left blank
, cd to git_koans home dir."""
target = cls.abs_path(dir)
cls.cwd = target
os.chdir(target)
@classmethod
def inc_counter(cls):
cls.keep['counter'] += 1
cls.save_state()
@classmethod
def reset_counter(cls, inp=1):
cls.keep['counter'] = inp
cls.save_state()
@classmethod
def set_counter(cls, inp):
"""Set counter to int value arg."""
cls.reset_counter(int(inp))
@classmethod
def save_state(cls):
target = cls.abs_path(".koans_state")
f = open(target, "w")
pickle.dump(cls.keep, f)
f.close()
@classmethod
def get_counter(cls):
"""Returns the value of counter."""
return cls.keep['counter']
@classmethod
def load_workset(cls, workset, live_git=False):
"""Creates a git directory for 'workset' and also a 'tmp' directory for inspection.
If live_git is True then an actual working .git is installed in the .sets/<workset> dir."""
if live_git:
# make this a live git dir with a .git init.
# then clone it target dir rather than copy and init.
dirp = os.path.join(".sets", workset)
abs = os.path.abspath(dirp)
os.chdir(abs)
gitdir = os.path.join(abs, ".git") # .git dir could have any user commits
mitdir = os.path.join(abs, ".mit") # calling the ref repo '.mit' keeps git
# from thinking it's a live repo. Keeps
# it pure.
shutil.rmtree(gitdir, ignore_errors=True)
shutil.copytree(mitdir, gitdir)
shutil.rmtree(State.abs_path(workset), ignore_errors=True)
State.cd()
out = cmd("git clone {0} {1}".format(os.path.join(".", ".sets", workset), workset))
else:
source = cls.abs_path(os.path.join(".sets", workset))
target = cls.abs_path(workset)
shutil.copytree(source, target)
State.cd(workset)
out = cmd("mv .mit .git")
out = cmd("git init")
try:
os.rmdir(cls.abs_path('tmp'))
except OSError:
pass
State.cd()
out = cmd("mkdir tmp")
@classmethod
def delete_workset(cls, workset):
"""Deletes dir for 'workset' (git repo) and the tmp dir."""
for loc in [workset, 'tmp']:
try:
shutil.rmtree(State.abs_path(loc))
except OSError, e:
pass
def sys_reset():
print ("Resetting koans to initial state...")
# make this safer
State.reset_counter()
# this should just kill all the worksets by looking in the .sets dir
for dir in ["set_a", "tmp", "work", "k8_repo", "rollback", "clone_work", "clone_rollback", "k8"]:
try:
State.delete_workset(dir)
except (OSError, TypeError) as e:
print "Did not rm -rf {0}".format(dir)
print "Exception msg: {0}".format(str(e))
pass
# work directory is something to think about
State.cd("")
cmd("mkdir work")
cmd("touch " + State.abs_path("work/.empty"))
def check(loc, setup=[], test_str='', verbose=False):
"""Check a result in location 'loc' (relative to State.basedir). Run shell commands
in setup array. Check for test string 'test_str'. Returns boolean."""
last = ""
retval = False
State.cd(loc)
for instruction in setup:
out = cmd(instruction)
last = out
if verbose:
print instruction
print out
match = re.search(test_str, last)
if not match == None and match.group():
retval = True
if test_str == '' and last == '': # empty string is desired result case
retval = True
return retval
def cmd(cmd, verbose=False):
"""Calls subprocess.check_output with 'shell=True' and stderr redirection.
Returns return val from subprocess. 'verbose' flag is a boolean. Set to
true for debugging info. Short cut."""
proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True);
out = ""
while True:
line = proc.stdout.readline()
if verbose:
print line
if line != '':
out += " " + line.rstrip()
else:
break
return out
def koan(fxn):
"""Prints koan header and increments state counter (given the koan is passed)."""
def new_fxn(*args, **kwargs):
header = kwargs.get('header', True)
test, answers = test_vals(*args, **kwargs)
if header:
print "\n\n******** Koan " + str(State.get_counter()) + " ********\n\n"
success = fxn(*args, **kwargs)
if success: # success
print "\n\nEnlightenment Achieved!"
State.inc_counter()
else: # failed
print ("\n\nThrough failure learning is achieved. Try it again.\n\n")
if not test:
# this runs the caller fxn !!!
kwargs['header']=False
getattr(sys.modules['koans'], fxn.__name__)(**kwargs)
#globals()[fxn.__name__](header=False)
return success
return new_fxn
def test_vals(*args, **kwargs):
"""Return vals in args and kwargs used for testing. Returns <bool test>,<list answers>."""
test = 'test' in args
answers = deque(kwargs.get('answers', []))
return test, answers
def git_set_tree(repo):
#git --git-dir=/Users/joelbremson/code/git_koans/.git --work-tree=./git_koans/ status "
"""Set the working repo to 'repo'. This is assumed to sit in the dir
under /git_koans. Always.
This is required because there are a lot of git repos potentially
floating about in the workspace."""
target = State.abs_path(repo)
out = cmd("git config --git_dir " + target)
print out
def pause():
"""Prints 'Press 'Enter' when ready' and takes an input. Returns input string."""
val = raw_input("Press 'Enter' when ready.")
return val