SlideShare a Scribd company logo
1 of 34
Download to read offline
2 + 2 = 5
Monkey-patching CPython with ctypes to
conform to Party doctrine
Prior art and reference
• forbiddenfruit
• https://github.com/clarete/forbiddenfruit
• python-doublescript
• https://github.com/fdintino/python-doublescript
Test-driven development
class TwoPlusTwoTestCase(TestCase):



def test_two_plus_two(self):

with two_plus_two_equals(5):

self.assertEqual(2 + 2, 5)
Naive approach
old_int_add = int.__add__



def int_add(a, b):

if a == b == 2:

return 5

else:

return old_int_add(a, b)



int.__add__ = int_add
int.__dict__['__add__'] = int_add
TypeError: can't set attributes of built-in/extension type 'int'
TypeError: 'dictproxy' object does not support item assignment
ctypes crash course
from ctypes import (
pythonapi, Structure, c_char_p, CFUNCTYPE)
ctypes.pythonapi
>>> from ctypes import pythonapi, c_char_p



>>> pythonapi.Py_GetVersion.restype = c_char_p

>>> pythonapi.Py_GetVersion()



2.7.13 (default, Feb 23 2017, 08:50:00)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]
ctypes.py_object
from ctypes import pythonapi, py_object



PyNumber_Absolute = pythonapi.PyNumber_Absolute

PyNumber_Absolute.argtypes = [py_object]

PyNumber_Absolute.restype = py_object



PyNumber_Absolute(-3) # 3

ctypes.Structure
class PyObject(Structure):

_fields_ = [

('ob_refcnt', Py_ssize_t),

('ob_type', py_object),

]
_CData.from_address(), POINTER
class PyObject(Structure):

_fields_ = [

('ob_refcnt', Py_ssize_t),

('ob_type', py_object)]



py_object_p = ctypes.POINTER(py_object) # We will use this later
foo = "foo"



pyobj = PyObject.from_address(id(foo))



print(pyobj.ob_refcnt) # 7

print(sys.getrefcount(foo)) # 8
Overriding int.__add__
>>> print type(int.__dict__)
<type 'dictproxy'>
# Python 3
>>> print(type(int.__dict__))
<class 'mappingproxy'>
Overriding int.__add__
typedef struct {

PyObject_HEAD

PyObject *dict;

} proxyobject;
Overriding int.__add__
class DictProxy(PyObject):

_fields_ = [

('dict', ctypes.POINTER(PyObject)),

]
Overriding int.__add__
def mutable_class_dict(cls):

dp = DictProxy.from_address(id(cls.__dict__))

temp = {}

pythonapi.PyDict_SetItem(

py_object(temp),

py_object(None),

dp.dict)

return temp[None]
Overriding int.__add__
old_int_add = int.__add__



def int_add(a, b):

if a == b == 2:

return 5

else:

return old_int_add(a, b)

int_dict = mutable_class_dict(int)

int_dict['__add__'] = int_add
Overriding int.__add__
>>> 2 + 2

4


>>> (2).__add__(2)

5
Why doesn’t overriding __add__ suffice?
PyObject *

PyNumber_Add(PyObject *v, PyObject *w)

{

PyObject *result = binary_op1(v, w, NB_SLOT(nb_add));

if (result == Py_NotImplemented) { /* ... */ }

return result;

}
/* object.h */
typedef struct _typeobject {
PyObject_VAR_HEAD

const char *tp_name; /* For printing, in format "<module>.<name>" */

Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */



/* Methods to implement standard operations */

destructor tp_dealloc;

printfunc tp_print;

getattrfunc tp_getattr;

/* ... */



/* Method suites for standard classes */

PySequenceMethods *tp_as_sequence;

PyMappingMethods *tp_as_mapping;



/* ... */

} PyTypeObject;
PyNumberMethods *tp_as_number;
/* object.h */
typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
typedef struct {

binaryfunc nb_add;

binaryfunc nb_subtract;

binaryfunc nb_multiply;

/* ... */

} PyNumberMethods;
• binaryfunc: a pointer to a function that takes two PyObject pointers as
arguments and returns a pointer to a PyObject
• Use ctypes.CFUNCTYPE(return_type, *arg_types)
binaryfunc = ctypes.CFUNCTYPE(py_object_p, py_object_p, py_object_p)
typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
/* object.h */
typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
typedef struct {

binaryfunc nb_add;

binaryfunc nb_subtract;

binaryfunc nb_multiply;

/* ... */

} PyNumberMethods;
• Use ctypes.Structure to represent the PyNumberMethods struct:
class PyNumberMethods(Structure):

