Python py_compile: Compile One File

Published on: August 27, 2026
Reading time: 7 minutes
Detailed view of code and file structure in a software development environment.

The py_compile module compiles one Python source file into a .pyc bytecode cache. It is useful when an editor, incremental build, installer, generator, or validation service needs precise control over one file instead of compiling a complete directory tree.

The API can choose the output path, define the logical filename shown in tracebacks, select an optimization level, and configure how the cache is invalidated. For entire projects, compileall is usually more convenient; for file-by-file workflows, py_compile is the focused tool.

Compile a source file

The primary function is py_compile.compile().

import py_compile

pyc_path = py_compile.compile(
    "module.py",
    doraise=True,
)
print(pyc_path)

When compilation succeeds, the function returns the path of the generated bytecode file.

Use doraise in automation

With doraise=True, a compilation failure raises PyCompileError.

try:
    py_compile.compile("module.py", doraise=True)
except py_compile.PyCompileError as error:
    print(error)

This is normally the clearest behavior for CI, editors, code generators, and package builders because the caller can stop immediately and report a structured error.

Behavior without doraise

When doraise=False, the module may print the compilation message to stderr instead of raising it directly.

Always inspect the return value. An old .pyc file may still exist after a failed attempt and should not be treated as proof that the current source compiled.

PyCompileError details

PyCompileError preserves information about the original syntax or compilation exception and provides a formatted message.

A diagnostic interface should keep filename, line, column, and error type. It can retrieve a short source excerpt with Python linecache.

The default output path

If cfile is omitted, Python computes the normal cache path under __pycache__.

__pycache__/module.cpython-314.pyc

The interpreter tag allows caches from different Python versions and optimization settings to coexist.

Choose a custom cfile

The cfile parameter selects a specific output file.

py_compile.compile(
    "src/module.py",
    cfile="build/module.pyc",
    doraise=True,
)

Create the parent directory first and make sure the destination belongs to the intended build workspace.

Protect output paths

The module checks selected cases where replacing the output would be unsafe, including relevant symbolic-link and non-regular-file situations.

Applications should still validate every destination, resolve it against an approved root, and avoid accepting arbitrary output paths from external input.

Atomic cache writing

Modern Python writes the compiled data to a temporary file and then replaces the target, reducing the risk of leaving a partial cache after an interruption.

The exact guarantees depend on the filesystem. Test network volumes, container mounts, and special storage when reliability matters.

Set the logical filename with dfile

The dfile argument controls the filename stored in the code object and later displayed in tracebacks.

py_compile.compile(
    "/build/work/module.py",
    dfile="/opt/app/module.py",
    doraise=True,
)

This is valuable when a build-worker path should not appear in production logs.

dfile does not copy source

The argument changes only the logical reference embedded in bytecode. It does not move the source or guarantee that the named path exists on the target system.

Choose a value that matches the final installation layout and preserve corresponding source when operators need readable diagnostics.

Select optimization

The optimize parameter chooses an optimization level.

py_compile.compile(
    "module.py",
    optimize=1,
    doraise=True,
)

The default follows the interpreter’s current optimization setting.

Assertions and docstrings

Optimization level 1 removes assertions, and level 2 can also remove docstrings.

Assertions are useful for development assumptions, but mandatory input validation and application rules must use explicit checks that remain active in every build.

Cache invalidation modes

The invalidation_mode parameter controls how the import system decides whether the bytecode still matches its source.

py_compile.compile(
    "module.py",
    doraise=True,
    invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
)

TIMESTAMP mode

TIMESTAMP records source metadata such as modification time and size.

It is efficient and commonly used, although builds that require deterministic artifacts may prefer a hash-based mode.

CHECKED_HASH mode

CHECKED_HASH stores a hash of the source and asks the import process to verify it.

This gives a stronger relationship between the content and its cache, with additional source reading according to the import policy.

UNCHECKED_HASH mode

UNCHECKED_HASH stores the source hash while allowing a managed environment to treat the artifact as already validated.

It fits controlled package systems that own installation and update decisions. Document the policy so stale caches cannot be introduced accidentally.

SOURCE_DATE_EPOCH

Reproducible-build environments may define SOURCE_DATE_EPOCH, which can influence the default invalidation behavior.

When the invalidation mode is part of the artifact contract, set it explicitly rather than relying only on environment defaults.

Compilation does not run the module

The compiler parses source and creates bytecode without executing imports, decorators, module-level calls, or class initialization.

A file can compile successfully and still fail during import because a dependency is missing or a runtime operation raises an exception.

Incremental editor validation

An editor can compile only the document that was saved.

def validate(path):
    try:
        py_compile.compile(path, doraise=True)
    except py_compile.PyCompileError as error:
        return False, str(error)
    return True, None

