Python site: Understand Package Paths

Published on: August 14, 2026
Reading time: 6 minutes
Directory diagram representing site-packages paths and Python site module configuration

The Python site module participates in interpreter startup and configures installation-specific import paths. It adds site-packages directories to sys.path, processes .pth files, attempts to import sitecustomize and usercustomize, and helps configure history and completion in interactive sessions.

These actions explain why installed distributions become importable and why two executions of the same Python version may have different paths. They also create a sensitive startup surface: executable lines in .pth files run on every interpreter launch, while customization modules can execute arbitrary code before the application starts.

Automatic import during startup

Python normally imports site automatically. The -S option disables that step.

python -S -c "import sys; print(sys.path)"

With -S, site-specific directories and helper builtins are not added. Since Python 3.14, virtual-environment values for sys.prefix and sys.exec_prefix are configured during path initialization and no longer depend on the site module.

Call site.main() explicitly

If Python started with -S, importing site does not automatically apply the normal modifications. Call site.main() to request them.

import site
site.main()

Libraries should not do this. Changing sys.path at runtime is global and may surprise unrelated components. The application’s entry point should own that decision.

How site directories are built

The module combines heads such as sys.prefix and sys.exec_prefix with platform-specific tails. On Unix, a typical directory is lib/pythonX.Y/site-packages; on Windows, it is usually Lib/site-packages.

Free-threaded builds may include a t suffix, such as python3.13t. Do not build these paths manually. Use site functions or Python sysconfig.

Read global site-packages paths

getsitepackages() returns recognized global site directories.

import site

for path in site.getsitepackages():
    print(path)

Embedded or unusual installations may behave differently. Portable tools should handle empty or unavailable results and respect the active virtual environment.

Read the user site path

getusersitepackages() returns the user-specific package directory.

import site

print(site.getusersitepackages())
print(site.ENABLE_USER_SITE)

A computed path does not prove that it was added to sys.path. Inspect ENABLE_USER_SITE.

Interpret ENABLE_USER_SITE

The flag has three meaningful states:

  • True: enabled and added to the search path.
  • False: disabled by the user through -s or PYTHONNOUSERSITE.
  • None: disabled for security reasons or by an administrator.

Do not collapse the value with bool() when you need to distinguish user preference from security policy.

Disable user packages

The -s option disables the user site:

python -s -c "import site; print(site.ENABLE_USER_SITE)"

PYTHONNOUSERSITE provides similar behavior. Services, scheduled jobs, and reproducible environments often benefit from excluding personal packages.

USER_BASE and PYTHONUSERBASE

getuserbase() returns the base used by the user installation scheme. PYTHONUSERBASE can override the default.

import site

print(site.getuserbase())
print(site.USER_BASE)

Changing this variable affects where installers place user scripts, modules, and data. Set it before Python starts and document it in build pipelines.

Use python -m site

The command-line interface prints sys.path, the user base, user site, and enablement state.

python -m site
python -m site --user-base
python -m site --user-site

When user-directory options are supplied, the exit code indicates whether the user site is enabled, disabled by the user, or blocked for security or administrative reasons.

Path configuration files

Files ending in .pth inside site directories are processed in alphabetical order. Ordinary lines add existing paths to sys.path. Blank lines and comments are ignored.

# example.pth
/opt/my_app/libs
/opt/my_app/plugins

Nonexistent paths are skipped and duplicates are avoided. The module does not require an entry to be a directory; an existing file can also be added.

Alphabetical order affects imports

File names determine processing order, which can affect module precedence. If the same import name exists in two locations, the final sys.path order determines which one wins.

Avoid undocumented naming tricks such as 00-first.pth. Prefer clean virtual environments and ordinary package installation.

Executable lines in .pth files

A line beginning with import or import is executed on every interpreter startup.

import my_startup_hook

This happens even when the application never uses the related package. The one-line restriction is deliberate and discourages complex startup logic.

Security risk of .pth execution

Anyone able to write into a site-packages directory can potentially gain persistent code execution in every Python process using that environment. Protect permissions and treat .pth files as executable configuration.

from pathlib import Path
import site

for directory in site.getsitepackages():
    for file in Path(directory).glob('*.pth'):
        print(file)
        for line in file.read_text(errors='replace').splitlines():
            if line.startswith(('import ', 'import\t')):
                print('  executes:', line)

.pth file encoding

Since Python 3.13, site first decodes .pth files as UTF-8 and falls back to the locale encoding. Use UTF-8 for predictability and avoid unnecessary non-ASCII characters in infrastructure paths.

Add a site directory programmatically

addsitedir() adds a directory and processes its .pth files.

import site

site.addsitedir('/opt/my_app/site-packages')

This changes global import state and may execute startup lines. Never pass an untrusted path. Plugin systems should prefer controlled installation and process restarts.

sitecustomize

After path processing, Python tries to import sitecustomize. Administrators can use it for small global policies, audit hooks, encoding choices, or corporate startup configuration.