_fields_ = [

('nb_add', binaryfunc),

('nb_subtract', binaryfunc),

('nb_multiply', binaryfunc),

# ...

]
typedef struct {

binaryfunc nb_add;

binaryfunc nb_subtract;

binaryfunc nb_multiply;

/* ... */

} PyNumberMethods;
class PyTypeObject(PyObject):

_fields_ = [

('ob_size', Py_ssize_t),

('tp_name', c_char_p),

('tp_basicsize', Py_ssize_t),

('tp_itemsize', Py_ssize_t),

('...', c_void_p * 6), # skip 6 functions, like tp_repr, for brevity

('tp_as_number', POINTER(PyNumberMethods)),
# ...

]

PyInt_Type = PyTypeObject.from_address(id(int))
>>> PyInt_Type.tp_as_number.contents.nb_add(2, 2)

4
def get_pointer_addr(cdata):

tmp_pointer = ctypes.cast(ctypes.byref(cdata), POINTER(c_void_p))

return tmp_pointer.contents.value

@contextlib.contextmanager

def two_plus_two_equals(new_sum):

old_nb_add_addr = get_pointer_addr(
Py_IntType.tp_as_number.contents.nb_add)

old_nb_add = binaryfunc(old_nb_add_addr)



def int_add(a, b):

if a == b == 2:

return new_sum

else:

return old_nb_add(a, b)



nb_add = binaryfunc(int_add)

Py_IntType.tp_as_number.contents.nb_add = nb_add

yield

Py_IntType.tp_as_number.contents.nb_add = old_nb_add
>>> with two_plus_two_equals(5):

... print(2 + 2)

4
>>> with two_plus_two_equals(5):

... print(eval("2 + 2"))

5
"2 + 2"
Using the dis module to see what’s going on
import dis
two = 2



def add_two_plus_two():

return two + two
>>> dis.dis(add_two_plus_two)
2 0 LOAD_GLOBAL 0 (two)
3 LOAD_GLOBAL 0 (two)
6 BINARY_ADD
7 RETURN_VALUE
The BINARY_ADD instruction opcode
/* ceval.c: PyEval_EvalFrameEx */

TARGET_NOARG(BINARY_ADD) {

w = POP();

v = TOP();

if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) {

/* INLINE: int + int */

register long a, b, i;

a = PyInt_AS_LONG(v);

b = PyInt_AS_LONG(w);

i = (long)((unsigned long)a + b);

x = PyInt_FromLong(i);

}

/* ... */

}
PyInt_CheckExact(v) PyInt_CheckExact(w)
class int2(int):

def __add__(self, other):

if self == other == 2:

return 5

else:

return int.__add__(self, other)

>>> (2).__class__ = int2
Solution: change (2).__class__ to something
other than int
TypeError: __class__ assignment: only for heap types
def set_type(obj, new_type):

old_type = obj.__class__



new_c_typeobj = PyTypeObject.from_address(id(new_type))

if new_c_typeobj.tp_flags & Py_TPFLAGS.HEAPTYPE:

Py_INCREF(new_type)



c_obj = PyObject.from_address(id(obj))

c_obj.ob_type = new_type



old_c_typeobj = PyTypeObject.from_address(id(old_type))

if old_c_typeobj.tp_flags & Py_TPFLAGS.HEAPTYPE:

Py_DECREF(old_type)



@contextlib.contextmanager

def override_type(obj, new_type):

old_type = obj.__class__

set_type(obj, new_type)

yield

set_type(obj, old_type)
>>> with override_type(2, int2):

