desugar
Unravelling Python's syntactic sugar source code.
There are accompanying blog posts to go with all of the code in this repository.
Unravelled syntax
obj.attr
βbuiltins.getattr(obj, "attr")
(includingobject.__getattribute__()
)a + b
βoperator.__add__(a, b)
a - b
βoperator.__sub__(a, b)
a * b
βoperator.__mul__(a, b)
a @ b
βoperator.__matmul__(a, b)
a / b
βoperator.__truediv__(a, b)
a // b
βoperator.__floordiv__(a, b)
a % b
βoperator.__mod__(a, b)
a ** b
βoperator.__pow__(a, b)
a << b
βoperator.__lshift__(a, b)
a >> b
βoperator.__rshift__(a, b)
a & b
βoperator.__and__(a, b)
a ^ b
βoperator.__xor__(a, b)
a | b
βoperator.__or__(a, b)
a += b
βa = operator.__iadd__(a, b)
a -= b
βa = operator.__isub__(a, b)
a *= b
βa = operator.__imul__(a, b)
a @= b
βa = operator.__imatmul__(a, b)
a /= b
βa = operator.__itruediv__(a, b)
a //= b
βa = operator.__ifloordiv__(a, b)
a %= b
βa = operator.__imod__(a, b)
a **= b
βa = operator.__ipow__(a, b)
a <<= b
βa = operator.__ilshift__(a, b)
a >>= b
βa = operator.__irshift__(a, b)
a &= b
βa = operator.__iand__(a, b)
a ^= b
βa = operator.__ixor__(a, b)
a |= b
βa = operator.__ior__(a, b)
~ a
βoperator.__invert__(a)
- a
βoperator.__neg__(a)
+ a
βoperator.__pos__(a)
a == b
βoperator.__eq__(a, b)
(includingobject.__eq__()
)a != b
βoperator.__ne__(a, b)
(includingobject.__ne__()
)a < b
βoperator.__lt__(a, b)
a <= b
βoperator.__le__(a, b)
a > b
βoperator.__gt__(a, b)
a >= b
βoperator.__ge__(a, b)
a is b
βoperator.is_(a, b)
a is not b
βoperator.is_not(a, b)
not a
βoperator.not_(a)
a in b
βoperator.__contains__(b, a)
a not in b
βoperator.not_(operator.__contains__(b, a))
a or b
β_temp if (_temp := a) else b
a and b
β_temp if not (_temp := a) else b
import a.b
βa = __import__('a.b', globals(), locals())
import a.b as c
βc = __import__('a', globals(), locals(), ['b'], 0).b
from .a import b
βb = __import__('a', globals(), locals(), ['b'], 1).b
from .a import b as c
βc = __import__('a', globals(), locals(), ['b'], 1).b
assert ...
β see below (post)for ...
β see below (includingbuiltins.iter()
andbuiltins.next()
)pass
β"pass"
with ...
β see below (post)async def ...
β see below (post)await ...
βdesugar.builtins._await(...)
async for
β see below (includingbuiltins.aiter()
andbuiltins.anext()
)async with
β see below (post)(c for b in a)
β see below (post)[c for b in a]
βlist(c for b in a)
{c for b in a}
βset(c for b in a)
{c: d for b in a}
βdict((c, d) for b in a)
[a, b]
βlist((a, b))
(includes iterable unpacking){a, b}
βset((a, b))
(includes iterable unpacking)(a, b)
) β(lambda *args: args)(a, b)
(includes iterable unpacking){a: b, c: d}
) βdict(((a, b), (c, d)))
(include dictionary unpacking)@decorator
β see below (post)break
β see below (post)continue
β see below (post)else
clause onwhile
β see below (post)elif
andelse
clauses onif
β see below (post)else
clause ontry
β see below (post)finally
clause ontry
β see below (post)raise A from B
β see below (post)x[A, B]
βtype(x).__getitem__(x, (A, B))
x[A, B] = C
βtype(x).__setitem__(x, (A, B), C)
del x[A, B]
βtype(x).__delitem__(x, (A, B))
A:B:C
βslice(A, B, C)
4+3j
βcomplex(4, 3)
True
βbool(1)
False
βbool(0)
None
β see below (post)b"ABC"
βbytes([65, 66, 67])
"ABC"
βbytes([65, 66, 67]).decode("utf-8")
...
βEllipsis
class A: ...
β see below (post).
;` β newline plus proper indentationif ...: ...
β see below (post)a := b
see the postlambda a: b
β see below (post)global A; A = 42
βgetattr(dict, "__setitem__")(globals(), "A", 42)
del A
β see below (post)
assert ...
With message
assert a, b
β
if __debug__:
if not a:
raise AssertionError(b)
Without a message
assert a
β
if __debug__:
if not a:
raise AssertionError
for ...
else
Without for a in b:
c
β
_iter = iter(b)
while True:
try:
a = next(_iter)
except StopIteration:
break
else:
c
del _iter
else
With for a in b:
c
else:
d
β
_iter = iter(b)
_looping = True
while _looping:
try:
a = next(_iter)
except StopIteration:
_looping = False
continue
else:
c
else:
d
del _iter, _looping
with ...
with a as b:
c
β
_enter = type(a).__enter__
_exit = type(a).__exit__
b = _enter(a)
try:
c
except:
if not _exit(a, *sys.exc_info()):
raise
else:
_exit(a, None, None, None)
async def ...
async def spam():
...
β
@types.coroutine
def spam():
...
async for ...
else
Without async for a in b:
c
β
_iter = aiter(b)
while True:
try:
a = await anext(_iter)
except StopAsyncIteration:
break
else:
c
del _iter
else
With async for a in b:
c
else:
d
β
_iter = aiter(b)
_looping = True
while _looping:
try:
a = await anext(_iter)
except StopAsyncIteration:
_looping = False
continue
else:
c
else:
d
del _iter, _looping
async with ...
async with a as b:
c
β
_enter = type(a).__aenter__
_exit = type(a).__aexit__
b = await _enter(a)
try:
c
except:
if not await _exit(a, *sys.exc_info()):
raise
else:
await _exit(a, None, None, None)
(c for b in a)
(c for b in a)
β
def _gen_exp(_leftmost_iterable):
for b in _leftmost_iterable:
yield c
_gen_exp(a)
@decorator
@decorator
def func():
...
β
def func():
...
_temp_func_name = func
del func
func = decorator(_temp_func_name)
break
while x:
break
β
class _BreakStatement(Exception):
pass
try:
while x:
raise _BreakStatement
except BreakStatement:
pass
continue
while x:
continue
β
class _ContinueStatement(Exception):
pass
while x:
try:
raise _ContinueStatement
except ContinueStatement:
pass
else
clause on a loop
while x:
break
else:
...
β
class _BreakStatement(Exception):
pass
try:
while x:
raise _BreakStatement
except _BreakStatement:
pass
else:
...
if
if A:
B
β
class _Done(Exception):
pass
try:
while A:
B
raise _Done
except _Done:
pass
elif
/else
on an if
statement
if A:
B
elif C:
D
else:
E
β
_B_ran = _D_ran = False
if A:
_B_ran = True
B
if not _B_ran and C:
_D_ran = True
D
if not (_B_ran or _D_ran):
E
try
else
try:
A
except:
B
else:
C
β
_A_finished = False
try:
A
_A_finished = True
except:
B
if _A_finished:
C
finally
try:
A
except Exception:
B
finally:
C
β
try:
try:
A
except Exception:
B
except BaseException:
C
raise
C
raise A from B
raise A from B
β
_raise = A
if isinstance(_raise, type) and issubclass(_raise, BaseException):
_raise = _raise()
elif not isinstance(_raise, BaseException):
raise TypeError("exceptions must derive from BaseException")
_from = B
if isinstance(_from, type) and issubclass(_from, BaseException):
_from = _from()
if _from is not None:
_raise.__cause__ = _from
raise _raise
None
None
β
def _None():
pass
_None()
class
class Example(SuperClass):
"""Docstring."""
a: int = 3
def c(self): return 42
β
def _exec_Example(_ns):
_temp_ns = {}
_temp_ns["__module__"] = _ns["__module__"] = __name__
_temp_ns[__"qualname__"] = _ns["__qualname__"] = "Example"
_temp_ns["__doc__"] = _ns["__doc__"] = """Docstring."""
_temp_ns["__annotations__"] = _ns["__annotations__"] = {"a": int}
_temp_ns["a"] = _ns["a"] = 3
def _method_c(self):
return 42
_method_c.__name__ = "c"
_method_c.__qualname__ = "Example.c"
temp_ns["c"] = _ns["c"] = _method_c
del _method_c
def _class_Example():
# Resolving MRO entries.
bases = types.resolve_bases((SuperClass, ))
# Determining the appropriate metaclass **and**
# preparing the class namespace.
meta, ns, kwds = types.prepare_class("Example", bases)
# Executing the class body.
_exec_Example(ns)
# Creating the class object.
cls = meta("Example", bases, ns)
## Class decorators, if there were any.
## Make the namespace read-only.
cls.__dict__ = read_only_proxy(ns)
return cls
Example = _class_Example()
lambda
lambda A: B
β
def _lambda(A):
return B
_lambda.__name__ = "<lambda>"
del
del A
β
Local
_DELETED = object()
# `del A`
A = _DELETED
# Referencing `A`
if A is _DELETED:
raise UnboundLocalError("cannot access local variable 'A' where it is not associated with a value")
Global
try:
gettattr(globals(), "__delitem__")("A")
except KeyError:
raise NameError("name 'A' is not defined")
Syntax that can't be unravelled
Keywords
Taken from the keyword
module.
Nothing; all unravelled!
Expressions
Statements
Tokens
Taken from the token
module.