1 of 226

Python internals:�How does CPython work?

disconnect3d

PyConPL 2019

1

2 of 226

#whoami

2

  • Security Engineer ~ Trail of Bits
  • Pwndbg project contributor
  • justCatTheFish CTF team captain
  • https://disconnect3d.pl/

3 of 226

Python

“The reference implementation”

4 of 226

“Hello world” in Python!!111oneoneone

“The reference implementation”

def add(x, y):

return x + y

print(add("Hello ", "world"))

5 of 226

“Hello world” in Python!!111oneoneone

“The reference implementation”

def add(x, y):

return x + y

print(add("Hello ", "world"))

da da da

duck typing

6 of 226

CPython

“The reference implementation”

7 of 226

CPython

8 of 226

CPython

9 of 226

There is no standard in Python as e.g. in C or C++

but there are PEPs!

https://www.python.org/dev/peps/

10 of 226

There is no standard in Python as e.g. in C or C++

but there are PEPs!

https://www.python.org/dev/peps/

11 of 226

Example PEP

12 of 226

13 of 226

Different Python implementations

14 of 226

Different Python implementations

  • PyPy - “fast” Python because it has a JIT compiler
  • Jython - Python on JVM (Java etc.)
  • IronPython - Python on .NET (C# etc.)
  • MicroPython - Python for microcontrollers ;)
  • Cython - Python and Cython compiler �(Cython is an extended Python language which allows for static types)

15 of 226

Different Python implementations

  • PyPy - “fast” Python because it has a JIT compiler
  • Jython - Python on JVM (Java etc.)
  • IronPython - Python on .NET (C# etc.)
  • MicroPython - Python for microcontrollers ;)
  • Cython - Python and Cython compiler �(Cython is an extended Python language which allows for static types)

C Y T H O N ! = C P Y T H O N

16 of 226

Let’s go back to CPython

17 of 226

CPython

IS

S L O W

but…

Should we care?

Is it always true?�(e.g. modules written in C)

18 of 226

CPython

IS

S L O W

but…

Should we care?

Is it always true?�(e.g. modules written in C)

19 of 226

CPython

IS

S L O W

but…

Should we care?

Is it always true?�(e.g. modules written in C)

20 of 226

CPython has�Global Interpreter Lock (GIL)

    • At a given time only one thread can execute Python code
    • But we can have modules written in C, which would release GIL�or use threads

21 of 226

CPython has�Global Interpreter Lock (GIL)

    • At a given time only one thread can execute Python code
    • But we can have modules written in C, which would release GIL�or use threads

22 of 226

a bit about internals

23 of 226

Under the hood everything (???) �is a PyObject*

Via https://github.com/python/cpython/blob/v3.7.3/Include/object.h#L106-L110

24 of 226

Under the hood everything (???) �is a PyObject*

Via https://github.com/python/cpython/blob/v3.7.3/Include/object.h#L106-L110

“Extra fields”, usually empty macro

25 of 226

Under the hood everything (???) �is a PyObject*

Via https://github.com/python/cpython/blob/v3.7.3/Include/object.h#L106-L110

“Extra fields”, usually empty macro

Reference counter (memory mngmnt)

26 of 226

Under the hood everything (???) �is a PyObject*

Via https://github.com/python/cpython/blob/v3.7.3/Include/object.h#L106-L110

“Extra fields”, usually empty macro

Reference counter (memory mngmnt)

Pointer to type object

27 of 226

28 of 226

29 of 226

30 of 226

31 of 226

32 of 226

Contains PyObject ob_base and some ob_size field

33 of 226

Contains PyObject ob_base and some ob_size field

34 of 226

Contains PyObject ob_base and some ob_size field

Data

35 of 226

Contains PyObject ob_base and some ob_size field

Data

But why “like this”?

36 of 226

Contains PyObject ob_base and some ob_size field

Data

But why “like this”?

37 of 226

Contains PyObject ob_base and some ob_size field

Data

But why “like this”?

