Understanding Python Manifest Files: Part 1

As a Python developer, efficiently managing dependencies is a fundamental aspect of building robust and reliable software projects. Python, with its rich history, offers a diverse array of manifest files that play a pivotal role in specifying your project's dependencies. In this blog, we'll delve into the various Python manifest files used to declare dependencies and how they collaborate seamlessly.

Naresh Devasani
Naresh Devasani

What are Manifest Files

Manifest files in Python serve as configuration files that provide essential information about your project. They allow you to define project metadata, dependencies, entry points, and other crucial details that empower package managers and tools to comprehend and manage your codebase effectively. These manifest files are pivotal in managing dependencies, ensuring version compatibility, enhancing reproducibility, and maintaining the overall stability and security of Python projects. They provide a clear and structured approach to handling a project's external dependencies, facilitating easier development, maintenance, and collaboration within Python software.

This blog covers requirements.txt, setup.py, setup.cfg and pyproject.toml files.


The requirements.txt file is a common way to list project dependencies explicitly. It's often used during development and deployment to install the specified packages. Here's a straightforward requirements.txt file:

Follow this link for more information on how requirements.txt can be leveraged: https://pip.pypa.io/en/stable/reference/requirements-file-format/

"requirements.txt" is primarily focused on specifying project dependencies. It's valuable for managing dependencies during development and deployment but doesn't address the broader concerns related to packaging and distribution.


The setup.py file is of paramount importance in the distribution and installation of Python packages. It operates in conjunction with the setuptools library to define project metadata, package structure, and dependencies. Here's a straightforward example of a setup.py file:

This example showcases the setup() function call, which specifies the project's name, version, included packages, and required dependencies through the install_requires parameter.


setup.cfg complements setup.py by serving as a configuration file that defines a package's metadata and other essential options typically provided to the setup() function. This declarative approach not only facilitates automation but also reduces boilerplate code.

  1. Automation Scenarios:
  • setup.cfg enables you to articulate project metadata and packaging details in a structured and declarative manner. This simplifies the automation of various package management tasks.
  • Tasks such as building source distributions, creating wheels, and generating documentation can be automated by specifying package dependencies, entry points, and other settings in setup.cfg.
  • Continuous Integration (CI) systems and packaging tools can effortlessly access information from setup.cfg to execute actions like building and publishing packages automatically.
  1. Reducing Boilerplate Code:
  • Traditionally, much of the metadata and settings required for packaging were embedded directly within the setup.py script, leading to substantial boilerplate code.
  • With the adoption of setup.cfg, a portion of this repetitive code is moved to a distinct configuration file. This streamlined approach enhances the clarity of the setup.py script, rendering it more concise and focused on executing essential packaging tasks.

Here's a comparison to illustrate the difference:

Without setup.cfg (in setup.py):

With setup.cfg and a simplified setup.py:


In the second example, the majority of the metadata and configuration details are encapsulated within setup.cfg, resulting in a more streamlined setup.py. This separation enhances code organization and readability, particularly in the context of larger projects.


Before the introduction of pyproject.toml-based builds (as defined in PEP 517 and PEP 518), pip primarily supported package installations via setup.py files built using setuptools. With the advent of PEP 621, the Python community embraced pyproject.toml as the standardized approach for specifying project metadata. Pyproject.toml, while not replacing setup.py, complements it by segregating the build and packaging process from project metadata.

The notable advantage of pyproject.toml lies in its ability to define build environments and dependencies consistently for the construction and distribution of Python packages. In practice, many projects now utilize both setup.py and pyproject.toml. Pyproject.toml specifies build configurations and dependencies, while setup.py retains its role in defining package metadata and distribution specifics. This dual approach allows projects to harness modern build processes while retaining compatibility with legacy packaging tools and systems.

Here's an example of a pyproject.toml file:

In summary, while pyproject.toml serves as a potent and contemporary means of declaring dependencies and project metadata, there are scenarios where setup.py or setup.cfg remain relevant for compatibility, customization, or legacy purposes. The choice between utilizing these files alongside pyproject.toml or exclusively depends on the specific requirements of your project and the ecosystem it operates within.
While this blog primarily focuses on dependency management within the discussed manifest files, it's important to recognize that these files offer a wide range of capabilities and configurations.

Effective Dependency Management

To ensure effective dependency management in your Python projects, consider the following best practices:

  1. Be Explicit: Clearly define your project's dependencies within the chosen manifest file. Specify version constraints to ensure compatibility and reproducibility.
  2. Regular Updates: Keep your dependencies up-to-date by routinely checking for updates and maintaining your manifest files accordingly.
  3. Documentation: Include comprehensive instructions in your project's documentation on how to set up the environment and install dependencies using the selected manifest file(s).
  4. Continuous Integration: Leverage continuous integration tools to automatically test your project across various Python versions and dependency configurations, thereby identifying potential issues at an early stage.
  5. Security: Prioritize security by incorporating Endor Labs' Open Source Governance tools into your workflow. These tools offer automatic analysis of various manifest files within the Python ecosystem, and will even catch “phantom dependencies” that aren’t declared in the manifest file. 


In essence, manifest files play a pivotal role in Python projects by defining dependencies. However, are they an absolute necessity? Not necessarily. Developers have the flexibility to manually create scripts for acquiring packages or even copy files manually. When a codebase incorporates a dependency not listed in the package manager's manifest files, it's termed a 'phantom dependency.' While relying on phantom dependencies isn't a recommended practice, it's worth noting that many organizations worldwide have yet to fully embrace manifest files. With the support of Endor Labs, you can enhance the security of your projects by addressing vulnerabilities in open-source software packages, even when dealing with phantom dependencies.