Join our Newsletter — 33% off our NHI Course

What is the difference between runtime dependencies and build requirements in Python packaging?

Runtime dependencies are packages needed after installation, when the application runs. Build requirements are packages needed earlier, while a distribution is being prepared or compiled. In Python packaging, build requirements may be installed in an ephemeral environment and never appear in the final environment, which is why they can evade standard lockfile-based review unless the tooling explicitly includes them.

Why This Matters for Security Teams

Python packaging splits responsibility between what an application needs to install cleanly and what it needs to execute safely. That distinction matters because build-time packages often sit in a separate, short-lived environment, so they can introduce risk without showing up in the final runtime bill of materials. In practice, teams that only review locked runtime artifacts can miss the tooling, compilers, and helper libraries that shaped the shipped package.

This is a supply chain issue as much as a packaging issue. A build requirement can influence what code is produced, what metadata is embedded, and whether sensitive material is exposed during packaging or wheel creation. If the build step is compromised, the final artifact can be trustworthy-looking while still carrying malicious or altered output. SLSA is relevant here because it focuses attention on build integrity and provenance, which is exactly where this boundary becomes important.

Security teams often discover the problem only after a dependency review passes but the build pipeline has already injected risk through a separate path.

How It Works in Practice

Runtime dependencies are the packages your code imports or calls once the application is deployed. Build requirements are the packages that make packaging possible in the first place, such as build backends, compilers, code generators, or tooling that prepares source into a distributable artifact. The key operational difference is timing: runtime dependencies must exist in the target environment, while build requirements may exist only while the artifact is being created.

That timing changes how you review them. A runtime dependency is usually checked for compatibility, patch level, and exposure in the deployment environment. A build requirement should be checked for trust in the build pipeline, because it can affect artifact integrity even if it never ships with the application. If the build environment is ephemeral, the dependency can still matter a great deal because it may:

  • execute code during package preparation or wheel creation,
  • pull additional transitive tools into the build step,
  • influence generated files, metadata, or compiled extensions,
  • touch secrets, signing material, or source repositories during CI.

That is why build requirements need provenance, pinning, and isolation controls, not just runtime review. For teams managing Python packaging at scale, SLSA helps frame the stronger expectation: you do not just want a working build, you want a verifiable build path. These controls tend to break down when packaging is delegated to ad hoc CI jobs that install whatever the build backend asks for without recording exactly what was trusted.

Common Variations and Edge Cases

Tighter build isolation often increases operational overhead, so teams have to balance repeatability against convenience. That trade-off becomes visible when a project uses native extensions, generated code, or modern packaging backends that resolve extra tooling dynamically.

Some cases blur the line. A dependency may be required both to build and to run, especially when the same library provides code generation at build time and functionality at runtime. Editable installs, source distributions, and wheel builds can also behave differently, so the same project may expose different dependency sets depending on how it is installed. Current guidance suggests treating the build graph as its own review surface instead of assuming it is fully captured by the runtime lockfile.

The most important edge case is hidden build execution. If packaging tools can fetch or execute extra components during build, the security question is no longer just “what does the application import?” but also “what code did the build system trust to produce the artifact?” For that reason, teams should treat unpinned or implicitly installed build requirements as higher risk than ordinary runtime packages. OWASP ASVS is useful as a general benchmark for verifying that the surrounding application environment still enforces strong control over authorization, integrity, and build-adjacent inputs.

Risk and Threat Considerations

The main risk is supply-chain exposure during artifact creation. Build requirements can be abused to alter outputs, introduce malicious code, or expose secrets in CI/CD environments, even when the final runtime environment looks clean. This is especially dangerous because the risk is often invisible to runtime-focused review.

Failure mechanism: An attacker targets the build path, not the deployed application. That can happen through a compromised build dependency, dependency confusion, malicious package update, or a build backend that executes unexpected code while resolving or preparing artifacts.

Impact: The shipped package can be corrupted, backdoored, or rebuilt with hidden changes, and reviewers may miss the compromise if they only inspect final runtime dependencies.

Practitioner Guidance

What to verify: Confirm whether your packaging toolchain distinguishes runtime from build requirements explicitly in the artifact review process. If it does not, treat build dependencies as a separate approval surface and record them separately from the runtime lockfile.

Common mistake: Assuming that a clean runtime dependency list means the package build was safe. The stronger control is to verify both what the application will load at runtime and what the build pipeline was allowed to execute while producing it.

Practitioner takeaway: The decisive security question is not only what your Python application runs, but what the build system was trusted to execute on the way there.