38 of 226

Contains PyObject ob_base and some ob_size field

Data

But why “like this”?

39 of 226

Memory is managed by reference counting

40 of 226

Memory is managed by reference counting

41 of 226

Memory is managed by reference counting

42 of 226

Memory is managed by reference counting

Btw why this cast here?

43 of 226

CPython is a stack based �virtual machine

44 of 226

CPython is a stack based �virtual machine

Which means that the operations arguments �and their results are stored on the stack

45 of 226

CPython VM

46 of 226

for (;;) {

opcode = get_opcode()

switch (opcode) {

case LOAD_GLOBAL: ...

case LOAD_CONST: ...

case CALL_FUNCTION: ...

...

}

}

47 of 226

for (;;) {

opcode = get_opcode()

switch (opcode) {

case LOAD_GLOBAL: ...

case LOAD_CONST: ...

case CALL_FUNCTION: ...

...

}

}

opcodes aka �“operational codes”

48 of 226

#define TARGET(op) \

TARGET_##op: \

case op:

49 of 226

python script.py

from invocation to execution

50 of 226

python script.py - the flow

  1. File decoding

# coding: utf-8�x = 123

Default encoding in Py3,

Btw in Py2 you could have “non-text” encoding like “hex” or “rot13”

51 of 226

python script.py - the flow

  • File decoding
  • Tokenization

52 of 226

python script.py - the flow

  • File decoding
  • Tokenization
  • Parsing, AST creation

ast.dump(ast.parse("assert 1==2"))

Assert(� test=Compare(� left=Num(n=1),� ops=[Eq()],� comparators=[Num(n=2)]� ),� msg=None�)

53 of 226

python script.py - the flow

  • File decoding
  • Tokenization
  • Parsing, AST creation

ast.dump(ast.parse("assert 1==2"))

Assert(� test=Compare(� left=Num(n=1),� ops=[Eq()],� comparators=[Num(n=2)]� ),� msg=None�)

54 of 226

python script.py - the flow

  • File decoding
  • Tokenization
  • Parsing, AST creation

ast.dump(ast.parse("assert 1==2"))

Assert(� test=Compare(� left=Num(n=1),� ops=[Eq()],� comparators=[Num(n=2)]� ),� msg=None�)

55 of 226

python script.py - the flow

  • File decoding
  • Tokenization
  • Parsing, AST creation
  • Compilation
    1. “peephole optimizer”
    2. Compilation to bytecode

56 of 226

python script.py - the flow

  • File decoding
  • Tokenization
  • Parsing, AST creation
  • Compilation
    • “peephole optimizer”
    • Compilation to bytecode

Super simple optimizations e.g. of arithmetic expressions: for example “2+2” is converted to “4”

57 of 226

python script.py - the flow

  • File decoding
  • Tokenization
  • Parsing, AST creation
  • Compilation
    • “peephole optimizer”
    • Compilation to bytecode

Create sth that the VM understands!

58 of 226

python script.py - the flow

  • File decoding
  • Tokenization
  • Parsing, AST creation
  • Compilation
    • “peephole optimizer”
    • Compilation to bytecode

5. Execution in the VM

59 of 226

python script.py - the flow

  • File decoding
  • Tokenization
  • Parsing, AST creation
  • Compilation
    • “peephole optimizer”
    • Compilation to bytecode

5. Execution in the VM

Disclaimer:

This is probably over-simplified

60 of 226

PYC files!

61 of 226

Pliki PYC

Created in __pycache__ for imported modules

  • Magic number (4B)
  • Timestamp (4B)
  • Size (4B)
  • Module code object

62 of 226

Pliki PYC

Created in __pycache__ for imported modules

  • Magic number (4B)
  • Timestamp (4B)
  • Size (4B)
  • Module code object

63 of 226

Pliki PYC

Created in __pycache__ for imported modules

  • Magic number (4B)
  • Timestamp (4B)
  • Size (4B)
  • Module code object

It can be loaded via marshal.loads(...)

