This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author vstinner
Recipients Mark.Shannon, brett.cannon, gvanrossum, petr.viktorin, rhettinger, serhiy.storchaka, vstinner, yselivanov
Date 2021-02-19.19:33:19
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <>
Guido: "I'm still worried about the change in semantics where globals["__builtins__"] is assigned a different dict after the function object has been created (...)"

Well, there is a semantics change of Python 3.10 documented at:

"Functions have a new __builtins__ attribute which is used to look for builtin symbols when a function is executed, instead of looking into __globals__['__builtins__']. (Contributed by Mark Shannon in bpo-42990.)"

And the function __builtins__ attribute is read-only.

Your example is not affected by PR 24564 because the globals has the "__builtins__" key.

In Python 3.10, you can modify func.__builtins__ (new attribute):
def foo(s): return len(s)
code = foo.__code__
FunctionType = type(foo)
f = FunctionType(code, {"__builtins__": {"len": len}})

Traceback (most recent call last):
NameError: name 'len' is not defined

Mark: "Because globals['__builtins__'] is cached for each function activation, executing functions don't see updates."

In Python 3.10, if someone wants to hack builtins while the function is running, modifying the builtins namespace in-place works as expected:
def f():
    builtins_ns = f.__globals__['__builtins__'].__dict__
    #builtins_ns = f.__builtins__
    builtins_ns['len'] = lambda x: 7



It also works with "builtins_ns = f.__builtins__".

Guido: "I realize this is a pretty esoteric, but it does show the change in semantics (from later to earlier binding). Should we care? I like early binding because it allows more optimizations[1], but traditionally Python's semantics use late binding."

Modifying built-in functions/types is commonly done in tests. Example:
import unittest.mock

def func():
    with unittest.mock.patch('builtins.chr', return_value='mock'):
        return chr(65)


The expected output is: "mock". Overriding an attribute of the builtins module immediately updates func.__builtins__. It works because func.__builtins__ is builtins.__dict__.

In FAT Python, I implemented an optimization which copies builtin functions to constants, replace LOAD_GLOBAL with LOAD_CONST:

This optimization breaks this Python semantics, it is no longer possible to override builtin functions in tests:
Date User Action Args
2021-02-19 19:33:20vstinnersetrecipients: + vstinner, gvanrossum, brett.cannon, rhettinger, petr.viktorin, Mark.Shannon, serhiy.storchaka, yselivanov
2021-02-19 19:33:20vstinnersetmessageid: <>
2021-02-19 19:33:20vstinnerlinkissue42990 messages
2021-02-19 19:33:19vstinnercreate