... print(eval("2 + 2"))

5
>>> two = 2
>>> with override_type(2, int2):

... print(two + two)
5
>>> with override_type(2, int2):

... print(2 + 2)
4
Final obstacle: peephole optimization
• When we disassembled the bytecode earlier, we used a
variable two rather than a literal 2:
import dis
two = 2



def add_two_plus_two():

return two + two
>>> dis.dis(add_two_plus_two)
2 0 LOAD_GLOBAL 0 (two)
3 LOAD_GLOBAL 0 (two)
6 BINARY_ADD
7 RETURN_VALUE
Final obstacle
• What happens if we use a literal 2?
import dis
def add_two_plus_two():

return 2 + 2
>>> dis.dis(add_two_plus_two)
4 0 LOAD_CONST 2 (4)
3 RETURN_VALUE
?!
What’s going on?
• peephole optimization: an optimization technique in compilers
where certain recognized instructions are replaced with
shorter or faster versions.
• In CPython, performed by the C function PyCode_Optimize
• Does not occur in an eval, hence why eval("2 + 2") works.
PyCode_Optimize
007d0850 PUSH R15
007d0852 PUSH R14
007d0854 MOV R14, RSI
007d0857 PUSH R13
...
NoOp_PyCode_Optimize
00ccc010 MOV R11, 0x...beee
00ccc01a MOV R10, 0x...2010
00ccc024 CLC
00ccc025 JMP R11
...
PyCode_Optimize
007d0850 JMP 0xbb7000
007d0856 NOP
007d0857 PUSH R13
...
007d0850 JMP 0xbb7000
007d0856 NOP
Disabling with a trampoline function
Success!
class TwoPlusTwoTestCase(TestCase):



def test_two_plus_two(self):

with two_plus_two_equals(5):

self.assertEqual(2 + 2, 5)
$ python runtests.py
.
----------------------------------------------------------------------
Ran 1 test in 0.187s
OK
We’re hiring
github.com/fdintino/python-doublescript
@frankiedintino
frankie@theatlantic.com

More Related Content

What's hot

[C++ Korea] Effective Modern C++ Study, Item 11 - 13
[C++ Korea] Effective Modern C++ Study, Item 11 - 13[C++ Korea] Effective Modern C++ Study, Item 11 - 13
[C++ Korea] Effective Modern C++ Study, Item 11 - 13Chris Ohk
 
C++aptitude questions and answers
C++aptitude questions and answersC++aptitude questions and answers
C++aptitude questions and answerssheibansari
 
Алексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуляАлексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуляSergey Platonov
 
C++11: Rvalue References, Move Semantics, Perfect Forwarding
C++11: Rvalue References, Move Semantics, Perfect ForwardingC++11: Rvalue References, Move Semantics, Perfect Forwarding
C++11: Rvalue References, Move Semantics, Perfect ForwardingFrancesco Casalegno
 
Basic C++ 11/14 for Python Programmers
Basic C++ 11/14 for Python ProgrammersBasic C++ 11/14 for Python Programmers
Basic C++ 11/14 for Python ProgrammersAppier
 
Pydiomatic
PydiomaticPydiomatic
Pydiomaticrik0
 
OpenGurukul : Language : C++ Programming
OpenGurukul : Language : C++ ProgrammingOpenGurukul : Language : C++ Programming
OpenGurukul : Language : C++ ProgrammingOpen Gurukul
 
STL ALGORITHMS
STL ALGORITHMSSTL ALGORITHMS
STL ALGORITHMSfawzmasood
 
Pysmbc Python C Modules are Easy
Pysmbc Python C Modules are EasyPysmbc Python C Modules are Easy
Pysmbc Python C Modules are EasyRoberto Polli
 
C++: Constructor, Copy Constructor and Assignment operator
C++: Constructor, Copy Constructor and Assignment operatorC++: Constructor, Copy Constructor and Assignment operator
C++: Constructor, Copy Constructor and Assignment operatorJussi Pohjolainen
 
C++tutorial
C++tutorialC++tutorial
C++tutorialdips17
 
