python
examples
examples.pyšpython
"""
Python Packaging - Practical Examples
This file demonstrates project structure, configuration, and packaging concepts
"""
import os
import sys
import json
from pathlib import Path
from typing import Optional, Dict, Any
# ============================================================
# 1. PACKAGE STRUCTURE DEMO
# ============================================================
print("=" * 60)
print("1. PACKAGE STRUCTURE")
print("=" * 60)
def show_recommended_structure():
"""Display recommended package structure."""
structure = """
Recommended Python Package Structure:
my_package/
āāā src/ # Source directory (recommended)
ā āāā my_package/ # Package directory
ā āāā __init__.py # Makes it a package
ā āāā core.py # Main functionality
ā āāā utils.py # Helper functions
ā āāā cli.py # Command-line interface
ā āāā data/ # Package data files
ā āāā config.json
ā
āāā tests/ # Test directory
ā āāā __init__.py
ā āāā conftest.py # pytest fixtures
ā āāā test_core.py
ā āāā test_utils.py
ā
āāā docs/ # Documentation
ā āāā index.md
ā āāā api.md
ā
āāā pyproject.toml # Modern configuration (required)
āāā README.md # Project description
āāā LICENSE # License file
āāā CHANGELOG.md # Version history
āāā requirements.txt # Dependencies (optional with pyproject.toml)
āāā requirements-dev.txt # Dev dependencies
āāā .gitignore # Git ignore patterns
"""
print(structure)
show_recommended_structure()
# ============================================================
# 2. __init__.py EXAMPLES
# ============================================================
print("\n" + "=" * 60)
print("2. __init__.py EXAMPLES")
print("=" * 60)
init_simple = '''
"""Simple __init__.py - just marks directory as package."""
'''
init_with_version = '''
"""Package with version information."""
__version__ = "0.1.0"
__author__ = "Your Name"
__email__ = "you@example.com"
'''
init_with_exports = '''
"""Package with explicit exports."""
__version__ = "0.1.0"
# Import commonly used items for convenience
from .core import main_function, SecondaryClass
from .utils import helper_function
# Define what "from package import *" exports
__all__ = [
"main_function",
"SecondaryClass",
"helper_function",
]
'''
init_lazy_loading = '''
"""Package with lazy loading for performance."""
__version__ = "0.1.0"
def __getattr__(name):
"""Lazy import heavy modules only when accessed."""
if name == "heavy_module":
from . import heavy_module
return heavy_module
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
'''
print("Simple __init__.py:")
print(init_simple)
print("\nWith version:")
print(init_with_version)
print("\nWith exports:")
print(init_with_exports)
# ============================================================
# 3. pyproject.toml GENERATOR
# ============================================================
print("\n" + "=" * 60)
print("3. pyproject.toml GENERATOR")
print("=" * 60)
def generate_pyproject_toml(
name: str,
version: str = "0.1.0",
description: str = "",
author: str = "",
email: str = "",
python_version: str = "3.8",
dependencies: Optional[list] = None,
dev_dependencies: Optional[list] = None,
cli_entry_points: Optional[Dict[str, str]] = None,
) -> str:
"""Generate a pyproject.toml file content."""
deps = dependencies or []
dev_deps = dev_dependencies or ["pytest>=7.0", "black", "mypy", "ruff"]
toml_content = f'''[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "{name}"
version = "{version}"
description = "{description}"
readme = "README.md"
license = {{text = "MIT"}}
authors = [
{{name = "{author}", email = "{email}"}}
]
requires-python = ">={python_version}"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
'''
if deps:
toml_content += "dependencies = [\n"
for dep in deps:
toml_content += f' "{dep}",\n'
toml_content += "]\n"
else:
toml_content += "dependencies = []\n"
toml_content += f'''
[project.optional-dependencies]
dev = [
'''
for dep in dev_deps:
toml_content += f' "{dep}",\n'
toml_content += "]\n"
if cli_entry_points:
toml_content += "\n[project.scripts]\n"
for cmd, entry in cli_entry_points.items():
toml_content += f'{cmd} = "{entry}"\n'
toml_content += '''
[tool.setuptools.packages.find]
where = ["src"]
[tool.black]
line-length = 88
target-version = ["py38"]
[tool.ruff]
line-length = 88
select = ["E", "F", "I"]
[tool.pytest.ini_options]
testpaths = ["tests"]
'''
return toml_content
# Generate example
example_toml = generate_pyproject_toml(
name="my-awesome-package",
version="0.1.0",
description="An awesome Python package",
author="John Doe",
email="john@example.com",
dependencies=["requests>=2.25.0", "click>=8.0.0"],
cli_entry_points={"my-cli": "my_awesome_package.cli:main"}
)
print("Generated pyproject.toml:")
print(example_toml)
# ============================================================
# 4. VERSION MANAGEMENT
# ============================================================
print("\n" + "=" * 60)
print("4. VERSION MANAGEMENT")
print("=" * 60)
class Version:
"""Semantic version handling."""
def __init__(self, version_string: str):
"""Parse version string like '1.2.3' or '1.2.3-beta.1'."""
parts = version_string.split('-')
main_version = parts[0]
self.prerelease = parts[1] if len(parts) > 1 else None
version_parts = main_version.split('.')
self.major = int(version_parts[0])
self.minor = int(version_parts[1]) if len(version_parts) > 1 else 0
self.patch = int(version_parts[2]) if len(version_parts) > 2 else 0
def bump_major(self) -> 'Version':
"""Bump major version (breaking changes)."""
return Version(f"{self.major + 1}.0.0")
def bump_minor(self) -> 'Version':
"""Bump minor version (new features)."""
return Version(f"{self.major}.{self.minor + 1}.0")
def bump_patch(self) -> 'Version':
"""Bump patch version (bug fixes)."""
return Version(f"{self.major}.{self.minor}.{self.patch + 1}")
def __str__(self) -> str:
base = f"{self.major}.{self.minor}.{self.patch}"
if self.prerelease:
return f"{base}-{self.prerelease}"
return base
def __repr__(self) -> str:
return f"Version('{self}')"
# Demonstrate version management
print("Semantic Versioning Examples:")
v = Version("1.2.3")
print(f"Current: {v}")
print(f"Bump patch (bug fix): {v.bump_patch()}")
print(f"Bump minor (new feature): {v.bump_minor()}")
print(f"Bump major (breaking): {v.bump_major()}")
v_pre = Version("2.0.0-alpha.1")
print(f"\nPre-release version: {v_pre}")
# ============================================================
# 5. DEPENDENCY MANAGEMENT
# ============================================================
print("\n" + "=" * 60)
print("5. DEPENDENCY MANAGEMENT")
print("=" * 60)
def parse_requirement(req_string: str) -> Dict[str, Any]:
"""Parse a requirement string into components."""
import re
# Handle extras like package[extra1,extra2]
extras_match = re.match(r'^([a-zA-Z0-9_-]+)\[(.*?)\](.*)$', req_string)
if extras_match:
name = extras_match.group(1)
extras = [e.strip() for e in extras_match.group(2).split(',')]
version_spec = extras_match.group(3)
else:
match = re.match(r'^([a-zA-Z0-9_-]+)(.*)$', req_string)
name = match.group(1) if match else req_string
version_spec = match.group(2) if match else ""
extras = []
return {
"name": name,
"extras": extras,
"version_spec": version_spec.strip(),
}
# Parse example requirements
requirements = [
"requests>=2.25.0",
"click>=8.0.0,<9.0.0",
"numpy==1.24.0",
"pandas[excel]>=1.5.0",
"django",
]
print("Parsing requirements:")
for req in requirements:
parsed = parse_requirement(req)
print(f" {req}")
print(f" -> name: {parsed['name']}, version: {parsed['version_spec'] or 'any'}")
if parsed['extras']:
print(f" -> extras: {parsed['extras']}")
# ============================================================
# 6. CLI WITH CLICK
# ============================================================
print("\n" + "=" * 60)
print("6. CLI WITH CLICK")
print("=" * 60)
# Simulated click-like CLI structure
cli_example = '''
import click
@click.group()
@click.version_option(version="0.1.0")
def cli():
"""My Package CLI - A helpful command-line tool."""
pass
@cli.command()
@click.argument("name")
@click.option("--count", "-c", default=1, help="Number of greetings")
@click.option("--formal", is_flag=True, help="Use formal greeting")
def greet(name, count, formal):
"""Greet someone by name."""
greeting = "Good day" if formal else "Hello"
for _ in range(count):
click.echo(f"{greeting}, {name}!")
@cli.command()
@click.option("--format", "-f", type=click.Choice(["json", "text"]), default="text")
def info(format):
"""Show package information."""
info_data = {"name": "my-package", "version": "0.1.0"}
if format == "json":
click.echo(json.dumps(info_data))
else:
click.echo(f"Package: {info_data['name']}")
click.echo(f"Version: {info_data['version']}")
@cli.command()
@click.argument("input_file", type=click.Path(exists=True))
@click.argument("output_file", type=click.Path())
@click.option("--verbose", "-v", is_flag=True)
def process(input_file, output_file, verbose):
"""Process a file."""
if verbose:
click.echo(f"Processing {input_file}...")
# Processing logic here
if verbose:
click.echo(f"Output written to {output_file}")
if __name__ == "__main__":
cli()
'''
print("Example Click CLI structure:")
print(cli_example)
# ============================================================
# 7. SETUP.PY GENERATOR (for legacy systems)
# ============================================================
print("\n" + "=" * 60)
print("7. SETUP.PY GENERATOR (Legacy)")
print("=" * 60)
def generate_setup_py(
name: str,
version: str = "0.1.0",
description: str = "",
author: str = "",
email: str = "",
url: str = "",
dependencies: Optional[list] = None,
) -> str:
"""Generate a setup.py file content."""
deps = dependencies or []
deps_str = ",\n ".join(f'"{dep}"' for dep in deps)
return f'''"""Setup script for {name}."""
from setuptools import setup, find_packages
with open("README.md", "r", encoding="utf-8") as f:
long_description = f.read()
setup(
name="{name}",
version="{version}",
author="{author}",
author_email="{email}",
description="{description}",
long_description=long_description,
long_description_content_type="text/markdown",
url="{url}",
packages=find_packages(where="src"),
package_dir={{"": "src"}},
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
],
python_requires=">=3.8",
install_requires=[
{deps_str}
],
extras_require={{
"dev": [
"pytest>=7.0",
"black",
"mypy",
],
}},
)
'''
setup_py = generate_setup_py(
name="my-package",
version="0.1.0",
description="A sample package",
author="John Doe",
email="john@example.com",
url="https://github.com/johndoe/my-package",
dependencies=["requests>=2.25.0", "click>=8.0.0"]
)
print("Generated setup.py:")
print(setup_py[:1000] + "...")
# ============================================================
# 8. PACKAGE DATA ACCESS
# ============================================================
print("\n" + "=" * 60)
print("8. PACKAGE DATA ACCESS")
print("=" * 60)
# Modern way to access package data (Python 3.9+)
package_data_example = '''
# Reading package data files
# Python 3.9+ using importlib.resources
import importlib.resources as pkg_resources
# Read text file
with pkg_resources.files('my_package').joinpath('data/config.json').open() as f:
config = json.load(f)
# Read as string
text = pkg_resources.files('my_package').joinpath('data/template.txt').read_text()
# Read binary
data = pkg_resources.files('my_package').joinpath('data/image.png').read_bytes()
# For Python 3.7-3.8 (legacy)
from importlib.resources import read_text, read_binary
text = read_text('my_package.data', 'config.json')
binary = read_binary('my_package.data', 'image.png')
# pyproject.toml configuration:
# [tool.setuptools.package-data]
# my_package = ["data/*.json", "data/*.txt", "templates/*.html"]
'''
print("Package data access patterns:")
print(package_data_example)
# ============================================================
# 9. PROJECT TEMPLATE GENERATOR
# ============================================================
print("\n" + "=" * 60)
print("9. PROJECT TEMPLATE GENERATOR")
print("=" * 60)
def create_project_structure(
project_name: str,
base_path: str = ".",
author: str = "Author",
email: str = "author@example.com",
) -> Dict[str, str]:
"""Generate file contents for a new Python project."""
package_name = project_name.replace("-", "_").lower()
files = {}
# pyproject.toml
files["pyproject.toml"] = generate_pyproject_toml(
name=project_name,
author=author,
email=email,
)
# src/package/__init__.py
files[f"src/{package_name}/__init__.py"] = f'''"""
{project_name} - A Python package.
"""
__version__ = "0.1.0"
__author__ = "{author}"
'''
# src/package/core.py
files[f"src/{package_name}/core.py"] = f'''"""Core functionality for {project_name}."""
def main():
"""Main entry point."""
print("Hello from {project_name}!")
if __name__ == "__main__":
main()
'''
# tests/__init__.py
files["tests/__init__.py"] = ""
# tests/test_core.py
files["tests/test_core.py"] = f'''"""Tests for core module."""
import pytest
from {package_name}.core import main
def test_main(capsys):
"""Test main function."""
main()
captured = capsys.readouterr()
assert "{project_name}" in captured.out
'''
# README.md
files["README.md"] = f'''# {project_name}
A Python package.
## Installation
```bash
pip install {project_name}
```
## Usage
```python
from {package_name} import main
main()
```
## Development
```bash
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest
```
## License
MIT License
'''
# .gitignore
files[".gitignore"] = '''# Python
__pycache__/
*.py[cod]
*.so
.Python
build/
dist/
*.egg-info/
*.egg
# Virtual environments
.venv/
venv/
ENV/
# IDE
.idea/
.vscode/
*.swp
# Testing
.pytest_cache/
.coverage
htmlcov/
# Type checking
.mypy_cache/
# Distribution
*.whl
'''
return files
# Generate example project
print("Example project structure generator:")
project_files = create_project_structure(
"my-awesome-project",
author="Jane Doe",
email="jane@example.com"
)
print(f"\nFiles that would be created:")
for filepath in sorted(project_files.keys()):
print(f" {filepath}")
# ============================================================
# 10. BUILDING AND PUBLISHING COMMANDS
# ============================================================
print("\n" + "=" * 60)
print("10. BUILDING AND PUBLISHING")
print("=" * 60)
commands = """
Building and Publishing Python Packages:
# 1. Set up virtual environment
python -m venv .venv
source .venv/bin/activate # Linux/macOS
# or: .venv\\Scripts\\activate # Windows
# 2. Install build tools
pip install build twine
# 3. Install package in development mode
pip install -e ".[dev]"
# 4. Run tests
pytest
# 5. Format and lint code
black src tests
ruff check src tests
mypy src
# 6. Build package
python -m build
# Creates:
# dist/my_package-0.1.0.tar.gz (source)
# dist/my_package-0.1.0-py3-none-any.whl (wheel)
# 7. Check package before upload
twine check dist/*
# 8. Upload to TestPyPI (for testing)
twine upload --repository testpypi dist/*
# 9. Test installation from TestPyPI
pip install --index-url https://test.pypi.org/simple/ my-package
# 10. Upload to PyPI (production)
twine upload dist/*
# Alternative: Using Poetry
poetry build
poetry publish
"""
print(commands)
# ============================================================
# SUMMARY
# ============================================================
print("\n" + "=" * 60)
print("PACKAGING SUMMARY")
print("=" * 60)
print("""
Key Concepts Covered:
ā Project structure (src layout)
ā pyproject.toml configuration
ā __init__.py patterns
ā Version management (semantic versioning)
ā Dependency management
ā CLI creation with Click
ā Legacy setup.py
ā Package data access
ā Project template generation
ā Building and publishing workflow
Essential Files:
- pyproject.toml: Modern configuration (required)
- README.md: Project description
- LICENSE: License file
- __init__.py: Package marker
- .gitignore: Git ignore patterns
Key Commands:
- pip install -e ".[dev]": Development install
- python -m build: Build package
- twine upload dist/*: Publish to PyPI
- pytest: Run tests
""")