dcreager.net

Importing a nested module does not create a shim

You can import a nested module using the typical dot notation: import os.path. This creates a binding in the current file-global scope called os. You must access the imported nested module (and its contents) using its full path: os.path.join.

Nothing unexpected so far. But at this point, there are a few possibilities:

  • os is just a shim object of some kind, and only contains the nested path module. You don't have access to any definitions in os unless you import explicitly that as well.
  • os is the actual os package. You have access to all of its definitions, as well as the path submodule, but not to any other submodules, unless you import them.
  • os is the actual os package. You have access to all of its definitions, the path submodule, and all other submodules in the package, but not any sub-submodules.
  • os is the actual os package. You have access to all of its definitions, the path submodule, and all other submodules and sub-submodules (etc) in the package.

I can't find if it's explicitly documented anywhere, but it seems Python implements option (2): importing os.path also implicitly imports os, but not any other submodules unless you explicitly import them.

A false start

Note that I first tried with logging—I thought it would be a better test since it contains more than one submodule.

But! This is a false positive. This behavior occurs because logging.config itself imports logging.handlers!

Python 3.12.7 (main, Oct  1 2024, 11:15:50) [GCC 14.2.1 20240910] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import logging
>>> logging
<module 'logging' from '/usr/lib/python3.12/logging/__init__.py'>
>>> logging.config
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'logging' has no attribute 'config'
>>> import logging.config
>>> logging
<module 'logging' from '/usr/lib/python3.12/logging/__init__.py'>
>>> logging.Logger
<class 'logging.Logger'>
>>> logging.config
<module 'logging.config' from '/usr/lib/python3.12/logging/config.py'>
>>> logging.config.fileConfig
<function fileConfig at 0x71a0d3dcdda0>
>>> logging.handlers
<module 'logging.handlers' from '/usr/lib/python3.12/logging/handlers.py'>
>>> logging.handlers.WatchedFileHandler
<class 'logging.handlers.WatchedFileHandler'>

» Languages » Python