...and executed via eval(...)

64 of 226

Pliki PYC

Created in __pycache__ for imported modules

  • Magic number (4B)
  • Timestamp (4B)
  • Size (4B)
  • Module code object

It can be loaded via marshal.loads(...)

...and executed via eval(...)

^DEMO

65 of 226

Disassembling code via

dis �module

* btw bytecode can differ between CPython versions

66 of 226

dis.dis(foo)

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

67 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

68 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

69 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

70 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

71 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

72 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

73 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

74 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

75 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

76 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

77 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

78 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

79 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

80 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

81 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

82 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

83 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

84 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

85 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

86 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

87 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

88 of 226

So now we go to next opcode

89 of 226

But to recap!

90 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

91 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

92 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

93 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

SP —>

94 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

95 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

96 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

97 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

98 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

99 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

100 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

101 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

right=0

102 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

right=0

103 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

right=0

104 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

right=0

105 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

right=0

left=x

106 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

right=0

left=x

107 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

right=0

left=x

oparg >=�left x

right 0

108 of 226

oparg >=,�left x, right 0

109 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

110 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

111 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

112 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

113 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

114 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

115 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

116 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

117 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

118 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

case PyCmp_EXC_MATCH:

if (PyTuple_Check(w)) {

Py_ssize_t i, length;

length = PyTuple_Size(w);

for (i = 0; i < length; i += 1) {

PyObject *exc = PyTuple_GET_ITEM(w, i);

if (!PyExceptionClass_Check(exc)) {

PyErr_SetString(PyExc_TypeError,

CANNOT_CATCH_MSG);

return NULL;

}

}

}

else {

if (!PyExceptionClass_Check(w)) {

PyErr_SetString(PyExc_TypeError,

CANNOT_CATCH_MSG);

return NULL;

}

}

res = PyErr_GivenExceptionMatches(v, w);

break;

119 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

120 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

121 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

A quick recap:

  • We are in COMPARE_OP
  • Which called�cmp_outcome(>=, x, 0)

122 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

A quick recap:

  • We are in COMPARE_OP
  • Which called�cmp_outcome(>=, x, 0)
  • What’s next?

123 of 226

oparg >=,�left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

int res = 0;

switch (op) {

case PyCmp_IS:

res = (v == w);

break;

case PyCmp_IS_NOT:

res = (v != w);

break;

case PyCmp_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

break;

case PyCmp_NOT_IN:

res = PySequence_Contains(w, v);

if (res < 0)

return NULL;

res = !res;

break;

case PyCmp_EXC_MATCH:

// (...)

default:

return PyObject_RichCompare(v, w, op);

}

v = res ? Py_True : Py_False;

Py_INCREF(v);

return v;

}

A quick recap:

  • We are in COMPARE_OP
  • Which called�cmp_outcome(>=, x, 0)
  • What’s next?

124 of 226

oparg >=, left x, right 0

125 of 226

oparg >=, left x, right 0

126 of 226

oparg >=, left x, right 0

127 of 226

oparg >=, left x, right 0

128 of 226

oparg >=, left x, right 0

129 of 226

oparg >=, left x, right 0

130 of 226

oparg >=, left x, right 0

131 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

For recap we are in:

  • COMPARE_OP
  • cmp_outcome(>=, x, 0)
  • PyObject* res = PyObject_RichCompare(>=, x, 0)
  • do_richcompare(left, right, op)

132 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

133 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

134 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

135 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

136 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

137 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

138 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

If the types are different

139 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

If the types are different�AND w is v’s subtype

140 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

If the types are different�AND w is v’s subtype

AND w has a tp_richcompare

141 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

If the types are different�AND w is v’s subtype

AND w has a tp_richcompare

...then use it :)

142 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

If the types are different�AND w is v’s subtype

AND w has a tp_richcompare

...then use it :)

...and they check for “NotImplemented” pattern

143 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

If the types are different�AND w is v’s subtype

AND w has a tp_richcompare

