- pyproject.toml (Modern Python projects)
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "your-package-name"
version = "0.1.0"
description = "A brief description of your package"
authors = [{name = "Your Name", email = "your.email@example.com"}]
dependencies = [
"numpy>=1.20.0",
"requests>=2.25.0"
]
[project.optional-dependencies]
dev = ["pytest>=6.0", "black", "flake8"]
- setup.py (Alternative/Legacy approach)
from setuptools import setup, find_packages
setup(
name="your-package-name",
version="0.1.0",
packages=find_packages(where="src"),
package_dir={"": "src"},
install_requires=[
"numpy>=1.20.0",
"requests>=2.25.0",
],
python_requires=">=3.7",
)
- src/your_package/init.py
"""
Your Package Name
A brief description of what your package does.
"""
__version__ = "0.1.0"
# Import main classes/functions to make them available at package level
from .core import MainClass
from .utils import helper_function
__all__ = ["MainClass", "helper_function"]
Why init.py is required: This file marks the directory as a Python package, allowing imports like from your_package import MainClass. It also controls what gets exported when users do from your_package import *.
- README.md structure
# Project Name
Brief description of what your project does.
## Installation
```bash
pip install your-package-name
```
## Quick Start
```python
from your_package import MainClass
## Example usage
obj = MainClass()
result = obj.do_something()
```
## Documentation
Link to full documentation
## Contributing
Guidelines for contributors
## License
MIT License (or your chosen license)
- Directory structure example
pytest is an open-source testing framework available on PyPI with advanced test discovery capabilities, simplified workflow, and an extensive plugin ecosystem.
tests/test_core.py
import pytest from my_package.core
import add_numbers, divide_numbers
def test_add_positive_numbers():
assert add_numbers(2, 3) == 5
def test_add_negative_numbers():
assert add_numbers(-1, -1) == -2
def test_add_zero():
assert add_numbers(0, 0) == 0
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide_numbers(10, 0)
Install and Run:
# Install pytest
pip install pytest
# Run all package tests
pytest
# Run with coverage
pip install pytest-cov
pytest --cov=my_package
# Run one test module
pytest tests/test_core.py
# Run one specific test
pytest tests/test_core.py::test_add_positive_numbers
More Information: https://docs.pytest.org/en/stable/
Why pytest over unittest?
While Python's built-in unittest module is available without installation, pytest has become the industry standard for several reasons: it uses simple assert statements instead of specialized assertion methods (like self.assertEqual()), provides more detailed failure messages, offers powerful fixtures for test setup, has automatic test discovery, and includes an extensive plugin ecosystem for coverage reporting, parallel testing, and more. For beginners and experienced developers alike, pytest's cleaner syntax and better error messages make tests easier to write and debug, hence many open-source projects use this for testing as well.
Packaging and Development
With your code tested and working, the next step is packaging it for distribution. Modern Python projects use pyproject.toml as the central configuration file. This provides unified configuration and enhances reproducibility by precisely defining project dependencies for consistent builds.
Here's a basic pyproject.toml example:
toml
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "my-package"
version = "0.1.0"
description = "A simple Python package"
authors = [{name = "Your Name", email = "your.email@example.com"}]
dependencies = [
"requests>=2.28.0",
"numpy>=1.24.0"
]
[project.optional-dependencies]
dev = ["pytest>=7.0", "black>=23.0"]
[project.urls]
Homepage = "https://github.com/yourusername/my-package"
"Bug Tracker" = "https://github.com/yourusername/my-package/issues"
[project.scripts]
my-tool = "my_package.cli:main"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
]
Building Your Package
Python packages come in two main distribution formats: sdist and bdist. Source distribution (sdist) is a compressed archive of source code that requires user compilation and installation, is platform-independent, and has smaller file size. Built distribution (bdist) is a pre-compiled package for immediate installation with faster installation but may have system compatibility limitations and larger file size.
-
First install the build tool
This creates both source and built distributions in the dist directory.
Wheels are Python's standard pre-built packages that include Python source code, natively compiled code, and metadata. Benefits include faster installation (no compilation needed), no build toolchain headaches, reliable installation, easier dependency management, and works on multiple platforms including specialized systems like z/OS.
Troubleshooting Build issues
-
"No module named 'build'": Install build package: pip install build
-
"Package directory not found": Check [tool.setuptools.packages.find] in pyproject.toml
-
"Invalid version": Ensure version follows semantic versioning (e.g., "0.1.0")
Use semantic versioning following the major.minor.patch format (e.g., 1.2.3):
-
Major: Breaking changes (1.0.0 → 2.0.0)
-
Minor: New features, backward compatible (1.0.0 → 1.1.0)
-
Patch: Bug fixes (1.0.0 → 1.0.1)
Documentation and Maintenance
Good documentation and maintenance practices are essential for package adoption and long-term success.
README.md
Your README.md appears on your PyPI package page and should include: project description, installation instructions (`pip install my-package`), quick start code example, key features list, link to full documentation, and license information. Keep it concise - users should understand what your package does and how to use it within 30 seconds.
Licensing
Choose an open-source license and add a LICENSE file to your project root. Popular options include MIT (permissive, allows commercial use), Apache 2.0 (includes patent protection), and GPL (requires derivatives to be open source). Get license text from https://choosealicense.com/ and update the copyright year and name.
Changelog Management
Maintain a CHANGELOG.md file to track changes between versions. Use this format:
# Changelog
## [0.2.0] - 2026-06-15
### Added
- New feature X
### Fixed
- Bug in Y function
## [0.1.0] - 2026-06-01
### Added
- Initial release
Categories: Added (new features), Fixed (bug fixes), Changed (modifications), Deprecated (soon-to-be removed), Removed (deleted features), Security (vulnerability fixes).
Deployment
The final step is deploying your package. As mentioned earlier, you can publish to PyPI for public distribution or use a private repository for internal use. Choose the deployment method that aligns with your project's needs, security requirements, and target audience. Both options support standard pip installation once properly configured.
Conclusion
Building and publishing Python packages involves careful planning and execution across multiple stages. By following these steps - setting up a proper project structure, writing tests, configuring your package correctly, and publishing to PyPI - you contribute to the Python ecosystem while making your code accessible to developers worldwide.
Want to learn more about using Python on z/OS? Visit our Python Portal for a convenient way to access more resources.