Skip to content

Instantly share code, notes, and snippets.

@RyanKung
Last active February 16, 2021 00:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RyanKung/4830d6c8474e6bcefa4edd13f122b4df to your computer and use it in GitHub Desktop.
Save RyanKung/4830d6c8474e6bcefa4edd13f122b4df to your computer and use it in GitHub Desktop.
just magic
import sys
import ast
origin_import = __import__
AST = {}
def custom_import(name, *args, **kwargs):
module = origin_import(name, *args, **kwargs)
if not hasattr(module, '__file__'):
return module
try:
mod_ast = ast.parse(
''.join(
open(
sys.modules[name].__file__.replace('pyc', 'py'),
'r'
).readlines()
)
)
patched_ast = patch(mod_ast)
exec(compile(patched_ast, name, "exec"), module.__dict__)
AST[name] = mod_ast
AST['%s_patched' % name] = patched_ast
return module
except:
return module
def gen_assign_checker_ast(targets, obj_name):
return ast.If(
test=ast.Call(
func=ast.Name(id='hasattr', ctx=ast.Load()),
args=[
ast.Name(id=obj_name, ctx=ast.Load()),
ast.Str(s='__assign__'),
],
keywords=[],
starargs=None,
kwargs=None
),
body=[
ast.Assign(
targets=[ast.Name(id=target, ctx=ast.Store())],
value=ast.Call(
func=ast.Attribute(
value=ast.Name(id=obj_name, ctx=ast.Load()),
attr='__assign__',
ctx=ast.Load()
),
args=[ast.Str(s=target)],
keywords=[],
starargs=None,
kwargs=None
)
)
for target in targets],
orelse=[]
)
class Transformer(ast.NodeTransformer):
def generic_visit(self, node):
ast.NodeTransformer.generic_visit(self, node)
return node
def visit_Assign(self, node):
if not isinstance(node.value, ast.Name):
return node
targets = [t.id for t in node.targets]
obj_name = node.value.id
new_node = gen_assign_checker_ast(targets, obj_name)
ast.copy_location(new_node, node)
ast.fix_missing_locations(new_node)
return new_node
def patch(node):
trans = Transformer()
new_node = trans.visit(node)
ast.fix_missing_locations(new_node)
return new_node
__builtins__.update(**dict(
__import__=custom_import,
__ast__=AST
))
a = 1
class T():
def __assign__(self, v):
print('called with %s' % v)
b = T()
c = b
@RyanKung
Copy link
Author

Test with

Python 3.6.0 (default, Mar  6 2017, 15:44:48)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import magic
>>> import test
called with c

@RyanKung
Copy link
Author

Note: Won't work under REPL environment.

@RyanKung
Copy link
Author

RyanKung commented Oct 26, 2017

If you interesting in this implementation, go check this repo:

https://github.com/RyanKung/assign

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment