Skip to content

Commit ec38815

Browse files
committed
prepare for release, vol. 1
1 parent f01eb40 commit ec38815

File tree

8 files changed

+580
-617
lines changed

8 files changed

+580
-617
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,7 @@ Already done elsewhere. See PyMonad or OSlash, or if you want to roll your own,
131131

132132
BSD.
133133

134-
Dynamic scoping based on [StackOverflow answer by Jason Orendorff (2010)], used under CC-BY-SA.
134+
Dynamic scoping based on [StackOverflow answer by Jason Orendorff (2010)](https://stackoverflow.com/questions/2001138/how-to-create-dynamical-scoped-variables-in-python), used under CC-BY-SA.
135+
136+
Core idea of `lispylet` based on [StackOverflow answer by divs1210 (2017)](https://stackoverflow.com/a/44737147), used under the MIT license.
135137

unpythonic/__init__.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,26 @@
22
# -*- coding: utf-8 -*
33
"""Unpythonic constructs that change the rules.
44
5-
See dir(unpythonic) and submodule docstrings for more info.
5+
There are two ``let`` constructs provided:
6+
7+
- ``unpythonic.let``:
8+
Pythonic syntax, but no guarantees on evaluation order of the bindings.
9+
Bindings are declared using kwargs.
10+
11+
- ``unpythonic.lispylet``:
12+
Guaranteed left-to-right evaluation of bindings, but clunky syntax.
13+
Bindings are declared as ``(("name", value), ...)``.
14+
15+
With ``import unpythonic``, the default ``let`` construct is ``unpythonic.let``.
16+
To override, just import the other one; they define the same names.
17+
18+
See ``dir(unpythonic)`` and submodule docstrings for more info.
619
"""
720

821
from .assignonce import *
922
from .dynscope import *
10-
#from .lispy_let import * # guaranteed evaluation order, clunky syntax
11-
from .pythonic_let import * # no guarantees on evaluation order, nice syntax
23+
from .let import * # no guarantees on evaluation order, nice syntax
24+
#from .lispylet import * # guaranteed evaluation order, clunky syntax
1225
from .misc import *
26+
27+
__version__ = '1.0.0'

unpythonic/assignonce.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3-
"""In Scheme terms, make define and set! look different.
4-
5-
For defensive programming, to avoid accidentally overwriting existing names.
6-
7-
Usage:
8-
9-
with assignonce() as e:
10-
e.foo = "bar" # new definition, ok
11-
e.foo <<= "tavern" # explicitly rebind foo in e
12-
e.foo = "quux" # AttributeError, foo already defined.
13-
"""
3+
"""Assign-once names."""
144

155
__all__ = ["assignonce"]
166

@@ -28,6 +18,15 @@
2818
# https://docs.python.org/3/reference/datamodel.html#object.__getattr__
2919

3020
class assignonce:
21+
"""Environment with assign-once names.
22+
23+
In Scheme terms, this makes ``define`` and ``set!`` look different::
24+
25+
with assignonce() as e:
26+
e.foo = "bar" # new definition, ok
27+
e.foo <<= "tavern" # explicitly rebind e.foo, ok
28+
e.foo = "quux" # AttributeError, e.foo already defined.
29+
"""
3130
def __init__(self):
3231
self._env = {} # should be private...
3332

unpythonic/dynscope.py

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,65 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3-
"""Dynamic scoping.
3+
"""Dynamic scoping."""
44

5-
This module exports a single object instance, dyn, which emulates dynamic scoping
6-
(Lisp's special variables; Racket's parameterize).
5+
__all__ = ["dyn"]
76

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
118

12-
- Dynamic variables are created by 'with dyn.let()'.
9+
_L = local() # each thread gets its own stack
10+
_L._stack = []
1311

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()
1719

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``).
1923
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).
2127
22-
Example:
28+
- Dynamic variables are created by ``with dyn.let()``.
2329
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.)
2533
26-
def f():
27-
print(dyn.a)
34+
- The blocks can be nested. Inner scopes mask outer ones, as usual.
2835
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.
3337
34-
with dyn.let(a=3):
35-
print(dyn.a) # 3
38+
Example::
3639
37-
print(dyn.a) # 2
40+
from dynscope import dyn
3841
39-
print(dyn.b) # AttributeError, dyn.b no longer exists
42+
def f():
43+
print(dyn.a)
4044
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()!
4249
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
4652
47-
__all__ = ["dyn"]
53+
print(dyn.a) # 2
4854
49-
from threading import local
55+
print(dyn.b) # AttributeError, dyn.b no longer exists
5056
51-
_L = local() # each thread gets its own stack
52-
_L._stack = []
57+
main()
5358
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).
6160
62-
class _Env(object):
61+
https://stackoverflow.com/questions/2001138/how-to-create-dynamical-scoped-variables-in-python
62+
"""
6363
def __getattr__(self, name):
6464
for scope in reversed(_L._stack):
6565
if name in scope:

0 commit comments

Comments
 (0)