Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to monkey patch builtin parentheses [], (), {} behaviour? (in python 3 code, without modifying C/interpreter)

I am just checking where is the limit of changing python using python (without modifying interpreter and/or C code).

I know that I can basically monkey patch every builtin function like this:

import builtins
int(1)
# 1

def new_int(number):
    return number + 1

builtins.int = new_int
int(1)
# 2

I know I can turn python classes upside down using special methods like __new__, __get__ etc. and that I can overload any operator.

but is there any way to monkey patch parentheses? so that instead of creating list, python will do something else with elements between [...] like creating tuple.

# normal
old = [1,2,3,4]
print(old)
# [1, 2, 3, 4]
type(old)
# <class 'list'>

# some strange code that monkeypatches [] so that instead of list, it creates tuple
def monkey_patch_list(values):
    return tuple(values)
[] = monkey_patch_list  # of course it is wrong

# new
new = [1,2,3,4]
print(new)
# (1, 2, 3, 4)
type(old)
# <class 'tuple'>

Probably there is no way to do it just in python but maybe somewhere hidden in python code there is a definition of handling [] so that I can mess it up. If there is anyone crazy like me and knows how to do it I will appreciate help.

Disclaimer: Don't worry just for fun, thanks ;)

like image 388
Adrian Kurzeja Avatar asked Oct 23 '25 16:10

Adrian Kurzeja


1 Answers

This can be done as a function decorator as long as the lists to be replaced as tuples are defined in a function.

To do that, use ast.NodeTransformer to replace any ast.List node with an equivalent ast.Tuple node in the function's AST:

import ast
import inspect
from textwrap import dedent

class ForceTuples(ast.NodeTransformer):
    def visit_List(self, node):
        return ast.Tuple(**vars(node))

    # remove 'force_tuples' from the function's decorator list to avoid re-decorating during exec
    def visit_FunctionDef(self, node):
        node.decorator_list = [
            decorator for decorator in node.decorator_list
            if not isinstance(decorator, ast.Name) or decorator.id != 'force_tuples'
        ]
        self.generic_visit(node)
        return node

def force_tuples(func):
    tree = ForceTuples().visit(ast.parse(dedent(inspect.getsource(func))))
    ast.fix_missing_locations(tree)
    scope = {}
    exec(compile(tree, inspect.getfile(func), 'exec'), func.__globals__, scope)
    return scope[func.__name__]

so that:

@force_tuples
def foo():
    bar = [1, 2, 3, 4]
    print(bar)
    print(type(bar))

foo()

outputs:

(1, 2, 3, 4)
<class 'tuple'>

Demo: https://replit.com/@blhsing/MellowLinearSystemsanalysis

like image 61
blhsing Avatar answered Oct 26 '25 04:10

blhsing