Skip to content

Commit 4c0762d

Browse files
committed
improve docstring
1 parent 4439f66 commit 4c0762d

File tree

1 file changed

+64
-15
lines changed

1 file changed

+64
-15
lines changed

unpythonic/ec.py

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@ class escape(Exception):
2323
2424
tag: anything comparable with ``==``
2525
Can be used to restrict which ``@setescape`` points may catch
26-
this escape instance.
26+
this escape instance. Default ``None``, meaning "no tag".
2727
2828
allow_catchall: bool
2929
Whether untagged "catch-all" ``@setescape`` points may catch
30-
this escape instance (regardless of whether or not we have a tag!).
30+
this escape instance (regardless of tag!).
31+
32+
Even if ``False``, if ``tag`` is ``None``, a ``@setescape``
33+
point may still override this with ``catch_untagged=True``.
34+
(See the table in ``help(setescape)``, case ``b0``.)
3135
"""
3236
def __init__(self, value, tag=None, allow_catchall=True):
3337
self.value = value
@@ -52,28 +56,51 @@ def setescape(tags=None, catch_untagged=True):
5256
A tuple is OR'd, like in ``isinstance()``.
5357
5458
Restrict which escapes will be caught by this ``@setescape`` point.
55-
If set, ignore any escapes **that have a tag**, which does not match
56-
any of the given tags.
5759
5860
catch_untagged: bool
59-
Choose whether this ``@setescape`` point catches untagged escapes.
61+
Whether this ``@setescape`` point catches untagged escapes.
62+
63+
Even if ``False``, if ``tags`` is ``None``, an escape instance
64+
may still override this with ``allow_catchall=True``. (See the
65+
table below, case ``a1``.)
6066
6167
The exact catch condition is::
6268
6369
# e is an escape instance
6470
if (tags is None and e.allow_catchall) or
65-
(e.tag is None and catch_untagged) or
71+
(catch_untagged and e.tag is None) or
6672
(tags is not None and e.tag is not None and e.tag in tags):
6773
# caught
6874
69-
The same in English:
75+
resulting in the following table. Here a ``@setescape`` point with no tags
76+
is a "catch-all"::
77+
78+
escape instance @setescape point
79+
0: no tag, ignore catch-alls a: no tags
80+
1: no tag, allow catch-alls b: no tags, catch untagged
81+
2: w/ tag, ignore catch-alls c: w/ tags
82+
3: w/ tag, allow catch-alls d: w/ tags, catch untagged
83+
84+
0 1 2 3
85+
a x x
86+
b x x x
87+
c t t
88+
d x x t t
7089
71-
- If we are an untagged "catch-all" point, catch any ``e`` that allows
72-
catchall. Don't care about whether or not it has a tag.
90+
= do not catch, pass on
91+
t = check tags, catch on match
92+
x = catch
7393
74-
- If ``e`` is untagged, catch if we should catch untagged escapes.
94+
**How to use the table**:
7595
76-
- Both us and ``e`` have tags. Catch if the tag of ``e`` matches one of ours.
96+
- If setting a ``@setescape`` point, pick a row. Read off what you'll catch.
97+
98+
- If raising an ``escape`` instance, pick a column. Read off what will catch it.
99+
100+
Default settings for both ends give case ``b1``. For a guaranteed one-to-one
101+
relationship, pick a unique tag, and use settings for case ``c2``.
102+
103+
**Examples**
77104
78105
Multi-return using escape continuation::
79106
@@ -132,7 +159,7 @@ def decorated(*args, **kwargs):
132159
return f(*args, **kwargs)
133160
except escape as e:
134161
if (tags is None and e.allow_catchall) or \
135-
(e.tag is None and catch_untagged) or \
162+
(catch_untagged and e.tag is None) or \
136163
(tags is not None and e.tag is not None and e.tag in tags):
137164
return e.value
138165
else: # meant for someone else, pass it on
@@ -187,7 +214,7 @@ def inner():
187214
188215
Similar usage is valid for named functions, too.
189216
"""
190-
# We need a process-wide unique id to tag the ec:
217+
# Create a process-wide unique id to tag the ec:
191218
anchor = object()
192219
uid = id(anchor)
193220
# Closure property important here. "ec" itself lives as long as someone
@@ -205,8 +232,7 @@ def ec(value):
205232
# Be catchable only by our own escape point.
206233
raise escape(value, uid, allow_catchall=False)
207234
try:
208-
# Set up a tagged escape point here and call f.
209-
# Catch only the one specific ec we just set up.
235+
# Set up a tagged escape point that catches only the ec we just set up.
210236
@setescape(uid, catch_untagged=False)
211237
def wrapper():
212238
return f(ec)
@@ -268,6 +294,29 @@ def erroneous(ec):
268294

269295
# tests with @looped in tco.py to prevent cyclic dependency
270296

297+
# def catching_truth_table():
298+
# def check(tags, catch_untagged, e):
299+
# if (tags is None and e.allow_catchall) or \
300+
# (catch_untagged and e.tag is None):
301+
# return 2 # unconditional catch
302+
# if (tags is not None and e.tag is not None): # and e.tag in tags):
303+
# return 1 # catch if tags match
304+
# return 0 # don't catch, pass on
305+
#
306+
# from itertools import product
307+
# _ = None
308+
# # @setescape point attributes
309+
# ps = ((None, False), (None, True),
310+
# (set(("tag",)), False), (set(("tag",)), True))
311+
# # escape instance attributes
312+
# es = (escape(_, None, False), escape(_, None, True),
313+
# escape(_, "tag", False), escape(_, "tag", True))
314+
# table = [check(t, c, e) for (t, c), e in product(ps, es)]
315+
# import numpy as np
316+
# a = np.reshape(table, (4, 4)) # row = p, col = e
317+
# print(a)
318+
# catching_truth_table()
319+
271320
print("All tests PASSED")
272321

273322
if __name__ == '__main__':

0 commit comments

Comments
 (0)