0

I'm writing a module manager for a program that I am writing now, and I wanted to store the module names in a dictionary then reference them and call them from globals().

module_number = 5
module_names  = ["", "quiz", "scores", "gender", "help", "exit"]

I would then expect that I can call

globals()[module_names[module_number]]()

Which would call exit() and close the script, instead I get the resulting error:

Traceback (most recent call last): File "a2.py", line 103, in start() File "a2.py", line 44, in start menu() File "a2.py", line 36, in menu call_module(choice) File "a2.py", line 50, in call_module globals()converter[int(module_number)]

KeyError: 'exit'

Community
  • 1
  • 1
Jack Hales
  • 1,574
  • 23
  • 51
  • [Difference between exit() and sys.exit() in Python](https://stackoverflow.com/q/6501121/674039) – wim Aug 02 '18 at 04:01

1 Answers1

6

exit isn't in globals() because it isn't a global, it's a builtin.

In Python, the "global" namespace is per-module, not system-wide. There's a special "builtins" module that holds the things that are truly system-wide, like the normal builtin functions and a few special things like exit.


You can access this module with import builtins.

The way the interpreter accesses this module is a little funky. Global lookup works roughly like this:

def get_global(global_namespace, name):
    try:
        return global_namespace[name]
    except KeyError:
        pass
    try:
        builtins = global_namespace['__builtins__']
    except KeyError:
        raise NameError(name)
    if isinstance(builtins, types.ModuleType):
        builtins = builtins.__dict__
    try:
        return builtins[name]
    except KeyError:
        raise NameError(name)

There's special code in places like exec, and the internal code for building function objects, that makes sure that if you override the normal globals dictionary, __builtins__ gets copied over (unless you explicitly tell it not to). When the import system builds a module object out of your module source (or compiled .pyc), it calls exec, so every module's globals ends up with the right __builtins__.


Most things in the builtins module are there because they're compiled into it (as you'd expect from the name); for CPython, you can see the source in Python/bltinmodule.c.

But notice that exit isn't there. In fact, it's injected into the builtins module by the site module, which is imported (unless you disable it) as part of the normal startup sequence. You can see the code that does this in Lib/site.py and Lib/_sitebuiltins.py. And the exit constant says it's injected this way.


So, when you type exit in your code, or at the interactive prompt, it's found in globals()['__builtins__']['exit'] or globals()['__builtins__'].__dict__['exit'].

But if you want to access it manually, you're better off doing an import builtins and accessed it as builtins.exit.

Although really, you rarely want to access builtins.exit anyway; if you want to exit programmatically, call sys.exit, which is a normal function. builtins.exit is a special Quitter object that's made specifically for interactive use. (It has a repr that gives a helpful message if you forget the parentheses, and some extra code to make it play nice with IDLE.)

In fact, the docs on the exit constant explicitly say that it's:

… useful for the interactive interpreter shell and should not be used in programs.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • -1 exit is not really in the builtins. it's injected by site. if you run `python -S` to disable site, you'll find that exit is not there.. – wim Aug 02 '18 at 04:12
  • 1
    @wim It _is_ in `builtins`. The fact that it's injected there, rather than being part of `bltinmodule.c`, doesn't change that fact. But if you think my answer isn't long enough, I can always add even more information… – abarnert Aug 02 '18 at 04:14
  • "it's a builtin" is misleading or glossing over - it's an instance of [`Quitter`](https://github.com/python/cpython/blob/80b762f010097ab8137782e5fbdc89c5c620ed4e/Lib/_sitebuiltins.py#L13-L26) patched in by site. Kind of different than being a built-in IMO. – wim Aug 02 '18 at 04:19
  • 1
    @wim I don't think it's at all misleading. It's a callable object in `builtins`. And I already differentiated it from "the normal builtin functions" in the first paragraph, and explained how it's not just a function at the end. But to make you happy, I added a section about how it ends up in `builtins` (at least in CPython and PyPy). – abarnert Aug 02 '18 at 04:23
  • @wim Also, would you say that, e.g., `SyntaxError` isn't built in because in Python 3 the builtin exceptions are injected into `builtin`, not compiled into the module? – abarnert Aug 02 '18 at 04:30
  • Not really, those are documented as "Built-in Exceptions", and as far as I know it's not possible to import builtins module without binding them. `exit`, `copyright`, etc are just documented as "constants added by the site module". There are other modules that also monkeypatch built-ins (for example IPython adds/modifies a few names), or you could patch your own in `sitecustomize.py` - it wouldn't elevate those objects to the status of "it's a builtin" just because the builtins module namespace is writable. – wim Aug 02 '18 at 13:47