|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 | # -*- coding: utf-8 -*- |
3 | | -"""Dynamic scoping. |
| 3 | +"""Dynamic scoping.""" |
4 | 4 |
|
5 | | -This module exports a single object instance, dyn, which emulates dynamic scoping |
6 | | -(Lisp's special variables; Racket's parameterize). |
| 5 | +__all__ = ["dyn"] |
7 | 6 |
|
8 | | -For implicitly passing stuff through several layers of function calls, |
9 | | -in cases where a lexical closure is not the right tool for the job |
10 | | -(i.e. when some of the functions are defined elsewhere in the code). |
| 7 | +from threading import local |
11 | 8 |
|
12 | | - - Dynamic variables are created by 'with dyn.let()'. |
| 9 | +_L = local() # each thread gets its own stack |
| 10 | +_L._stack = [] |
13 | 11 |
|
14 | | - - The created dynamic variables exist while the with block is executing, |
15 | | - and fall out of scope when the with block exits. (I.e. dynamic variables |
16 | | - exist during the dynamic extent of the with block.) |
| 12 | +class _EnvBlock(object): |
| 13 | + def __init__(self, kwargs): |
| 14 | + self.kwargs = kwargs |
| 15 | + def __enter__(self): |
| 16 | + _L._stack.append(self.kwargs) |
| 17 | + def __exit__(self, t, v, tb): |
| 18 | + _L._stack.pop() |
17 | 19 |
|
18 | | - - The blocks can be nested. Inner scopes mask outer ones, as usual. |
| 20 | +class _Env(object): |
| 21 | + """This module exports a single object instance, ``dyn``, which emulates dynamic |
| 22 | + scoping (Lisp's special variables; Racket's ``parameterize``). |
19 | 23 |
|
20 | | - - Each thread has its own dynamic scope stack. |
| 24 | + For implicitly passing stuff through several layers of function calls, |
| 25 | + in cases where a lexical closure is not the right tool for the job |
| 26 | + (i.e. when some of the functions are defined elsewhere in the code). |
21 | 27 |
|
22 | | -Example: |
| 28 | + - Dynamic variables are created by ``with dyn.let()``. |
23 | 29 |
|
24 | | - from dynscope import dyn |
| 30 | + - The created dynamic variables exist while the with block is executing, |
| 31 | + and fall out of scope when the with block exits. (I.e. dynamic variables |
| 32 | + exist during the dynamic extent of the with block.) |
25 | 33 |
|
26 | | - def f(): |
27 | | - print(dyn.a) |
| 34 | + - The blocks can be nested. Inner scopes mask outer ones, as usual. |
28 | 35 |
|
29 | | - def main(): |
30 | | - with dyn.let(a=2, b="foo"): |
31 | | - print(dyn.a) # 2 |
32 | | - f() # note f is defined outside the lexical scope of main()! |
| 36 | + - Each thread has its own dynamic scope stack. |
33 | 37 |
|
34 | | - with dyn.let(a=3): |
35 | | - print(dyn.a) # 3 |
| 38 | + Example:: |
36 | 39 |
|
37 | | - print(dyn.a) # 2 |
| 40 | + from dynscope import dyn |
38 | 41 |
|
39 | | - print(dyn.b) # AttributeError, dyn.b no longer exists |
| 42 | + def f(): |
| 43 | + print(dyn.a) |
40 | 44 |
|
41 | | - main() |
| 45 | + def main(): |
| 46 | + with dyn.let(a=2, b="foo"): |
| 47 | + print(dyn.a) # 2 |
| 48 | + f() # note f is defined outside the lexical scope of main()! |
42 | 49 |
|
43 | | -Based on StackOverflow answer by Jason Orendorff (2010): |
44 | | - https://stackoverflow.com/questions/2001138/how-to-create-dynamical-scoped-variables-in-python |
45 | | -""" |
| 50 | + with dyn.let(a=3): |
| 51 | + print(dyn.a) # 3 |
46 | 52 |
|
47 | | -__all__ = ["dyn"] |
| 53 | + print(dyn.a) # 2 |
48 | 54 |
|
49 | | -from threading import local |
| 55 | + print(dyn.b) # AttributeError, dyn.b no longer exists |
50 | 56 |
|
51 | | -_L = local() # each thread gets its own stack |
52 | | -_L._stack = [] |
| 57 | + main() |
53 | 58 |
|
54 | | -class _EnvBlock(object): |
55 | | - def __init__(self, kwargs): |
56 | | - self.kwargs = kwargs |
57 | | - def __enter__(self): |
58 | | - _L._stack.append(self.kwargs) |
59 | | - def __exit__(self, t, v, tb): |
60 | | - _L._stack.pop() |
| 59 | + Based on StackOverflow answer by Jason Orendorff (2010). |
61 | 60 |
|
62 | | -class _Env(object): |
| 61 | + https://stackoverflow.com/questions/2001138/how-to-create-dynamical-scoped-variables-in-python |
| 62 | + """ |
63 | 63 | def __getattr__(self, name): |
64 | 64 | for scope in reversed(_L._stack): |
65 | 65 | if name in scope: |
|
0 commit comments