...then use it :)

...and they check for “NotImplemented” pattern

144 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

If ‘v’ has tp_richcompare - use it

145 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

...and if `w` has and we didn’t check for reverse_op

146 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

This is not the end of the func!�We might end up with “res” having a value

147 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

/* If neither object implements it, provide a sensible default

for == and !=, but raise an exception for ordering. */

switch (op) {

case Py_EQ:

res = (v == w) ? Py_True : Py_False;

break;

case Py_NE:

res = (v != w) ? Py_True : Py_False;

break;

default:

PyErr_Format(PyExc_TypeError,

"'%s' not supported between instances of '%.100s' and '%.100s'",

opstrings[op],

v->ob_type->tp_name,

w->ob_type->tp_name);

return NULL;

}

Py_INCREF(res);

return res;

}

148 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

/* If neither object implements it, provide a sensible default

for == and !=, but raise an exception for ordering. */

switch (op) {

case Py_EQ:

res = (v == w) ? Py_True : Py_False;

break;

case Py_NE:

res = (v != w) ? Py_True : Py_False;

break;

default:

PyErr_Format(PyExc_TypeError,

"'%s' not supported between instances of '%.100s' and '%.100s'",

opstrings[op],

v->ob_type->tp_name,

w->ob_type->tp_name);

return NULL;

}

Py_INCREF(res);

return res;

}

149 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

/* If neither object implements it, provide a sensible default

for == and !=, but raise an exception for ordering. */

switch (op) {

case Py_EQ:

res = (v == w) ? Py_True : Py_False;

break;

case Py_NE:

res = (v != w) ? Py_True : Py_False;

break;

default:

PyErr_Format(PyExc_TypeError,

"'%s' not supported between instances of '%.100s' and '%.100s'",

opstrings[op],

v->ob_type->tp_name,

w->ob_type->tp_name);

return NULL;

}

Py_INCREF(res);

return res;

}

150 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

/* If neither object implements it, provide a sensible default

for == and !=, but raise an exception for ordering. */

switch (op) {

case Py_EQ:

res = (v == w) ? Py_True : Py_False;

break;

case Py_NE:

res = (v != w) ? Py_True : Py_False;

break;

default:

PyErr_Format(PyExc_TypeError,

"'%s' not supported between instances of '%.100s' and '%.100s'",

opstrings[op],

v->ob_type->tp_name,

w->ob_type->tp_name);

return NULL;

}

Py_INCREF(res);

return res;

}

151 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

/* If neither object implements it, provide a sensible default

for == and !=, but raise an exception for ordering. */

switch (op) {

case Py_EQ:

res = (v == w) ? Py_True : Py_False;

break;

case Py_NE:

res = (v != w) ? Py_True : Py_False;

break;

default:

PyErr_Format(PyExc_TypeError,

"'%s' not supported between instances of '%.100s' and '%.100s'",

opstrings[op],

v->ob_type->tp_name,

w->ob_type->tp_name);

return NULL;

}

Py_INCREF(res);

return res;

}

152 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

/* If neither object implements it, provide a sensible default

for == and !=, but raise an exception for ordering. */

switch (op) {

case Py_EQ:

res = (v == w) ? Py_True : Py_False;

break;

case Py_NE:

res = (v != w) ? Py_True : Py_False;

break;

default:

PyErr_Format(PyExc_TypeError,

"'%s' not supported between instances of '%.100s' and '%.100s'",

opstrings[op],

v->ob_type->tp_name,

w->ob_type->tp_name);

return NULL;

}

Py_INCREF(res);

return res;

}

153 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

/* If neither object implements it, provide a sensible default

for == and !=, but raise an exception for ordering. */

switch (op) {

case Py_EQ:

res = (v == w) ? Py_True : Py_False;

break;

case Py_NE:

res = (v != w) ? Py_True : Py_False;

break;

default:

PyErr_Format(PyExc_TypeError,

"'%s' not supported between instances of '%.100s' and '%.100s'",

opstrings[op],

v->ob_type->tp_name,

w->ob_type->tp_name);

return NULL;

}

