Last active
August 29, 2024 20:06
-
-
Save dutc/8a4f6d6c436e9ab484d6 to your computer and use it in GitHub Desktop.
implicit self
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from ctypes import c_void_p, c_char_p, c_long, c_int, c_size_t, py_object, \ | |
Structure, POINTER, cdll | |
from opcode import HAVE_ARGUMENT, opmap | |
from itertools import imap, chain | |
from copy import deepcopy | |
from sysconfig import get_config_vars | |
from sys import version_info | |
assert version_info.major == 2 and version_info.minor == 7, \ | |
'this code presumes certain C struct layouts; works only with Python 2.7' | |
PYLIB_PATH = 'libpython2.7.so' # path to python dyn lib | |
PyCell = cdll.LoadLibrary(PYLIB_PATH).PyCell_New | |
PyCell.restype, PyCell.argtypes = py_object, [py_object] | |
class PyMemberDef(Structure): | |
_fields_ = [('name', c_char_p), | |
('type', c_int), | |
('offset', c_size_t), | |
('flags', c_int), | |
('doc', c_char_p),] | |
class PyTypeObject(Structure): | |
_fields_ = ([('_ob_next', py_object), | |
('_ob_prev', py_object),] | |
if get_config_vars().get('Py_DEBUG', False) else []) + \ | |
[('ob_refcnt', c_size_t), | |
('ob_type', py_object), | |
('ob_size', c_size_t), | |
('tp_name', c_char_p), | |
('tp_basicsize', c_size_t), | |
('tp_itemsize', c_size_t), | |
('tp_dealloc', c_void_p), | |
('tp_print', c_void_p), | |
('tp_getattr', c_void_p), | |
('tp_setattr', c_void_p), | |
('tp_compare', c_void_p), | |
('tp_repr', c_void_p), | |
('tp_as_number', c_void_p), | |
('tp_as_sequence', c_void_p), | |
('tp_as_mapping', c_void_p), | |
('tp_hash', c_void_p), | |
('tp_call', c_void_p), | |
('tp_str', c_void_p), | |
('tp_getattro', c_void_p), | |
('tp_setattro', c_void_p), | |
('tp_as_buffer', c_void_p), | |
('tp_flags', c_long), | |
('tp_doc', c_char_p), | |
('tp_traverse', c_void_p), | |
('tp_clear', c_void_p), | |
('tp_richcompare', c_void_p), | |
('tp_weaklistoffset', c_size_t), | |
('tp_iter', c_void_p), | |
('tp_iternext', c_void_p), | |
('tp_methods', c_void_p), | |
('tp_members', POINTER(PyMemberDef)), | |
('tp_getset', c_void_p), | |
('tp_base', c_void_p), | |
('tp_dict', py_object), | |
('tp_descr_get', c_void_p), | |
('tp_descr_set', c_void_p), | |
('tp_dictoffset', c_size_t), | |
('tp_init', c_void_p), | |
('tp_alloc', c_void_p), | |
('tp_new', c_void_p), | |
('tp_free', c_void_p), | |
('tp_is_gc', c_void_p), | |
('tp_bases', py_object), | |
('tp_mro', py_object), | |
('tp_cache', py_object), | |
('tp_subclasses', py_object), | |
('tp_weaklist', py_object), | |
('tp_del', c_void_p), | |
('tp_version_tag', c_int),] | |
def f(): pass | |
func_closure = PyTypeObject.from_address(id(type(f))).tp_members[0] | |
__closure__ = PyTypeObject.from_address(id(type(f))).tp_members[1] | |
co_flags = PyTypeObject.from_address(id(type(f.func_code))).tp_members[3] | |
co_code = PyTypeObject.from_address(id(type(f.func_code))).tp_members[4] | |
co_freevars = PyTypeObject.from_address(id(type(f.func_code))).tp_members[8] | |
func_closure.flags &= ~0x1 # unset READONLY flag | |
__closure__.flags &= ~0x1 | |
co_flags.flags &= ~0x1 | |
co_code.flags &= ~0x1 | |
co_freevars.flags &= ~0x1 | |
def opcodes(ops): | |
ops = imap(ord,ops) | |
while True: | |
op = next(ops) | |
if op < HAVE_ARGUMENT: | |
yield op, None | |
else: | |
# ignores extended args | |
oparg = next(ops) + next(ops)*256 | |
yield op, oparg | |
def instancemethod(f): | |
@apply | |
class wrapper(object): | |
def __get__(_, self, cls, f=f): | |
func_code = f.func_code | |
# see if __self__ or __cls__ are referenced but unbound | |
# (i.e., default to global) | |
global_self = next((i for i,name in enumerate(func_code.co_names) | |
if name == '__self__'), None) | |
global_cls = next((i for i,name in enumerate(func_code.co_names) | |
if name == '__cls__'), None) | |
if global_self is None and global_cls is None: | |
return f | |
closure = (f.__closure__ or ()) + (PyCell(self), PyCell(cls),) | |
freevars = func_code.co_freevars + ('__self__', '__cls__',) | |
load_self = (opmap['LOAD_DEREF'], len(closure)-2, 0) | |
load_cls = (opmap['LOAD_DEREF'], len(closure)-1, 0) | |
code = ''.join(imap(chr,chain.from_iterable( | |
load_self if op is opmap['LOAD_GLOBAL'] | |
and arg == global_self | |
else load_cls if op is opmap['LOAD_GLOBAL'] | |
and arg == global_cls | |
else (op, arg%256, arg//256) if arg is not None | |
else (op,) | |
for i,(op,arg) in enumerate(opcodes(func_code.co_code))))) | |
f = deepcopy(f) | |
f.func_closure = closure | |
f.func_code.co_freevars = freevars | |
f.func_code.co_code = code | |
f.func_code.co_flags &= ~0x40 # unset CO_NOFREE (no freevars) flag | |
return f | |
return wrapper | |
### example of use: ### | |
# silence PyFlakes NameError | |
__self__, __cls__ = None, None | |
class Foo(object): | |
@instancemethod | |
def __init__(x): | |
__self__.x = x | |
@instancemethod | |
def bar(y): | |
return __self__.x * y | |
class Baz(object): | |
@instancemethod | |
def __init__(x): | |
__self__.x = x | |
def bar(z): | |
@instancemethod | |
def bar(y): | |
return __self__.x + y + z | |
return bar | |
bar = bar(30) | |
if __name__ == '__main__': | |
foo, baz = Foo(10), Baz(10) | |
assert foo.bar(20) == 10*20 | |
assert baz.bar(20) == 10+20+30 | |
print 'all done!' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment