Course outline

Python / Reliable programs / Lesson 7

Modules, packages, and isolated environments

Organize code into importable boundaries and distinguish an import package from an installed distribution and a virtual environment.

About 5 min readContent reviewed
On this page

Lesson goals

  • A module owns a namespace
  • Record reproducibility and boundaries

A module owns a namespace

A module is an importable unit, commonly a Python source file, that provides a namespace. import math binds the module name, and math.isqrt selects one of its attributes. from math import isqrt binds that attribute directly in the current namespace. Explicit imports help readers discover where behavior comes from; wildcard imports obscure that information and invite collisions. Imports normally execute a module’s top-level code on first loading in a process and then reuse its cached module object. Therefore top-level network calls, prompts, or file modifications make a library surprising and can cause trouble before a caller even invokes a function.

Packages and entry points

A regular import package groups modules in a directory containing __init__.py; namespace packages are a separate supported mechanism without that regular-package marker. Keep an ordinary beginner package explicit before introducing more flexible layouts. Relative imports refer to the package’s structure and work best when the program is invoked as a module with python -m package.module. The __name__ value distinguishes importing a module from executing it as the main entry point. Put command-line orchestration in main and call it under the __name__ == "__main__" guard, leaving reusable functions available without triggering the application.

Use the library before adding dependencies

The standard library offers well-defined modules for many routine tasks. The first example uses integer square root, which avoids float conversion and gives the largest integer whose square does not exceed the input. Import only what clarifies the program; shortening every name is not a goal. A file named math.py in the working project can shadow the real library module, so choose project module names deliberately. Avoid diagnosing import problems by repeatedly changing search paths: first identify which interpreter runs, where the current module lives, and which module path was actually imported. These facts usually reveal the hidden mismatch.

python Use the library before adding dependencies — worked example
import math

print(math.isqrt(80))
print(math.isqrt(81))
Expected output
8
9

Eight squared is at most eighty while nine squared is larger; eighty-one has the exact integer square root nine.

An environment selects installed packages

A virtual environment isolates an interpreter’s package installation context from other projects. Create one with the intended interpreter using python -m venv .venv; activation merely adjusts shell convenience, and invoking the environment’s interpreter directly also works. Use that interpreter’s -m pip so package management targets the same environment. These are setup commands rather than runnable course examples, and no package installation is required here. An import package name and a distribution name are not guaranteed to match. The metadata example inspects the standard-library module itself, avoiding assumptions about installed third-party distributions and making the visible result portable.

python An environment selects installed packages — worked example
import importlib.util
import sys

print(importlib.util.find_spec("json") is not None)
print("Python", sys.version_info.major)
Expected output
True
Python 3

find_spec locates the standard json module without depending on third-party installation metadata. The major version remains stable across supported Python 3 interpreters.

Record reproducibility and boundaries

An environment is disposable configuration, not a folder to copy into version control or a security sandbox. Record supported Python versions and dependencies in project metadata, and regenerate environments from those records. Dependency pinning for an application and compatibility ranges for a reusable library solve different problems. A small public API helps modules evolve internally without forcing changes throughout the project. Keep configuration loading and command-line parsing near the outer boundary, then pass values into internal functions. When an import fails, verify package layout, invocation method, environment, and name shadowing in that order before inventing fallback imports that silently hide a broken installation.

Common errors and fixes

ModuleNotFoundError after installing a dependency

Why it happens
Installation and execution targeted different interpreters.
How to fix it
Use the running environment’s interpreter with -m pip.
Prevent it next time
Record interpreter selection in editor and shell configuration.

A local file hides a standard module

Why it happens
The project filename collides with an importable library name.
How to fix it
Rename the local module and inspect the imported module’s path.
Prevent it next time
Choose domain-specific module names instead of library names.

Importing a helper launches the whole program

Why it happens
Top-level code performs application orchestration immediately.
How to fix it
Move orchestration into main and guard its invocation.
Prevent it next time
Test importing modules without user interaction or external side effects.

Check your understanding

1. Explain how you would split calculation code from a command-line entry point.

Show worked solution

Put reusable pure functions in a domain module. In a separate entry module, parse arguments, call those functions, format results, and expose main. Guard the main call, then run with python -m the_package.entry. Importing the domain module should not parse arguments or write files.

2. Why is copying a .venv directory to another machine an unreliable deployment plan?

Show worked solution

The environment may embed interpreter paths and contain platform-specific artifacts. Recreate it with a supported interpreter and recorded dependency metadata on the target system, then verify imports and tests there. The portable unit is the source and reproducible specification, rather than assumptions about one machine’s environment directory.

Key takeaways

  • Imports expose namespaces and execute initialization.
  • Use the intended interpreter for both execution and installation.
  • A virtual environment isolates dependencies but is not a security boundary.

Official references