OILS / mycpp / examples / scoped_resource.py View on Github | oilshell.org

221 lines, 90 significant
1#!/usr/bin/env python2
2"""
3scoped_resource.py
4"""
5from __future__ import print_function
6
7import os
8import sys
9
10from mycpp import mylib
11from mycpp.mylib import log
12from typing import List, Optional, Any
13
14
15class MyError(Exception):
16
17 def __init__(self):
18 # type: () -> None
19 pass
20
21
22def Error(error):
23 # type: (bool) -> None
24 if error:
25 raise MyError()
26
27
28#class BadName(object):
29class ctx_BadName(object):
30
31 def __init__(self):
32 # type: () -> None
33 self.i = 42
34
35 def __exit__(self, type, value, traceback):
36 # type: (Any, Any, Any) -> None
37 self.i = 43
38
39
40class ctx_NoArgs(object):
41 """Regression for most vexing parse."""
42
43 def __init__(self):
44 # type: () -> None
45 print('> NoArgs')
46
47 def __enter__(self):
48 # type: () -> None
49 """no-op, but it has to exist to be used as context manager."""
50 pass
51
52 def __exit__(self, type, value, traceback):
53 # type: (Any, Any, Any) -> None
54 print('< NoArgs')
55
56
57class ctx_DirStack(object):
58
59 def __init__(self, state, entry):
60 # type: (DirStack, str) -> None
61 self.state = state
62 state.Push(entry)
63
64 # Bug #1986: add heap-allocated member of context manager
65 self.restored = [] # type: List[str]
66 self.restored.append('foo')
67 self.non_pointer_member = 42 # make sure we don't root this
68
69 def __enter__(self):
70 # type: () -> None
71 """no-op, but it has to exist to be used as context manager."""
72 pass
73
74 def __exit__(self, type, value, traceback):
75 # type: (Any, Any, Any) -> None
76 self.restored.pop()
77 self.state.Pop()
78
79
80class DirStack(object):
81 """For pushd/popd/dirs."""
82
83 def __init__(self):
84 # type: () -> None
85 self.stack = [] # type: List[str]
86 self.Reset() # Invariant: it always has at least ONE entry.
87
88 def Reset(self):
89 # type: () -> None
90 del self.stack[:]
91 self.stack.append('CWD')
92
93 def Push(self, entry):
94 # type: (str) -> None
95 self.stack.append(entry)
96
97 def Pop(self):
98 # type: () -> Optional[str]
99 if len(self.stack) <= 1:
100 return None
101 self.stack.pop() # remove last
102 return self.stack[-1] # return second to last
103
104 def Iter(self):
105 # type: () -> List[str]
106 """Iterate in reverse order."""
107 # mycpp REWRITE:
108 #return reversed(self.stack)
109 ret = [] # type: List[str]
110 ret.extend(self.stack)
111 ret.reverse()
112 return ret
113
114 def __repr__(self):
115 # type: () -> str
116 return repr(self.stack)
117
118
119# C++ translation
120#
121# class _ErrExit__Context; // forward declaration
122# class _ErrExit {
123# };
124#
125# class _ErrExit__Context {
126# _ErrExit__Context(_ErrExit* state) : state_(state) {
127# state->Push();
128# }
129# ~_ErrExit__Context() {
130# state->Pop();
131# }
132# };
133
134
135def DoWork(d, do_raise):
136 # type: (DirStack, bool) -> None
137
138 # problem
139 # with self.mem.ctx_Call(...)
140 # PushCall/PopCall
141 # with self.mem.ctx_Temp(...)
142 # PushTemp/PopCall
143 # with self.mem.ctx_Source(...)
144 # PushSource/PopSource
145 #
146 # Scope_Call
147 # Scope_Temp
148
149 # PROBLEM: WE LOST TYPE CHECKING!
150 #with e.Context('zz') as _:
151 with ctx_DirStack(d, 'foo') as _:
152 log(' in context stack %d', len(d.stack))
153 if do_raise:
154 Error(do_raise)
155
156
157def run_tests():
158 # type: () -> None
159
160 # Use cases:
161 #
162 # Many in cmd_exec.py
163 #
164 # fd_state.Push(...) and Pop
165 # BeginAlias, EndAlias
166 # PushSource, PopSource (opens files)
167 # source
168 # eval -- no file opened, but need to change the current file
169 # PushTemp, PopTemp for env bindings
170 # PushErrExit, PopErrExit
171 # loop_level in cmd_exec
172
173 d = DirStack()
174
175 for do_raise in [False, True]:
176 log('')
177 log('-> dir stack %d', len(d.stack))
178 try:
179 DoWork(d, do_raise)
180 except MyError:
181 log('exited with exception')
182 log('<- dir stack %d', len(d.stack))
183
184 # C++ translation
185 #
186 # _Errexit e;
187 # e.errexit = true;
188 #
189 # log("-> errexit %d", e.errexit)
190 # {
191 # _ErrExit__Context(e);
192 # log(" errexit %d", e.errexit)
193 # }
194 # log("<- errexit %d", e.errexit)
195
196 with ctx_NoArgs():
197 print('hi')
198
199
200def run_benchmarks():
201 # type: () -> None
202 d = DirStack()
203 for i in xrange(1000000):
204 # Does NOT trigger the bug!
205 #mylib.MaybeCollect()
206 try:
207 with ctx_DirStack(d, 'foo') as _:
208 # Bug #1986: add collection in this loop
209 mylib.MaybeCollect()
210 if i % 10000 == 0:
211 raise MyError()
212 except MyError:
213 log('exception')
214
215
216if __name__ == '__main__':
217 if os.getenv('BENCHMARK'):
218 log('Benchmarking...')
219 run_benchmarks()
220 else:
221 run_tests()