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.