Recursion to iteration automation.
Recursion to iteration automation.Recursion to iteration automation.
Recursion to iteration automation.Russell Childs
 
FP 201 - Unit 6
FP 201 - Unit 6FP 201 - Unit 6
FP 201 - Unit 6rohassanie
 

What's hot (20)

[C++ Korea] Effective Modern C++ Study, Item 11 - 13
[C++ Korea] Effective Modern C++ Study, Item 11 - 13[C++ Korea] Effective Modern C++ Study, Item 11 - 13
[C++ Korea] Effective Modern C++ Study, Item 11 - 13
 
Constructor,destructors cpp
Constructor,destructors cppConstructor,destructors cpp
Constructor,destructors cpp
 
Smart Pointers
Smart PointersSmart Pointers
Smart Pointers
 
C++aptitude questions and answers
C++aptitude questions and answersC++aptitude questions and answers
C++aptitude questions and answers
 
Алексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуляАлексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуля
 
C++11: Rvalue References, Move Semantics, Perfect Forwarding
C++11: Rvalue References, Move Semantics, Perfect ForwardingC++11: Rvalue References, Move Semantics, Perfect Forwarding
C++11: Rvalue References, Move Semantics, Perfect Forwarding
 
Python programming
Python  programmingPython  programming
Python programming
 
Ds lab handouts
Ds lab handoutsDs lab handouts
Ds lab handouts
 
Basic C++ 11/14 for Python Programmers
Basic C++ 11/14 for Python ProgrammersBasic C++ 11/14 for Python Programmers
Basic C++ 11/14 for Python Programmers
 
Pydiomatic
PydiomaticPydiomatic
Pydiomatic
 
OpenGurukul : Language : C++ Programming
OpenGurukul : Language : C++ ProgrammingOpenGurukul : Language : C++ Programming
OpenGurukul : Language : C++ Programming
 
STL ALGORITHMS
STL ALGORITHMSSTL ALGORITHMS
STL ALGORITHMS
 
Idiomatic C++
Idiomatic C++Idiomatic C++
Idiomatic C++
 
Pysmbc Python C Modules are Easy
Pysmbc Python C Modules are EasyPysmbc Python C Modules are Easy
Pysmbc Python C Modules are Easy
 
Functional python
Functional pythonFunctional python
Functional python
 
C++: Constructor, Copy Constructor and Assignment operator
C++: Constructor, Copy Constructor and Assignment operatorC++: Constructor, Copy Constructor and Assignment operator
C++: Constructor, Copy Constructor and Assignment operator
 
Constructors & destructors
Constructors & destructorsConstructors & destructors
Constructors & destructors
 
C++tutorial
C++tutorialC++tutorial
C++tutorial
 
Recursion to iteration automation.
Recursion to iteration automation.Recursion to iteration automation.
Recursion to iteration automation.
 
FP 201 - Unit 6
FP 201 - Unit 6FP 201 - Unit 6
FP 201 - Unit 6
 

Similar to 2 + 2 = 5: Monkey-patching CPython with ctypes to conform to Party doctrine

Threads and Callbacks for Embedded Python
Threads and Callbacks for Embedded PythonThreads and Callbacks for Embedded Python
Threads and Callbacks for Embedded PythonYi-Lung Tsai
 
Notes about moving from python to c++ py contw 2020
Notes about moving from python to c++ py contw 2020Notes about moving from python to c++ py contw 2020
Notes about moving from python to c++ py contw 2020Yung-Yu Chen
 
Boost.Python: C++ and Python Integration
Boost.Python: C++ and Python IntegrationBoost.Python: C++ and Python Integration
Boost.Python: C++ and Python IntegrationGlobalLogic Ukraine
 
Shared Memory Parallelism with Python by Dr.-Ing Mike Muller
Shared Memory Parallelism with Python by Dr.-Ing Mike MullerShared Memory Parallelism with Python by Dr.-Ing Mike Muller
Shared Memory Parallelism with Python by Dr.-Ing Mike MullerPyData
 