Py_INCREF(res);

return res;

}

154 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

/* If neither object implements it, provide a sensible default

for == and !=, but raise an exception for ordering. */

switch (op) {

case Py_EQ:

res = (v == w) ? Py_True : Py_False;

break;

case Py_NE:

res = (v != w) ? Py_True : Py_False;

break;

default:

PyErr_Format(PyExc_TypeError,

"'%s' not supported between instances of '%.100s' and '%.100s'",

opstrings[op],

v->ob_type->tp_name,

w->ob_type->tp_name);

return NULL;

}

Py_INCREF(res);

return res;

}

FYI we are in

do_richcompare(x, 0, >=)

155 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

/* If neither object implements it, provide a sensible default

for == and !=, but raise an exception for ordering. */

switch (op) {

case Py_EQ:

res = (v == w) ? Py_True : Py_False;

break;

case Py_NE:

res = (v != w) ? Py_True : Py_False;

break;

default:

PyErr_Format(PyExc_TypeError,

"'%s' not supported between instances of '%.100s' and '%.100s'",

opstrings[op],

v->ob_type->tp_name,

w->ob_type->tp_name);

return NULL;

}

Py_INCREF(res);

return res;

}

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

156 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

157 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

It depends on `x` (or `v`) type

158 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

159 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

160 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

161 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

162 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

163 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

164 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

165 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

166 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

167 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

168 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

169 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

170 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

171 of 226

/* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */

