--- name: scientific-python-packaging description: Create distributable scientific Python packages following Scientific Python community best practices with pyproject.toml, src layout, and Hatchling build backend --- # Scientific Python Packaging A comprehensive guide to creating, structuring, and distributing Python packages for scientific computing, following the [Scientific Python Community guidelines](https://learn.scientific-python.org/development/guides/packaging-simple/). This skill focuses on modern packaging standards using `pyproject.toml`, PEP 621 metadata, and the Hatchling build backend. ## Quick Decision Tree **Package Structure Selection:** ``` START ├─ Pure Python scientific package (most common) → Pattern 1 (src/ layout) ├─ Need data files with package → Pattern 2 (data/ subdirectory) ├─ CLI tool → Pattern 5 (add [project.scripts]) └─ Complex multi-feature package → Pattern 3 (full-featured) ``` **Build Backend Choice:** ``` START → Use Hatchling (recommended for scientific Python) ├─ Need VCS versioning? → Add hatch-vcs plugin ├─ Simple manual versioning? → version = "X.Y.Z" in pyproject.toml └─ Dynamic from __init__.py? → [tool.hatch.version] path ``` **Dependency Management:** ``` START ├─ Runtime dependencies → [project] dependencies ├─ Optional features → [project.optional-dependencies] ├─ Development tools → [dependency-groups] (PEP 735) └─ Version constraints → Use >= for minimum, avoid upper caps ``` **Publishing Workflow:** ``` 1. Build: python -m build 2. Check: twine check dist/* 3. Test: twine upload --repository testpypi dist/* 4. Verify: pip install --index-url https://test.pypi.org/simple/ pkg 5. Publish: twine upload dist/* ``` **Common Task Quick Reference:** ```bash # Setup new package mkdir -p my-pkg/src/my_pkg && cd my-pkg # Create pyproject.toml with [build-system] and [project] sections # Development install pip install -e . --group dev # Build distributions python -m build # Test installation pip install dist/*.whl # Publish twine upload dist/* ``` ## When to Use This Skill - Creating scientific Python libraries for distribution - Building research software packages with proper structure - Publishing scientific packages to PyPI - Setting up reproducible scientific Python projects - Creating installable packages with scientific dependencies - Implementing command-line tools for scientific workflows - Following community standards for scientific Python development - Preparing packages for peer review and publication ## Core Concepts ### 1. Modern Build Systems Python packages now use standardized build systems instead of classic `setup.py`: - **PEP 621**: Standardized project metadata in `pyproject.toml` - **PEP 517/518**: Build system independence - **Build backend**: Hatchling - **No classic files**: No `setup.py`, `setup.cfg`, or `MANIFEST.in` ### 2. Build Backend: Hatchling - **Hatchling**: Excellent balance of speed, configurability, and extendability - Modern, standards-compliant build backend - Automatic package discovery in `src/` layout - VCS-aware file inclusion for SDists - Extensible through plugins ### 3. Package Structure - **src/ layout**: Required for proper isolation (prevents importing uninstalled code) - **Automatic discovery**: Hatchling auto-detects packages in `src/` - **Standard structure**: Consistent organization for testing and documentation ### 4. Scientific Python Standards - **Dependency management**: Careful version constraints - **Python version support**: Minimum version without upper caps - **Development dependencies**: Use dependency-groups (PEP 735) - **Documentation**: Include README, LICENSE, and docs folder - **Testing**: Dedicated tests folder ## Quick Start ### Minimal Scientific Package Structure ``` my-sci-package/ ├── pyproject.toml ├── README.md ├── LICENSE ├── src/ │ └── my_sci_package/ │ ├── __init__.py │ ├── analysis.py │ └── utils.py ├── tests/ │ ├── test_analysis.py │ └── test_utils.py └── docs/ └── index.md ``` ### Minimal pyproject.toml with Hatchling ```toml [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "my-sci-package" version = "0.1.0" description = "A scientific Python package for data analysis" readme = "README.md" license = "BSD-3-Clause" license-files = ["LICENSE"] requires-python = ">=3.9" authors = [ {name = "Your Name", email = "you@example.com"}, ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Science/Research", "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering", ] dependencies = [ "numpy>=1.20", "scipy>=1.7", ] [project.urls] Homepage = "https://github.com/username/my-sci-package" Documentation = "https://my-sci-package.readthedocs.io" "Bug Tracker" = "https://github.com/username/my-sci-package/issues" Discussions = "https://github.com/username/my-sci-package/discussions" Changelog = "https://my-sci-package.readthedocs.io/en/latest/changelog.html" [dependency-groups] test = [ "pytest>=7.0", "pytest-cov>=4.0", ] dev = [ {include-group = "test"}, "ruff>=0.1", "mypy>=1.0", ] ``` ## Package Structure Patterns ### Pattern 1: Pure Python Scientific Package (Recommended) ``` my-sci-package/ ├── pyproject.toml ├── README.md ├── LICENSE ├── .gitignore ├── src/ │ └── my_sci_package/ │ ├── __init__.py │ ├── analysis.py │ ├── preprocessing.py │ ├── visualization.py │ ├── utils.py │ └── py.typed # For type hints ├── tests/ │ ├── __init__.py │ ├── test_analysis.py │ ├── test_preprocessing.py │ └── test_visualization.py └── docs/ ├── conf.py ├── index.md └── api.md ``` **Key advantages:** - Prevents accidental imports from source - Forces proper installation for testing - Professional structure for scientific libraries - Clear separation of concerns ### Pattern 2: Scientific Package with Data Files ``` my-sci-package/ ├── pyproject.toml ├── README.md ├── LICENSE ├── src/ │ └── my_sci_package/ │ ├── __init__.py │ ├── analysis.py │ └── data/ │ ├── reference.csv │ ├── constants.json │ └── coefficients.dat ├── tests/ │ └── test_analysis.py └── docs/ └── index.md ``` **Include data files in pyproject.toml (if needed):** ```toml [tool.hatch.build.targets.wheel] packages = ["src/my_sci_package"] # Only if you need to explicitly include data [tool.hatch.build.targets.wheel.force-include] "src/my_sci_package/data" = "my_sci_package/data" ``` **Access data files in code:** ```python from importlib.resources import files import json def load_constants(): """Load constants from package data.""" data_file = files("my_sci_package").joinpath("data/constants.json") with data_file.open() as f: return json.load(f) ``` ## Complete pyproject.toml Examples ### Pattern 3: Full-Featured Scientific Package ```toml [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "advanced-sci-package" version = "1.0.0" description = "Advanced scientific computing package" readme = "README.md" license = "BSD-3-Clause" license-files = ["LICENSE"] requires-python = ">=3.9" authors = [ {name = "Research Team", email = "team@university.edu"}, ] maintainers = [ {name = "Lead Maintainer", email = "maintainer@university.edu"}, ] keywords = ["scientific-computing", "data-analysis", "research"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Science/Research", "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Physics", "Topic :: Scientific/Engineering :: Mathematics", ] dependencies = [ "numpy>=1.20", "scipy>=1.7", "pandas>=1.3", "matplotlib>=3.4", ] [project.optional-dependencies] ml = [ "scikit-learn>=1.0", "tensorflow>=2.8", ] viz = [ "plotly>=5.0", "seaborn>=0.11", ] all = [ "advanced-sci-package[ml,viz]", ] [project.urls] Homepage = "https://github.com/org/advanced-sci-package" Documentation = "https://advanced-sci-package.readthedocs.io" Repository = "https://github.com/org/advanced-sci-package" "Bug Tracker" = "https://github.com/org/advanced-sci-package/issues" Discussions = "https://github.com/org/advanced-sci-package/discussions" Changelog = "https://advanced-sci-package.readthedocs.io/en/latest/changelog.html" [project.scripts] sci-analyze = "advanced_sci_package.cli:main" [dependency-groups] test = [ "pytest>=7.0", "pytest-cov>=4.0", "pytest-xdist>=3.0", ] docs = [ "sphinx>=5.0", "sphinx-rtd-theme>=1.0", "numpydoc>=1.5", ] dev = [ {include-group = "test"}, {include-group = "docs"}, "ruff>=0.1", "mypy>=1.0", "pre-commit>=3.0", ] # Hatchling configuration [tool.hatch.build.targets.wheel] packages = ["src/advanced_sci_package"] # Ruff configuration (linting and formatting) [tool.ruff] line-length = 88 target-version = "py39" [tool.ruff.lint] select = ["E", "F", "I", "N", "W", "UP", "NPY", "RUF"] ignore = ["E501"] # Line too long (handled by formatter) # Pytest configuration [tool.pytest.ini_options] testpaths = ["tests"] python_files = ["test_*.py"] addopts = "-v --cov=advanced_sci_package --cov-report=term-missing" # MyPy configuration [tool.mypy] python_version = "3.9" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true # Coverage configuration [tool.coverage.run] source = ["src"] omit = ["*/tests/*"] [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", ] ``` ## Project Metadata ### License (Modern SPDX Format) Use SPDX identifiers (supported by hatchling>=1.26): ```toml [project] license = "BSD-3-Clause" license-files = ["LICENSE"] ``` Common scientific licenses: - `MIT` - Permissive, simple - `BSD-3-Clause` - Permissive, commonly used in science - `Apache-2.0` - Permissive, explicit patent grant - `GPL-3.0-or-later` - Copyleft **Do not include License classifiers if using the `license` field.** ### Python Version Requirements **Best practice**: Specify minimum version only, no upper cap: ```toml requires-python = ">=3.9" ``` This allows pip to back-solve for old package versions when needed. ### Dependencies **Use appropriate version constraints:** ```toml dependencies = [ "numpy>=1.20", # Minimum version "scipy>=1.7,<2.0", # Compatible range (use sparingly) "pandas>=1.3", # Open-ended (preferred) "matplotlib>=3.4", # Minimum version ] ``` **Avoid pinning exact versions unless absolutely necessary.** ### Classifiers Important classifiers for scientific packages: ```toml classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Science/Research", "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Physics", "Typing :: Typed", ] ``` [Browse all classifiers](https://pypi.org/classifiers/) ## Optional Dependencies (Extras) Use extras for optional scientific features: ```toml [project.optional-dependencies] plotting = [ "matplotlib>=3.4", "seaborn>=0.11", ] ml = [ "scikit-learn>=1.0", "xgboost>=1.5", ] parallel = [ "dask[array]>=2021.0", "joblib>=1.0", ] all = [ "my-sci-package[plotting,ml,parallel]", ] ``` **Install with extras:** ```bash pip install my-sci-package[plotting] pip install my-sci-package[plotting,ml] pip install my-sci-package[all] ``` ## Development Dependencies (Dependency Groups) Use `dependency-groups` (PEP 735) instead of extras for development tools: ```toml [dependency-groups] test = [ "pytest>=7.0", "pytest-cov>=4.0", "hypothesis>=6.0", ] docs = [ "sphinx>=5.0", "numpydoc>=1.5", "sphinx-gallery>=0.11", ] dev = [ {include-group = "test"}, {include-group = "docs"}, "ruff>=0.1", "mypy>=1.0", ] ``` **Install dependency groups:** ```bash # Using uv (recommended) uv pip install --group dev # Using pip 25.1+ pip install --group dev # Traditional approach with editable install pip install -e ".[dev]" # if using extras ``` **Advantages over extras:** - Formally standardized - More composable - Not available on PyPI (development-only) - Installed by default with `uv` ## Command-Line Interface ### Pattern 5: Scientific CLI Tool ```python # src/my_sci_package/cli.py import click import numpy as np from pathlib import Path @click.group() @click.version_option() def cli(): """Scientific analysis CLI tool.""" pass @cli.command() @click.argument("input_file", type=click.Path(exists=True)) @click.option("--output", "-o", type=click.Path(), help="Output file path") @click.option("--threshold", "-t", type=float, default=0.5, help="Analysis threshold") def analyze(input_file: str, output: str, threshold: float): """Analyze scientific data from input file.""" # Load and analyze data data = np.loadtxt(input_file) result = np.mean(data[data > threshold]) click.echo(f"Analysis complete: mean = {result:.4f}") if output: np.savetxt(output, [result]) click.echo(f"Results saved to {output}") @cli.command() @click.argument("input_file", type=click.Path(exists=True)) @click.option("--format", type=click.Choice(["png", "pdf", "svg"]), default="png") def plot(input_file: str, format: str): """Generate plots from data.""" import matplotlib.pyplot as plt data = np.loadtxt(input_file) plt.plot(data) output_file = f"plot.{format}" plt.savefig(output_file) click.echo(f"Plot saved to {output_file}") def main(): """Entry point for CLI.""" cli() if __name__ == "__main__": main() ``` **Register in pyproject.toml:** ```toml [project.scripts] sci-analyze = "my_sci_package.cli:main" ``` **Usage:** ```bash pip install -e . sci-analyze analyze data.txt --threshold 0.7 sci-analyze plot data.txt --format pdf ``` ## Versioning ### Pattern 6: Manual Versioning ```toml [project] version = "1.2.3" ``` ```python # src/my_sci_package/__init__.py __version__ = "1.2.3" ``` ### Pattern 7: Dynamic Versioning with Hatchling ```toml [project] dynamic = ["version"] [tool.hatch.version] path = "src/my_sci_package/__init__.py" ``` ```python # src/my_sci_package/__init__.py __version__ = "1.2.3" ``` ### Pattern 8: Git-Based Versioning with Hatchling ```toml [build-system] requires = ["hatchling", "hatch-vcs"] build-backend = "hatchling.build" [project] dynamic = ["version"] [tool.hatch.version] source = "vcs" [tool.hatch.build.hooks.vcs] version-file = "src/my_sci_package/_version.py" ``` **Semantic versioning for scientific software:** - `MAJOR`: Breaking API changes - `MINOR`: New features, backward compatible - `PATCH`: Bug fixes ## Building and Publishing ### Pattern 9: Build Package Locally ```bash # Install build tools pip install build # Build distribution python -m build # Creates: # dist/my-sci-package-1.0.0.tar.gz (source distribution) # dist/my_sci_package-1.0.0-py3-none-any.whl (wheel) # Verify the distribution pip install twine twine check dist/* # Inspect contents tar -tvf dist/*.tar.gz unzip -l dist/*.whl ``` **Critical**: Test the SDist contents to ensure all necessary files are included. ### Pattern 10: Publishing to PyPI ```bash # Install publishing tools pip install twine # Test on TestPyPI first (always!) twine upload --repository testpypi dist/* # Install and test from TestPyPI pip install --index-url https://test.pypi.org/simple/ my-sci-package # If everything works, publish to PyPI twine upload dist/* ``` **Using API tokens (recommended):** Create `~/.pypirc`: ```ini [distutils] index-servers = pypi testpypi [pypi] username = __token__ password = pypi-...your-token... [testpypi] username = __token__ password = pypi-...your-test-token... ``` ### Pattern 11: Automated Publishing with GitHub Actions ```yaml # .github/workflows/publish.yml name: Publish to PyPI on: release: types: [published] jobs: publish: runs-on: ubuntu-latest environment: release permissions: id-token: write # For trusted publishing steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install build tools run: pip install build - name: Build package run: python -m build - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 ``` **Use PyPI trusted publishing instead of API tokens for GitHub Actions.** ## Testing Installation ### Pattern 12: Editable Install for Development ```bash # Install in development mode pip install -e . # With dependency groups pip install -e . --group dev # Using uv (recommended for scientific workflows) uv pip install -e . --group dev # Now changes to source code are immediately reflected ``` ### Pattern 13: Testing in Isolated Environment ```bash # Create and activate virtual environment python -m venv test-env source test-env/bin/activate # Linux/Mac # Install from wheel pip install dist/my_sci_package-1.0.0-py3-none-any.whl # Test import and version python -c "import my_sci_package; print(my_sci_package.__version__)" # Test CLI sci-analyze --help # Cleanup deactivate rm -rf test-env ``` ## Documentation ### Pattern 14: Scientific Package README.md ```markdown # My Scientific Package [![PyPI version](https://badge.fury.io/py/my-sci-package.svg)](https://pypi.org/project/my-sci-package/) [![Python versions](https://img.shields.io/pypi/pyversions/my-sci-package.svg)](https://pypi.org/project/my-sci-package/) [![Tests](https://github.com/username/my-sci-package/workflows/Tests/badge.svg)](https://github.com/username/my-sci-package/actions) [![Documentation](https://readthedocs.org/projects/my-sci-package/badge/?version=latest)](https://my-sci-package.readthedocs.io/) A Python package for [brief description of scientific purpose]. ## Features - Feature 1: Description - Feature 2: Description - Feature 3: Description ## Installation ```bash pip install my-sci-package ``` For plotting capabilities: ```bash pip install my-sci-package[plotting] ``` ## Quick Start ```python import my_sci_package as msp import numpy as np # Example usage data = np.random.randn(100) result = msp.analyze(data, threshold=0.5) print(f"Result: {result}") ``` ## Documentation Full documentation: https://my-sci-package.readthedocs.io ## Citation If you use this package in your research, please cite: ```bibtex @software{my_sci_package, author = {Your Name}, title = {My Scientific Package}, year = {2025}, url = {https://github.com/username/my-sci-package} } ``` ## Development ```bash git clone https://github.com/username/my-sci-package.git cd my-sci-package pip install -e . --group dev pytest ``` ## License BSD-3-Clause License - see LICENSE file for details. ``` ## File Templates ### .gitignore for Scientific Python Packages ```gitignore # Build artifacts build/ dist/ *.egg-info/ *.egg .eggs/ src/**/_version.py # Python __pycache__/ *.py[cod] *$py.class *.so # Virtual environments venv/ env/ ENV/ # IDE .vscode/ .idea/ *.swp # Testing .pytest_cache/ .coverage htmlcov/ .hypothesis/ # Documentation docs/_build/ docs/_generated/ # Scientific data (adjust as needed) *.hdf5 *.nc *.mat data/processed/ # Jupyter .ipynb_checkpoints/ *.ipynb # Distribution *.whl *.tar.gz ``` ### Pattern 15: Sphinx Documentation Setup ```python # docs/conf.py import sys from pathlib import Path # Add package to path sys.path.insert(0, str(Path("..").resolve() / "src")) # Project information project = "My Scientific Package" copyright = "2025, Your Name" author = "Your Name" # Extensions extensions = [ "sphinx.ext.autodoc", "sphinx.ext.napoleon", # NumPy/Google style docstrings "sphinx.ext.viewcode", "sphinx.ext.mathjax", # Math rendering "sphinx.ext.intersphinx", "numpydoc", # NumPy documentation style ] # Intersphinx mapping intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "numpy": ("https://numpy.org/doc/stable/", None), "scipy": ("https://docs.scipy.org/doc/scipy/", None), "pandas": ("https://pandas.pydata.org/docs/", None), } # Theme html_theme = "sphinx_rtd_theme" ``` ## Checklist for Publishing Scientific Packages - [ ] Code is tested with pytest (>90% coverage recommended) - [ ] Documentation is complete (README, docstrings, Sphinx docs) - [ ] Version number follows semantic versioning - [ ] CHANGELOG.md or NEWS.md updated - [ ] LICENSE file included with appropriate license - [ ] pyproject.toml has complete metadata - [ ] Package uses src/ layout - [ ] Package builds without errors (`python -m build`) - [ ] SDist contents verified (`tar -tvf dist/*.tar.gz`) - [ ] Installation tested in clean environment - [ ] CLI tools work if applicable - [ ] All classifiers are appropriate - [ ] Python version constraint is correct (no upper bound) - [ ] Dependencies have appropriate version constraints - [ ] Repository is linked in project.urls - [ ] Tested on TestPyPI first - [ ] GitHub release created (if using) - [ ] Documentation published (ReadTheDocs, GitHub Pages) - [ ] Citation information included (CITATION.cff or README) ## Best Practices for Scientific Python Packages 1. **Use src/ layout** - Prevents importing uninstalled code, ensures proper testing 2. **Use pyproject.toml** - Modern standard, tool-independent configuration 3. **Use Hatchling** - Modern, fast, and configurable build backend 4. **No classic files** - Avoid setup.py, setup.cfg, MANIFEST.in 5. **Version constraints** - Minimum versions for dependencies, no upper cap for Python 6. **Test SDist contents** - Always verify what files are included/excluded 7. **Use TestPyPI** - Always test publishing before going to production 8. **Document thoroughly** - README, docstrings, Sphinx documentation 9. **Include LICENSE** - Use SPDX identifiers, choose appropriate scientific license 10. **Use dependency-groups** - For development dependencies (PEP 735) 11. **Semantic versioning** - Clear versioning strategy 12. **Automate CI/CD** - GitHub Actions for testing and publishing 13. **Type hints** - Include py.typed marker for typed packages 14. **Citation information** - Make it easy for users to cite your work 15. **Community standards** - Follow Scientific Python guidelines ## Scientific Python Specific Considerations ### NumPy-style Docstrings ```python def analyze_data(data, threshold=0.5, method="mean"): """ Analyze scientific data above a threshold. Parameters ---------- data : array_like Input data array to analyze. threshold : float, optional Minimum value for inclusion in analysis, by default 0.5. method : {"mean", "median", "std"}, optional Statistical method to apply, by default "mean". Returns ------- result : float Computed statistical result. Raises ------ ValueError If method is not recognized. Examples -------- >>> import numpy as np >>> data = np.array([0.1, 0.6, 0.8, 0.3, 0.9]) >>> analyze_data(data, threshold=0.5) 0.7666666666666667 Notes ----- This function uses NumPy for efficient computation. References ---------- .. [1] Harris et al., "Array programming with NumPy", Nature 585, 2020. """ pass ``` ### Scientific Dependencies Common scientific Python dependencies: ```toml dependencies = [ "numpy>=1.20", # Arrays and numerical computing "scipy>=1.7", # Scientific computing algorithms "pandas>=1.3", # Data structures and analysis "matplotlib>=3.4", # Plotting "xarray>=0.19", # Labeled multi-dimensional arrays "scikit-learn>=1.0", # Machine learning "astropy>=5.0", # Astronomy (if applicable) ] ``` ### Reproducibility Include information for reproducibility: ```toml [project.urls] "Source Code" = "https://github.com/org/package" "Documentation" = "https://package.readthedocs.io" "Bug Reports" = "https://github.com/org/package/issues" "Changelog" = "https://github.com/org/package/blob/main/CHANGELOG.md" "Citation" = "https://doi.org/10.xxxx/xxxxx" # DOI if available ``` ## Resources - **Scientific Python Development Guide**: https://learn.scientific-python.org/development/ - **Simple Packaging Guide**: https://learn.scientific-python.org/development/guides/packaging-simple/ - **Python Packaging Guide**: https://packaging.python.org/ - **PyPI**: https://pypi.org/ - **TestPyPI**: https://test.pypi.org/ - **Hatchling documentation**: https://hatch.pypa.io/latest/ - **build**: https://pypa-build.readthedocs.io/ - **twine**: https://twine.readthedocs.io/ - **Scientific Python Cookie**: https://github.com/scientific-python/cookie - **NumPy documentation style**: https://numpydoc.readthedocs.io/ ## Common Issues and Solutions ### Issue: Import errors in tests **Problem**: Tests import the source code instead of installed package. **Solution**: Use src/ layout and install package with `pip install -e .` ### Issue: Missing files in distribution **Problem**: Data files or documentation not included in SDist/wheel. **Solution**: - For Hatchling: VCS ignore file controls SDist contents - Check with: `tar -tvf dist/*.tar.gz` - Explicitly configure if needed in `[tool.hatch.build]` ### Issue: Dependency conflicts **Problem**: Users cannot install due to incompatible dependency versions. **Solution**: Use minimal version constraints, avoid upper bounds on dependencies. ### Issue: Python version incompatibility **Problem**: Package doesn't work on newer Python versions. **Solution**: Don't cap `requires-python`, test on multiple Python versions with CI.