Python internals:�How does CPython work?
disconnect3d
PyConPL 2019
1
#whoami
2
Python
“The reference implementation”
“Hello world” in Python!!111oneoneone
“The reference implementation”
def add(x, y):
return x + y
print(add("Hello ", "world"))
“Hello world” in Python!!111oneoneone
“The reference implementation”
def add(x, y):
return x + y
print(add("Hello ", "world"))
da da da
duck typing
CPython
“The reference implementation”
CPython
CPython
There is no standard in Python as e.g. in C or C++
but there are PEPs!
There is no standard in Python as e.g. in C or C++
but there are PEPs!
Example PEP
Different Python implementations
Different Python implementations
Different Python implementations
C Y T H O N ! = C P Y T H O N
Let’s go back to CPython
CPython
IS
S L O W
but…
Should we care?
Is it always true?�(e.g. modules written in C)
CPython
IS
S L O W
but…
Should we care?
Is it always true?�(e.g. modules written in C)
CPython
IS
S L O W
but…
Should we care?
Is it always true?�(e.g. modules written in C)
CPython has�Global Interpreter Lock (GIL)
CPython has�Global Interpreter Lock (GIL)
a bit about internals
Under the hood everything (???) �is a PyObject*
Via https://github.com/python/cpython/blob/v3.7.3/Include/object.h#L106-L110
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
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)
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
But where is the object’s data stored in?
But where is the object’s data stored in?
But where is the object’s data stored in?
But where is the object’s data stored in?
But where is the object’s data stored in?
But where is the object’s data stored in?
Contains PyObject ob_base and some ob_size field
But where is the object’s data stored in?
Contains PyObject ob_base and some ob_size field
But where is the object’s data stored in?
Contains PyObject ob_base and some ob_size field
Data
But where is the object’s data stored in?
Contains PyObject ob_base and some ob_size field
Data
But why “like this”?
But where is the object’s data stored in?
Contains PyObject ob_base and some ob_size field
Data
But why “like this”?
But where is the object’s data stored in?
Contains PyObject ob_base and some ob_size field
Data
But why “like this”?
But where is the object’s data stored in?
Contains PyObject ob_base and some ob_size field
Data
But why “like this”?
Memory is managed by reference counting
Memory is managed by reference counting
Memory is managed by reference counting
Memory is managed by reference counting
Btw why this cast here?
CPython is a stack based �virtual machine
CPython is a stack based �virtual machine
Which means that the operations arguments �and their results are stored on the stack
CPython VM
for (;;) {
opcode = get_opcode()
switch (opcode) {
case LOAD_GLOBAL: ...
case LOAD_CONST: ...
case CALL_FUNCTION: ...
...
}
}
for (;;) {
opcode = get_opcode()
switch (opcode) {
case LOAD_GLOBAL: ...
case LOAD_CONST: ...
case CALL_FUNCTION: ...
...
}
}
opcodes aka �“operational codes”
#define TARGET(op) \
TARGET_##op: \
case op:
python script.py
from invocation to execution
python script.py - the flow
# coding: utf-8�x = 123
Default encoding in Py3,
Btw in Py2 you could have “non-text” encoding like “hex” or “rot13”
python script.py - the flow
python script.py - the flow
ast.dump(ast.parse("assert 1==2"))
Assert(� test=Compare(� left=Num(n=1),� ops=[Eq()],� comparators=[Num(n=2)]� ),� msg=None�)
python script.py - the flow
ast.dump(ast.parse("assert 1==2"))
Assert(� test=Compare(� left=Num(n=1),� ops=[Eq()],� comparators=[Num(n=2)]� ),� msg=None�)
python script.py - the flow
ast.dump(ast.parse("assert 1==2"))
Assert(� test=Compare(� left=Num(n=1),� ops=[Eq()],� comparators=[Num(n=2)]� ),� msg=None�)
python script.py - the flow
python script.py - the flow
Super simple optimizations e.g. of arithmetic expressions: for example “2+2” is converted to “4”
python script.py - the flow
Create sth that the VM understands!
python script.py - the flow
5. Execution in the VM
python script.py - the flow
5. Execution in the VM
Disclaimer:
This is probably over-simplified
PYC files!
Pliki PYC
Created in __pycache__ for imported modules
Pliki PYC
Created in __pycache__ for imported modules
Pliki PYC
Created in __pycache__ for imported modules
It can be loaded via marshal.loads(...)
...and executed via eval(...)
Pliki PYC
Created in __pycache__ for imported modules
It can be loaded via marshal.loads(...)
...and executed via eval(...)
^DEMO
Disassembling code via
dis �module
* btw bytecode can differ between CPython versions
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
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
So now we go to next opcode
But to recap!
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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 —>
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
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
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
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
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
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
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
oparg >=,�left x, right 0
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
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;
}
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;
}
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:
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:
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:
oparg >=, left x, right 0
oparg >=, left x, right 0
oparg >=, left x, right 0
oparg >=, left x, right 0
oparg >=, left x, right 0
oparg >=, left x, right 0
oparg >=, left x, right 0
/* 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:
/* 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);
}
// (...)
/* 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);
}
// (...)
/* 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);
}
// (...)
/* 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);
}
// (...)
/* 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);
}
// (...)
/* 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);
}
// (...)
/* 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
/* 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
/* 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
/* 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 :)
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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;
}
/* 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;
}
/* 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;
}
/* 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;
}
/* 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;
}
/* 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;
}
/* 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;
}
/* 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, >=)
/* 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, >=)
/* 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, >=)
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
/* 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
As a reminder...
oparg >=, left x, right 0
oparg >=, left x, right 0
static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{
// (...)
default:
return PyObject_RichCompare(v, w, op);
oparg >=, left x, right 0
static PyObject*�cmp_outcome(int op, PyObject *v, PyObject *w)�{
// (...)
default:
return PyObject_RichCompare(v, w, op);
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);
}
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);
}
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
Let’s go back!
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 —>
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 —>
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 —>
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 —>
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
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
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
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
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 —>
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 —>
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
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
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
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
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
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”
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
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)
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 —>
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 —>
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 —>
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
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 —>
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 —>
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?
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:
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
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
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”?
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!
W T F
Another “dis” example
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
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
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!
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
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
Decompile pycs via
uncompyle6
DEMO^
...and that’s all :P
Hope you enjoyed.
Thank you!