4

After deleting a builtin function like this, I want to restore it without restarting the interpreter.

>>> import builtins
>>> del builtins.eval
>>> builtins.eval = None

I tried reloading the builtin module using importlib, that didn't restore eval.

>>> import importlib
>>> importlib.reload(builtins)
<module 'builtins' (built-in)>
>>> eval("5 + 5")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

I also tried to assign a __builtins__ variable from another module. That didn't work as well.

>>> import os
>>> __builtins__ = os.__builtins__
>>> eval()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

Is there a way to restore a builtin function after deleting it?

Ahmed Tounsi
  • 1,482
  • 1
  • 14
  • 24
  • 6
    What problem are you trying to solve by deleting the builtin in the first place? – Karl Knechtel Jul 13 '20 at 12:11
  • 1
    `del sys.modules['builtins']; from builtins import eval`. – ekhumoro Jul 13 '20 at 12:30
  • 2
    From the docs: It is generally not very useful to reload built-in or dynamically loaded modules. Reloading sys, __main__, builtins and other key modules is not recommended. In many cases extension modules are not designed to be initialized more than once, and may fail in arbitrary ways when reloaded. https://docs.python.org/3/library/importlib.html – ScootCork Jul 13 '20 at 12:32

2 Answers2

4

I think the usage pattern of builtins is different from what you suggest. What you typically do is that you re-bind a built-in name for your purpose and then use builtins to restore the functionality:

eval = None

eval('5 + 5')
# TypeError: 'NoneType' object is not callable


import builtins


eval = builtins.eval
eval('5 + 5')
# 10

or (as commented by @ShadowRanger), even more simply in this specific case:


eval = None

eval('5 + 5')
# TypeError: 'NoneType' object is not callable

del eval

eval('5 + 5')
# 10
norok2
  • 25,683
  • 4
  • 73
  • 99
  • 3
    You don't even need to import `builtins` for this scenario. Just replace `import builtins` & `eval = builtins.eval` with `del eval`. The replaced `eval` is *global*, but not *built-in* in this case, so deleting it from globals unshadows the builtin with no import required. Doing the import changes behavior slightly vs. the unmodified state (since now `eval` is a global, not a built-in). – ShadowRanger Jul 13 '20 at 12:34
1

After posting the question I figured out a way to restore it using the BuiltinImporter.

>>> import builtins
>>> del builtins.eval
>>> builtins.eval = None
>>> eval()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
>>> import importlib
>>> bi = importlib.machinery.BuiltinImporter
>>> bi.load_module("builtins")
<module 'builtins' (built-in)>
>>> import sys
>>> __builtins__ = bi.load_module.__globals__['module_from_spec'](sys.modules['builtins'].__spec__)
>>> eval("5 + 5")
10
k_ssb
  • 6,024
  • 23
  • 47
Ahmed Tounsi
  • 1,482
  • 1
  • 14
  • 24
  • 1
    just wanted to make a note after experiencing how `bi.load_module("builtins")` didn't replace `__builtins__`, although you could still access the new module from `sys.modules['builtins']`, meaning `eval()` would error while `sys.modules['builtins'].eval()` would work. – Tcll Jun 01 '23 at 00:05