Object.__class__.__dict__ - python object model and friends - with examples
Object.__class__.__dict__ - python object model and friends - with examplesObject.__class__.__dict__ - python object model and friends - with examples
Object.__class__.__dict__ - python object model and friends - with examplesRobert Lujo
 
Extending Python - EuroPython 2014
Extending Python - EuroPython 2014Extending Python - EuroPython 2014
Extending Python - EuroPython 2014fcofdezc
 
Cluj.py Meetup: Extending Python in C
Cluj.py Meetup: Extending Python in CCluj.py Meetup: Extending Python in C
Cluj.py Meetup: Extending Python in CSteffen Wenz
 
Iterator protocol
Iterator protocolIterator protocol
Iterator protocolAkshar Raaj
 
Intro python-object-protocol
Intro python-object-protocolIntro python-object-protocol
Intro python-object-protocolShiyao Ma
 
Effective testing with pytest
Effective testing with pytestEffective testing with pytest
Effective testing with pytestHector Canto
 
Value Objects, Full Throttle (to be updated for spring TC39 meetings)
Value Objects, Full Throttle (to be updated for spring TC39 meetings)Value Objects, Full Throttle (to be updated for spring TC39 meetings)
Value Objects, Full Throttle (to be updated for spring TC39 meetings)Brendan Eich
 
Advanced python
Advanced pythonAdvanced python
Advanced pythonEU Edge
 
Py.test
Py.testPy.test
Py.testsoasme
 
Virtual function
Virtual functionVirtual function
Virtual functionharman kaur
 
Php Extensions for Dummies
Php Extensions for DummiesPhp Extensions for Dummies
Php Extensions for DummiesElizabeth Smith
 
Why you-dont-need-design-patterns-in-python
Why you-dont-need-design-patterns-in-pythonWhy you-dont-need-design-patterns-in-python
Why you-dont-need-design-patterns-in-pythonSivanagaraju Pachipulusu
 

Similar to 2 + 2 = 5: Monkey-patching CPython with ctypes to conform to Party doctrine (20)

Threads and Callbacks for Embedded Python
Threads and Callbacks for Embedded PythonThreads and Callbacks for Embedded Python
Threads and Callbacks for Embedded Python
 
Notes about moving from python to c++ py contw 2020
Notes about moving from python to c++ py contw 2020Notes about moving from python to c++ py contw 2020
Notes about moving from python to c++ py contw 2020
 
Boost.Python: C++ and Python Integration
Boost.Python: C++ and Python IntegrationBoost.Python: C++ and Python Integration
Boost.Python: C++ and Python Integration
 
Shared Memory Parallelism with Python by Dr.-Ing Mike Muller
Shared Memory Parallelism with Python by Dr.-Ing Mike MullerShared Memory Parallelism with Python by Dr.-Ing Mike Muller
Shared Memory Parallelism with Python by Dr.-Ing Mike Muller
 
Object.__class__.__dict__ - python object model and friends - with examples
Object.__class__.__dict__ - python object model and friends - with examplesObject.__class__.__dict__ - python object model and friends - with examples
Object.__class__.__dict__ - python object model and friends - with examples
 
Python - OOP Programming
Python - OOP ProgrammingPython - OOP Programming
Python - OOP Programming
 
20BCE1734.pdf
20BCE1734.pdf20BCE1734.pdf
20BCE1734.pdf
 
Extending Python - EuroPython 2014
Extending Python - EuroPython 2014Extending Python - EuroPython 2014
Extending Python - EuroPython 2014
 
Cluj.py Meetup: Extending Python in C
Cluj.py Meetup: Extending Python in CCluj.py Meetup: Extending Python in C
Cluj.py Meetup: Extending Python in C
 
Iterator protocol
Iterator protocolIterator protocol
Iterator protocol
 
Advance python
Advance pythonAdvance python
Advance python
 
Intro python-object-protocol
Intro python-object-protocolIntro python-object-protocol
Intro python-object-protocol
 
Effective testing with pytest
Effective testing with pytestEffective testing with pytest
Effective testing with pytest
 
Refactoring
RefactoringRefactoring
Refactoring
 
