Python tutorials > Deployment > Packaging > How to create `setup.py`?

How to create `setup.py`?

The setup.py file is the cornerstone of Python packaging. It's used by setuptools to build, distribute, and install your Python package. This tutorial will guide you through creating a functional setup.py, explaining each component in detail.

Basic Structure of `setup.py`

This code snippet represents a basic setup.py file. Let's break down each argument passed to the setup() function:

  • name: The name of your package. This should be unique on PyPI.
  • version: The current version of your package. Follow semantic versioning (SemVer) for best practices.
  • packages: A list of packages to include in your distribution. find_packages() automatically discovers all packages and subpackages in your project. The include argument allows you to specify which packages to include using wildcards.
  • install_requires: A list of dependencies required by your package. setuptools will automatically install these when your package is installed.
  • python_requires: Specifies the minimum Python version required to run your package.
  • author: The author's name.
  • author_email: The author's email address.
  • description: A short, one-line description of your package.
  • long_description: A longer, more detailed description of your package. Typically read from a README.md file.
  • long_description_content_type: Specifies the format of the long_description (e.g., 'text/markdown' for Markdown).
  • url: The URL of your package's homepage (e.g., GitHub repository).
  • license: The license under which your package is distributed (e.g., 'MIT').
  • classifiers: A list of Trove classifiers that categorize your package. These help users find your package on PyPI. A full list can be found on the Python Package Index website.

from setuptools import setup, find_packages

setup(
    name='your_package_name',
    version='0.1.0',
    packages=find_packages(include=['your_package_name', 'your_package_name.*']),
    install_requires=[
        'requests',
        'numpy',
    ],
    python_requires='>=3.6',
    author='Your Name',
    author_email='your.email@example.com',
    description='A short description of your package',
    long_description=open('README.md').read(),
    long_description_content_type='text/markdown',
    url='https://github.com/yourusername/your_package_name',
    license='MIT',
    classifiers=[
        'Development Status :: 3 - Alpha',
        'Intended Audience :: Developers',
        'Topic :: Software Development :: Libraries',
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Programming Language :: Python :: 3.8',
        'Programming Language :: Python :: 3.9',
        'Programming Language :: Python :: 3.10',
        'Programming Language :: Python :: 3.11',
    ],
)

Concepts Behind the Snippet

The core concept is to describe your Python project's metadata and dependencies in a way that setuptools can understand. This allows for easy installation and distribution using tools like pip. The find_packages() function is crucial for automatically including all your project's modules without having to manually list them.

Real-Life Use Case Section

Imagine you've developed a Python library for data analysis. Your setup.py would specify numpy, pandas, and matplotlib as install_requires. When a user installs your library using pip install your_package, these dependencies will be automatically installed alongside your library, ensuring that it functions correctly.

Best Practices

  • Keep it concise: Only include necessary information in setup.py.
  • Use find_packages(): Avoid manually listing packages unless you have a very specific reason to do so.
  • Specify dependencies accurately: Use version specifiers (e.g., requests>=2.20) to ensure compatibility.
  • Maintain a good README.md: Provide a clear and comprehensive description of your package.
  • Use classifiers appropriately: Help users find your package by using relevant classifiers.

Interview Tip

Be prepared to explain the purpose of setup.py and the role of setuptools. Also, understand the difference between install_requires and extras_require. Demonstrate familiarity with best practices for Python packaging.

When to use them

You should use setup.py whenever you want to create a reusable Python package that can be easily installed and distributed. This is essential for sharing your code with others and making it accessible through PyPI.

Alternatives

While setup.py with setuptools is the traditional approach, alternative packaging tools exist:

  • Poetry: A dependency management and packaging tool.
  • Pipenv: Another dependency management tool that integrates virtual environments.
  • Flit: A simpler packaging tool that focuses on ease of use.
These tools often provide a higher-level interface and automate many of the tasks involved in packaging.

Adding Data Files

To include data files (e.g., configuration files, templates, or data sets) in your package, you can use the include_package_data, package_data, and data_files arguments:

  • include_package_data=True: Tells setuptools to automatically include any files matched by the MANIFEST.in file.
  • package_data: A dictionary that maps package names to lists of filename patterns. These files will be installed within the package directory. For example, the code above includes all .txt files in the data directory and all .html files in the templates directory within the your_package_name package.
  • data_files: A list of tuples, where each tuple contains an installation directory and a list of files to install in that directory. The installation directory is relative to the installation prefix (e.g., /usr/local on Linux).

from setuptools import setup, find_packages
import os

setup(
    name='your_package_name',
    version='0.1.0',
    packages=find_packages(include=['your_package_name', 'your_package_name.*']),
    install_requires=[
        'requests',
        'numpy',
    ],
    python_requires='>=3.6',
    author='Your Name',
    author_email='your.email@example.com',
    description='A short description of your package',
    long_description=open('README.md').read(),
    long_description_content_type='text/markdown',
    url='https://github.com/yourusername/your_package_name',
    license='MIT',
    classifiers=[
        'Development Status :: 3 - Alpha',
        'Intended Audience :: Developers',
        'Topic :: Software Development :: Libraries',
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Programming Language :: Python :: 3.8',
        'Programming Language :: Python :: 3.9',
        'Programming Language :: Python :: 3.10',
        'Programming Language :: Python :: 3.11',
    ],
    include_package_data=True,
    package_data={'your_package_name': ['data/*.txt', 'templates/*.html']},
    data_files=[('config', ['config/settings.ini'])]
)

FAQ

  • What is the purpose of `MANIFEST.in`?

    The MANIFEST.in file is used to specify additional files to include in your package that are not automatically included by setuptools. This is often used for data files, documentation, or other non-code assets. You can specify patterns using wildcards (e.g., include *.txt).

  • How do I handle versioning?

    Use semantic versioning (SemVer). The version string should be in the format MAJOR.MINOR.PATCH. Increment the MAJOR version for incompatible API changes, the MINOR version for added functionality in a backwards-compatible manner, and the PATCH version for bug fixes.

  • How to run tests during installation?

    You can integrate testing into your setup process by using the test_suite or tests_require arguments in setup.py. Define a test suite or specify test dependencies. This allows running tests automatically when the package is installed or built.