A missing module is silently ignored, but other exceptions can produce confusing startup failures. Keep the module small, tested, and independent of fragile external services.

usercustomize

When the user site is enabled, Python attempts to import usercustomize from the user site-packages directory.

It can customize trusted interactive sessions, but applications should not depend on it. Production services often disable user site to prevent personal preferences from altering behavior.

Avoid printing during startup

Output from customization modules may corrupt protocols, JSON-producing tools, and command-line programs. With pythonw.exe, output may be silently discarded.

Log only when explicitly enabled and send messages to an appropriate protected destination.

Automatic readline configuration

In interactive mode without -S, site configures rlcompleter and history when readline is available. The history file is commonly ~/.python_history.

For completion behavior and dynamic-attribute side effects, see Python rlcompleter.

Disable the interactive hook

sys.__interactivehook__ controls this setup. A customization module can remove it.

import sys

if hasattr(sys, '__interactivehook__'):
    del sys.__interactivehook__

Do this only when you own the interactive experience. A library should not modify the hook globally.

Virtual environments and pyvenv.cfg

pyvenv.cfg may contain include-system-site-packages = true. When false, the environment excludes global packages.

For reproducible projects, keep it false and install every dependency into the venv. Global packages can hide undeclared requirements.

Inspect virtual-environment prefixes

import sys

print('prefix:', sys.prefix)
print('base_prefix:', sys.base_prefix)
print('venv:', sys.prefix != sys.base_prefix)

In Python 3.14, these values remain correct even with -S.

site or sysconfig?

Use site to inspect active directories, user-site status, and startup customization. Use sysconfig for structured installation and build schemes.

Do not infer cross-platform rules from one observed path on one machine.

Diagnose an unexpected import

import module
import sys

print(module.__file__)
print('\n'.join(sys.path))

Then inspect .pth files, PYTHONPATH, user site, virtual-environment settings, and customization modules. Python pkgutil can list available modules.

PYTHONPATH interaction

PYTHONPATH influences startup search paths before several site operations. A global value can affect every Python environment.

Avoid setting it permanently system-wide. Prefer a controlled editable installation or virtual-environment configuration.

Isolated mode with -I

The -I option ignores PYTHON* environment variables and disables user site, among other protections.

python -I -c "import sys; print(sys.path)"

It is useful for administrative tools that should be less influenced by a user’s environment, although installation-level customizations still need review.

Test sitecustomize safely

Use a temporary virtual environment, place the module in its site-packages directory, and launch a subprocess.

import subprocess

result = subprocess.run(
    ['.venv/bin/python', '-c', 'print("ok")'],
    text=True,
    capture_output=True,
    check=True,
)
assert result.stdout.strip() == 'ok'

Test normal startup, -S, -s, errors, and missing output streams. Never experiment in global Python.

Audit startup differences

python -m site
python -s -m site
python -S -c "import sys; print(sys.path)"
python -I -m site

The differences reveal the effects of user site, the site module, and isolated mode.

Common mistakes

  • Adding user-controlled paths with addsitedir().
  • Putting complex logic in a .pth import line.
  • Using sitecustomize as a plugin system.
  • Printing to stdout during startup.
  • Depending on global packages inside a venv.
  • Confusing an existing user-site directory with an enabled one.
  • Building site-packages paths manually.

Best practices

  • Use clean reproducible virtual environments.
  • Protect site-packages permissions.
  • Audit executable .pth lines.
  • Keep customization modules minimal.
  • Disable user site in services.
  • Use sysconfig for structured paths.
  • Test startup in subprocesses.

Conclusion

The Python site module explains much of automatic import configuration: site-packages directories, .pth files, user packages, and customization hooks.

This convenience runs before the application and demands strict control. Protect directories, avoid complex startup logic, and use virtual environments. Consult the official site documentation and the sys.path initialization documentation.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Developer configuring a Python HTTPS server and TLS certificate
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: Build a Local HTTPS Server in Python

    Learn Python HTTPSServer for local HTTPS services, TLS certificates, threaded handling, testing, and practical security limits.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Protected files representing secure TAR extraction with Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: Extract TAR Safely

    Learn Python tarfile extraction_filter for safer TAR extraction, path validation, links, permissions, and resource limits.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Python code showing warnings controlled with catch_warnings
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: Capture Python Warnings in Tests

    Learn Python catch_warnings to capture, test, and control warnings with focused filters and safe temporary scopes.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Python code representing persistent pickle references
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: Serialize External References

    Learn Python pickle persistent_id for stable external references, validation, security, performance, and long-term compatibility.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Binary code representing Python buffers and memory views
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: Count Values Without Buffer Copies

    Learn Python memoryview.count to count bytes and values in buffers without copies, with formats, limits, and practical safety.

    Ler mais

    Tempo de leitura: 5 minutos
    06/10/2026
    Python code and type annotations
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: Avoid Circular Imports in Annotations

    Learn Python annotationlib to inspect deferred annotations, avoid circular imports, and build safer runtime tooling.

    Ler mais

    Tempo de leitura: 5 minutos
    06/10/2026