Value Objects, Full Throttle (to be updated for spring TC39 meetings)
Value Objects, Full Throttle (to be updated for spring TC39 meetings)Value Objects, Full Throttle (to be updated for spring TC39 meetings)
Value Objects, Full Throttle (to be updated for spring TC39 meetings)
 
Advanced python
Advanced pythonAdvanced python
Advanced python
 
Py.test
Py.testPy.test
Py.test
 
Virtual function
Virtual functionVirtual function
Virtual function
 
Php Extensions for Dummies
Php Extensions for DummiesPhp Extensions for Dummies
Php Extensions for Dummies
 
Why you-dont-need-design-patterns-in-python
Why you-dont-need-design-patterns-in-pythonWhy you-dont-need-design-patterns-in-python
Why you-dont-need-design-patterns-in-python
 

Recently uploaded

10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024Mind IT Systems
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is insideshinachiaurasa2
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfproinshot.com
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdfPearlKirahMaeRagusta1
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...Jittipong Loespradit
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrandmasabamasaba
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...SelfMade bd
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnAmarnathKambale
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 

Recently uploaded (20)

10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 

2 + 2 = 5: Monkey-patching CPython with ctypes to conform to Party doctrine

  • 1. 2 + 2 = 5 Monkey-patching CPython with ctypes to conform to Party doctrine
  • 2. Prior art and reference • forbiddenfruit • https://github.com/clarete/forbiddenfruit • python-doublescript • https://github.com/fdintino/python-doublescript
  • 3. Test-driven development class TwoPlusTwoTestCase(TestCase):
 
 def test_two_plus_two(self):
 with two_plus_two_equals(5):
 self.assertEqual(2 + 2, 5)
  • 4. Naive approach old_int_add = int.__add__
 
 def int_add(a, b):
 if a == b == 2:
 return 5
 else:
 return old_int_add(a, b)
 
 int.__add__ = int_add int.__dict__['__add__'] = int_add TypeError: can't set attributes of built-in/extension type 'int' TypeError: 'dictproxy' object does not support item assignment
  • 5. ctypes crash course from ctypes import ( pythonapi, Structure, c_char_p, CFUNCTYPE)
  • 6. ctypes.pythonapi >>> from ctypes import pythonapi, c_char_p
 
 >>> pythonapi.Py_GetVersion.restype = c_char_p
 >>> pythonapi.Py_GetVersion()
 
 2.7.13 (default, Feb 23 2017, 08:50:00) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]
  • 7. ctypes.py_object from ctypes import pythonapi, py_object
 
 PyNumber_Absolute = pythonapi.PyNumber_Absolute
 PyNumber_Absolute.argtypes = [py_object]
 PyNumber_Absolute.restype = py_object
 
 PyNumber_Absolute(-3) # 3

  • 8. ctypes.Structure class PyObject(Structure):
 _fields_ = [
 ('ob_refcnt', Py_ssize_t),
 ('ob_type', py_object),
 ]
  • 9. _CData.from_address(), POINTER class PyObject(Structure):
 _fields_ = [
 ('ob_refcnt', Py_ssize_t),
 ('ob_type', py_object)]
 
 py_object_p = ctypes.POINTER(py_object) # We will use this later foo = "foo"
 
 pyobj = PyObject.from_address(id(foo))
 
 print(pyobj.ob_refcnt) # 7
 print(sys.getrefcount(foo)) # 8
  • 10. Overriding int.__add__ >>> print type(int.__dict__) <type 'dictproxy'> # Python 3 >>> print(type(int.__dict__)) <class 'mappingproxy'>
  • 11. Overriding int.__add__ typedef struct {
 PyObject_HEAD
 PyObject *dict;
 } proxyobject;
  • 12. Overriding int.__add__ class DictProxy(PyObject):
 _fields_ = [
 ('dict', ctypes.POINTER(PyObject)),
 ]
  • 13. Overriding int.__add__ def mutable_class_dict(cls):
 dp = DictProxy.from_address(id(cls.__dict__))
 temp = {}
 pythonapi.PyDict_SetItem(
 py_object(temp),
 py_object(None),
 dp.dict)
 return temp[None]
  • 14. Overriding int.__add__ old_int_add = int.__add__
 
 def int_add(a, b):
 if a == b == 2:
 return 5
 else:
 return old_int_add(a, b)
 int_dict = mutable_class_dict(int)
 int_dict['__add__'] = int_add
  • 15. Overriding int.__add__ >>> 2 + 2
 4 
 >>> (2).__add__(2)
 5
  • 16. Why doesn’t overriding __add__ suffice? PyObject *
 PyNumber_Add(PyObject *v, PyObject *w)
 {
 PyObject *result = binary_op1(v, w, NB_SLOT(nb_add));
 if (result == Py_NotImplemented) { /* ... */ }
 return result;
 }
  • 17. /* object.h */ typedef struct _typeobject { PyObject_VAR_HEAD
 const char *tp_name; /* For printing, in format "<module>.<name>" */
 Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
 
 /* Methods to implement standard operations */
 destructor tp_dealloc;
 printfunc tp_print;
 getattrfunc tp_getattr;
 /* ... */
 
 /* Method suites for standard classes */
 PySequenceMethods *tp_as_sequence;
 PyMappingMethods *tp_as_mapping;
 
 /* ... */
 } PyTypeObject; PyNumberMethods *tp_as_number;
  • 18. /* object.h */ typedef PyObject * (*binaryfunc)(PyObject *, PyObject *); typedef struct {
 binaryfunc nb_add;
 binaryfunc nb_subtract;
 binaryfunc nb_multiply;
 /* ... */
 } PyNumberMethods; • binaryfunc: a pointer to a function that takes two PyObject pointers as arguments and returns a pointer to a PyObject • Use ctypes.CFUNCTYPE(return_type, *arg_types) binaryfunc = ctypes.CFUNCTYPE(py_object_p, py_object_p, py_object_p) typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
  • 19. /* object.h */ typedef PyObject * (*binaryfunc)(PyObject *, PyObject *); typedef struct {
 binaryfunc nb_add;
 binaryfunc nb_subtract;
 binaryfunc nb_multiply;
 /* ... */
 } PyNumberMethods; • Use ctypes.Structure to represent the PyNumberMethods struct: class PyNumberMethods(Structure):
 _fields_ = [
 ('nb_add', binaryfunc),
 ('nb_subtract', binaryfunc),
 ('nb_multiply', binaryfunc),
 # ...
 ] typedef struct {
 binaryfunc nb_add;
 binaryfunc nb_subtract;
 binaryfunc nb_multiply;
 /* ... */
 } PyNumberMethods;
  • 20. class PyTypeObject(PyObject):
 _fields_ = [
 ('ob_size', Py_ssize_t),
 ('tp_name', c_char_p),
 ('tp_basicsize', Py_ssize_t),
 ('tp_itemsize', Py_ssize_t),
 ('...', c_void_p * 6), # skip 6 functions, like tp_repr, for brevity
 ('tp_as_number', POINTER(PyNumberMethods)), # ...
 ]
 PyInt_Type = PyTypeObject.from_address(id(int)) >>> PyInt_Type.tp_as_number.contents.nb_add(2, 2)
 4
  • 21. def get_pointer_addr(cdata):
 tmp_pointer = ctypes.cast(ctypes.byref(cdata), POINTER(c_void_p))
 return tmp_pointer.contents.value
 @contextlib.contextmanager
 def two_plus_two_equals(new_sum):
 old_nb_add_addr = get_pointer_addr( Py_IntType.tp_as_number.contents.nb_add)
 old_nb_add = binaryfunc(old_nb_add_addr)
 
 def int_add(a, b):
 if a == b == 2:
 return new_sum
 else:
 return old_nb_add(a, b)
 
 nb_add = binaryfunc(int_add)
 Py_IntType.tp_as_number.contents.nb_add = nb_add
 yield
 Py_IntType.tp_as_number.contents.nb_add = old_nb_add
  • 22. >>> with two_plus_two_equals(5):
 ... print(2 + 2)
 4 >>> with two_plus_two_equals(5):
 ... print(eval("2 + 2"))
 5 "2 + 2"
  • 23. Using the dis module to see what’s going on import dis two = 2
 
 def add_two_plus_two():
 return two + two >>> dis.dis(add_two_plus_two) 2 0 LOAD_GLOBAL 0 (two) 3 LOAD_GLOBAL 0 (two) 6 BINARY_ADD 7 RETURN_VALUE
  • 24. The BINARY_ADD instruction opcode /* ceval.c: PyEval_EvalFrameEx */
 TARGET_NOARG(BINARY_ADD) {
 w = POP();
 v = TOP();
 if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) {
 /* INLINE: int + int */
 register long a, b, i;
 a = PyInt_AS_LONG(v);
 b = PyInt_AS_LONG(w);
 i = (long)((unsigned long)a + b);
 x = PyInt_FromLong(i);
 }
 /* ... */
 } PyInt_CheckExact(v) PyInt_CheckExact(w)
  • 25. class int2(int):
 def __add__(self, other):
 if self == other == 2:
 return 5
 else:
 return int.__add__(self, other)
 >>> (2).__class__ = int2 Solution: change (2).__class__ to something other than int TypeError: __class__ assignment: only for heap types
  • 26. def set_type(obj, new_type):
 old_type = obj.__class__
 
 new_c_typeobj = PyTypeObject.from_address(id(new_type))
 if new_c_typeobj.tp_flags & Py_TPFLAGS.HEAPTYPE:
 Py_INCREF(new_type)
 
 c_obj = PyObject.from_address(id(obj))
 c_obj.ob_type = new_type
 
 old_c_typeobj = PyTypeObject.from_address(id(old_type))
 if old_c_typeobj.tp_flags & Py_TPFLAGS.HEAPTYPE:
 Py_DECREF(old_type)
 
 @contextlib.contextmanager
 def override_type(obj, new_type):
 old_type = obj.__class__
 set_type(obj, new_type)
 yield
 set_type(obj, old_type)
  • 27. >>> with override_type(2, int2):
 ... print(eval("2 + 2"))
 5 >>> two = 2 >>> with override_type(2, int2):
 ... print(two + two) 5 >>> with override_type(2, int2):
 ... print(2 + 2) 4
  • 28. Final obstacle: peephole optimization • When we disassembled the bytecode earlier, we used a variable two rather than a literal 2: import dis two = 2
 
 def add_two_plus_two():
 return two + two >>> dis.dis(add_two_plus_two) 2 0 LOAD_GLOBAL 0 (two) 3 LOAD_GLOBAL 0 (two) 6 BINARY_ADD 7 RETURN_VALUE
  • 29. Final obstacle • What happens if we use a literal 2? import dis def add_two_plus_two():
 return 2 + 2 >>> dis.dis(add_two_plus_two) 4 0 LOAD_CONST 2 (4) 3 RETURN_VALUE ?!
  • 30. What’s going on? • peephole optimization: an optimization technique in compilers where certain recognized instructions are replaced with shorter or faster versions. • In CPython, performed by the C function PyCode_Optimize • Does not occur in an eval, hence why eval("2 + 2") works.
  • 31. PyCode_Optimize 007d0850 PUSH R15 007d0852 PUSH R14 007d0854 MOV R14, RSI 007d0857 PUSH R13 ... NoOp_PyCode_Optimize 00ccc010 MOV R11, 0x...beee 00ccc01a MOV R10, 0x...2010 00ccc024 CLC 00ccc025 JMP R11 ... PyCode_Optimize 007d0850 JMP 0xbb7000 007d0856 NOP 007d0857 PUSH R13 ... 007d0850 JMP 0xbb7000 007d0856 NOP Disabling with a trampoline function
  • 32. Success! class TwoPlusTwoTestCase(TestCase):
 
 def test_two_plus_two(self):
 with two_plus_two_equals(5):
 self.assertEqual(2 + 2, 5) $ python runtests.py . ---------------------------------------------------------------------- Ran 1 test in 0.187s OK