python

examples

examples.pyšŸ
"""
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
""")
Examples - Python Tutorial | DeepML