This quickly catches syntax issues, but it does not replace a language server, linter, or type checker.

Validate generated code

Code generators can compile their output immediately to confirm that templates produced valid Python.

A reliable sequence is to write source atomically, compile it, and publish the generated package only after success.

Source encoding

The compiler follows Python’s source-encoding rules. Invalid declarations or incompatible bytes produce errors.

Use tokenize.open() when inspecting source and see Python tokenize.

Keep filenames private and useful

Absolute build paths stored in code objects can reveal usernames, CI directories, and infrastructure layout.

Use dfile to create stable production paths while retaining enough detail for debugging.

Command-line usage

The module can compile several named files from the command line.

python -m py_compile module.py other.py

CI should check the command’s exit status. Consult the active interpreter’s help for exact command-line behavior.

Compile many files

The Python function compiles one file at a time. For a complete directory tree, use Python compileall.

Incremental systems can maintain a bounded queue and associate each result with the source revision that produced it.

Parallel compilation

Independent files can be compiled concurrently, but two workers must not write the same output path.

Generate deterministic destinations, remove duplicate jobs, and keep concurrency appropriate for the filesystem.

Source changes during compilation

A file may change between scheduling and result publication. Editors should compare a document version or content hash before accepting a result.

If the version differs, discard the stale compilation and process the latest source.

Read-only application directories

When runtime directories are read-only, compile during image creation or choose a writable build destination.

A production service should not need elevated privileges merely to create __pycache__.

Version compatibility

Bytecode is tied to a Python implementation and major/minor version. A magic number prevents many incompatible loads.

Compile with the same interpreter that will run the application and do not publish one .pyc as a universal artifact.

Bytecode is not source protection

Inspection tools can recover names, constants, metadata, and instructions from compiled files.

Keep secrets outside source and bytecode, and do not rely on .pyc as strong obfuscation.

Run an import smoke test

After compilation, import the module in the target environment.

python -c "import module"

This detects missing dependencies and initialization behavior that syntax compilation cannot see.

Remove orphaned artifacts

Deleted source files can leave old caches behind. A clean build directory prevents removed modules from surviving in a package.

Never mix caches from several commits, interpreters, or optimization settings without an intentional layout.

Path controls

Validate source and output paths, parent directories, and symbolic links. Keep every compilation inside a defined workspace.

A public compilation service should use a separate worker process and isolated temporary directory.

Resource limits

Very large or deeply nested source can consume substantial CPU and memory. Limit source size, concurrent jobs, and total compilation time.

Run public or uploaded-source validation away from the main request process.

Observability

Record logical filename, duration, source size, Python version, optimization level, invalidation mode, and outcome.

Avoid logging entire source files. A brief diagnostic excerpt is normally sufficient.

Testing

Test valid code, SyntaxError, invalid encoding, custom output, dfile, each invalidation mode, optimization, permission failures, symbolic-link destinations, and source changes during the job.

Verify both the generated cache path and the filename shown in a traceback.

Common mistakes

Common failures include omitting doraise=True in automation, ignoring the result, writing to an unchecked destination, compiling with a different Python version, relying on assertions for mandatory rules, confusing dfile with file copying, and leaving stale caches in builds.

Conclusion

py_compile offers precise control over compiling one Python source file. Use doraise=True, select safe cfile and dfile values, define optimization and invalidation according to build policy, and test imports in the final environment.

Use compileall for directory trees. Consult the official py_compile documentation and Python dis to inspect the resulting bytecode.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Colorful stacked shipping containers at Hamburg port, showcasing global trade and logistics.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python modulefinder: Discover Imports

    Learn Python modulefinder to discover imports, transitive dependencies, missing modules, paths, plugins, and limitations of static analysis.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python compileall: Generate .pyc Bytecode

    Learn Python compileall to generate .pyc files, validate syntax, compile in parallel, control optimization, paths, and reproducible builds.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Vivid close-up of code on a computer screen showcasing programming details.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python codeop: Compile Interactive Input

    Learn Python codeop to detect complete, incomplete, or invalid commands, build REPLs, and preserve __future__ flags safely per session.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python linecache: Read Source Lines

    Learn Python linecache to retrieve source lines, refresh cached files, support tracebacks and loaders, preserve indentation, and secure paths.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python faulthandler: Diagnose Crashes

    Learn Python faulthandler to diagnose crashes, deadlocks, fatal signals, timeouts, and hangs with stack dumps from every thread.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026
    Detailed view of programming code in a dark theme on a computer screen.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python symtable: Analyze Name Scopes

    Learn Python symtable to analyze scopes, locals, globals, parameters, imports, nonlocals, closures, and compiler namespaces.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026