static PyObject *do_richcompare(PyObject *v, PyObject *w, int op) {

richcmpfunc f;

PyObject *res;

int checked_reverse_op = 0;

if (v->ob_type != w->ob_type &&

PyType_IsSubtype(w->ob_type, v->ob_type) &&

(f = w->ob_type->tp_richcompare) != NULL) {

checked_reverse_op = 1;

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {

res = (*f)(w, v, _Py_SwappedOp[op]);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

// (...)

Which of the path our code uses?

FYI we are in

do_richcompare(x, 0, >=)

Let’s assume `x` (or `v`) is int

172 of 226

As a reminder...

173 of 226

oparg >=, left x, right 0

174 of 226

oparg >=, left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

// (...)

default:

return PyObject_RichCompare(v, w, op);

175 of 226

oparg >=, left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

// (...)

default:

return PyObject_RichCompare(v, w, op);

176 of 226

oparg >=, left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

// (...)

default:

return PyObject_RichCompare(v, w, op);

static PyObject*�do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

177 of 226

oparg >=, left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

// (...)

default:

return PyObject_RichCompare(v, w, op);

static PyObject*�do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

178 of 226

oparg >=, left x, right 0

static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{

// (...)

default:

return PyObject_RichCompare(v, w, op);

static PyObject*�do_richcompare(PyObject *v, PyObject *w, int op) {

// (...)

if ((f = v->ob_type->tp_richcompare) != NULL) {

res = (*f)(v, w, op);

if (res != Py_NotImplemented)

return res;

Py_DECREF(res);

}

And this calls

Long_compare ! ;D

179 of 226

Let’s go back!

180 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

181 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

So we have the comparison (x>=0) result,�let’s assume it is True

SP —>

182 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

So we have the comparison (x>=0) result,�let’s assume it is True

SP —>

183 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

So we have the comparison (x>=0) result,�let’s assume it is True

SP —>

184 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

So we have the comparison (x>=0) result,�let’s assume it is True

185 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

So we have the comparison (x>=0) result,�let’s assume it is True

186 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

So we have the comparison (x>=0) result,�let’s assume it is True

187 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

So we have the comparison (x>=0) result,�let’s assume it is True

188 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

189 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

190 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

cond=True

191 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

cond=True

192 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

cond=True

193 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

cond=True

194 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

cond=True

We check if bool(cond) is True

195 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

cond=True

In our case “cond” is “Py_True”

196 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

True

0

SP —>

cond=True

197 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

PUSH(x)

198 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

199 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

200 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

201 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

retval = x

202 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

203 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

x

0

SP —>

204 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

How does it look in code?

205 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

if x>= 0:

206 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

if x>= 0:

return x

207 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

if x>= 0:

return x�return -x

208 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

if x>= 0:

return x�return -x

But what if we write it using “else”?

209 of 226

dis.dis(foo)

Line Addr Opcode Argument

2 0 LOAD_FAST 0 (x)

2 LOAD_CONST 1 (0)

4 COMPARE_OP 5 (>=)

6 POP_JUMP_IF_FALSE 12

3 8 LOAD_FAST 0 (x)

10 RETURN_VALUE

4 >> 12 LOAD_FAST 0 (x)

14 UNARY_NEGATIVE

16 RETURN_VALUE

if x>= 0:

return x�return -x

But what if we write it using “else”?

Let’s see!

210 of 226

211 of 226

212 of 226

213 of 226

214 of 226

215 of 226

216 of 226

W T F

217 of 226

Another “dis” example

218 of 226

def foo(x):print('Hello world')�� y = x ** 2�� return y

dis.dis(foo)

2 0 LOAD_GLOBAL 0 (print)

2 LOAD_CONST 1 ('Hello world')

4 CALL_FUNCTION 1

6 POP_TOP

4 8 LOAD_FAST 0 (x)

10 LOAD_CONST 2 (2)

12 BINARY_POWER

14 STORE_FAST 1 (y)

6 16 LOAD_FAST 1 (y)

18 RETURN_VALUE

219 of 226

def foo(x):print('Hello world')�� y = x ** 2�� return y

dis.dis(foo)

2 0 LOAD_GLOBAL 0 (print)

2 LOAD_CONST 1 ('Hello world')

4 CALL_FUNCTION 1

6 POP_TOP

4 8 LOAD_FAST 0 (x)

10 LOAD_CONST 2 (2)

12 BINARY_POWER

14 STORE_FAST 1 (y)

6 16 LOAD_FAST 1 (y)

18 RETURN_VALUE

220 of 226

def foo(x):print('Hello world')�� y = x ** 2�� return y

dis.dis(foo)

2 0 LOAD_GLOBAL 0 (print)

2 LOAD_CONST 1 ('Hello world')

4 CALL_FUNCTION 1

6 POP_TOP

4 8 LOAD_FAST 0 (x)

10 LOAD_CONST 2 (2)

12 BINARY_POWER

14 STORE_FAST 1 (y)

6 16 LOAD_FAST 1 (y)

18 RETURN_VALUE

The CALL_FUNCTION�Arg is a number of function arguments!

221 of 226

def foo(x):print('Hello world')�� y = x ** 2�� return y

dis.dis(foo)

2 0 LOAD_GLOBAL 0 (print)

2 LOAD_CONST 1 ('Hello world')

4 CALL_FUNCTION 1

6 POP_TOP

4 8 LOAD_FAST 0 (x)

10 LOAD_CONST 2 (2)

12 BINARY_POWER

14 STORE_FAST 1 (y)

6 16 LOAD_FAST 1 (y)

18 RETURN_VALUE

222 of 226

def foo(x):print('Hello world')�� y = x ** 2�� return y

dis.dis(foo)

2 0 LOAD_GLOBAL 0 (print)

2 LOAD_CONST 1 ('Hello world')

4 CALL_FUNCTION 1

6 POP_TOP

4 8 LOAD_FAST 0 (x)

10 LOAD_CONST 2 (2)

12 BINARY_POWER

14 STORE_FAST 1 (y)

6 16 LOAD_FAST 1 (y)

18 RETURN_VALUE

223 of 226

Decompile pycs via

uncompyle6

DEMO^

224 of 226

...and that’s all :P

225 of 226

Hope you enjoyed.

226 of 226

Thank you!

https://disconnect3d.pl/

disconnect3d # irc.freenode.net