├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ └── code-issue.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── ch07-packaging.yml │ ├── ch08-packaging.yml │ └── codeql-analysis.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── ch03 └── first-python-package │ ├── .gitignore │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.md │ ├── pyproject.toml │ ├── setup.cfg │ └── src │ └── imppkg │ ├── __init__.py │ ├── data.json │ └── hello.py ├── ch04 └── first-python-package │ ├── .gitignore │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.md │ ├── pyproject.toml │ ├── setup.cfg │ ├── setup.py │ └── src │ └── imppkg │ ├── __init__.py │ ├── data.json │ ├── harmonic_mean.pyx │ ├── harmony.py │ └── hello.py ├── ch05 └── first-python-package │ ├── .gitignore │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.md │ ├── pyproject.toml │ ├── setup.cfg │ ├── setup.py │ ├── src │ └── imppkg │ │ ├── __init__.py │ │ ├── data.json │ │ ├── harmonic_mean.pyx │ │ ├── harmony.py │ │ └── py.typed │ └── test │ ├── __init__.py │ └── test_harmonic_mean.py ├── ch06 └── first-python-package │ ├── .gitignore │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.md │ ├── pyproject.toml │ ├── setup.cfg │ ├── setup.py │ ├── src │ └── imppkg │ │ ├── __init__.py │ │ ├── data.json │ │ ├── harmonic_mean.pyx │ │ ├── harmony.py │ │ └── py.typed │ └── test │ ├── __init__.py │ └── test_harmonic_mean.py ├── ch07 └── first-python-package │ ├── .gitignore │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.md │ ├── pyproject.toml │ ├── setup.cfg │ ├── setup.py │ ├── src │ └── imppkg │ │ ├── __init__.py │ │ ├── data.json │ │ ├── harmonic_mean.pyx │ │ ├── harmony.py │ │ └── py.typed │ └── test │ ├── __init__.py │ └── test_harmonic_mean.py ├── ch08 └── first-python-package │ ├── .gitignore │ ├── .readthedocs.yaml │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.md │ ├── docs │ ├── conf.py │ └── index.rst │ ├── pyproject.toml │ ├── setup.cfg │ ├── setup.py │ ├── src │ └── imppkg │ │ ├── __init__.py │ │ ├── data.json │ │ ├── harmonic_mean.pyx │ │ ├── harmony.py │ │ └── py.typed │ └── test │ ├── __init__.py │ └── test_harmonic_mean.py ├── ch09 └── first-python-package │ ├── .gitignore │ ├── .readthedocs.yaml │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.md │ ├── docs │ ├── conf.py │ └── index.rst │ ├── pyproject.toml │ ├── setup.cfg │ ├── setup.py │ ├── src │ └── imppkg │ │ ├── __init__.py │ │ ├── data.json │ │ ├── harmonic_mean.pyx │ │ ├── harmony.py │ │ └── py.typed │ └── test │ ├── __init__.py │ └── test_harmonic_mean.py ├── ch10 └── python-package-template │ ├── cookiecutter.json │ └── {{cookiecutter.package_distribution_name}} │ ├── .gitignore │ ├── .readthedocs.yaml │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.md │ ├── docs │ ├── conf.py │ └── index.rst │ ├── pyproject.toml │ ├── setup.cfg │ ├── setup.py │ ├── src │ └── {{cookiecutter.package_import_name}} │ │ ├── __init__.py │ │ └── py.typed │ └── test │ ├── __init__.py │ └── test_{{cookiecutter.package_import_name}}.py └── cover.jpg /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Publishing Python Packages 2 | 3 | Thank you for your interest in improving _Publishing Python Packages_! 4 | Please read these guidelines to see how best to contribute to the book's success. 5 | 6 | ## Source code issues 7 | 8 | If you've discovered an issue with the code in this repository, please [open an issue](https://github.com/daneah/publishing-python-packages/issues/new/choose). 9 | Or, if you have time, please consider forking this repo and opening a pull request with the fix! 10 | 11 | ## Errata 12 | 13 | If you've found a typo, inaccuracy, or other issue with the text of the book, please mention it in [the book forum](https://livebook.manning.com/book/publishing-python-packages/). 14 | 15 | ## Questions 16 | 17 | If you just have a question or would like to discuss ideas from the book, [start a discussion](https://github.com/daneah/publishing-python-packages/discussions/new). [My Twitter account](https://twitter.com/easyaspython) is also a good way to reach me! 18 | 19 | ## Code of conduct 20 | 21 | Please read the [code of conduct](../CODE_OF_CONDUCT.md) before contributing to this repository. 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/code-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Code issue 3 | about: Identify an issue with the source code for an example or exercise 4 | title: A concise summary of the issue should go here 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | [Line(s) of code in question]() 11 | 12 | ## Issue 13 | 14 | 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description of issue 2 | 3 | 4 | 5 | ## Description of solution 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | day: "monday" 9 | time: "09:00" 10 | 11 | - package-ecosystem: "pip" 12 | directory: "/ch09" # Leave this as "/" in your project 13 | schedule: 14 | interval: "weekly" 15 | day: "monday" 16 | time: "09:00" 17 | -------------------------------------------------------------------------------- /.github/workflows/ch07-packaging.yml: -------------------------------------------------------------------------------- 1 | name: Packaging (chapter 7) 2 | 3 | on: 4 | - push 5 | 6 | jobs: 7 | format: 8 | name: Check formatting 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | - uses: actions/setup-python@v4.0.0 14 | with: 15 | python-version: "3.10" 16 | 17 | - name: Install tox 18 | run: python -m pip install tox 19 | 20 | - name: Run black 21 | run: tox -e format 22 | working-directory: ch07/first-python-package # You don't need this in your package 23 | 24 | lint: 25 | name: Lint 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - uses: actions/setup-python@v4.0.0 31 | with: 32 | python-version: "3.10" 33 | 34 | - name: Install tox 35 | run: python -m pip install tox 36 | 37 | - name: Run flake8 38 | run: tox -e lint 39 | working-directory: ch07/first-python-package # You don't need this in your package 40 | 41 | typecheck: 42 | name: Type check 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v3 46 | 47 | - uses: actions/setup-python@v4.0.0 48 | with: 49 | python-version: "3.10" 50 | 51 | - name: Install tox 52 | run: python -m pip install tox 53 | 54 | - name: Run mypy 55 | run: python -m tox -e typecheck 56 | working-directory: ch07/first-python-package # You don't need this in your package 57 | 58 | test: 59 | name: Test 60 | runs-on: ubuntu-latest 61 | strategy: 62 | matrix: 63 | python: 64 | - version: "3.10" 65 | toxenv: "py310" 66 | - version: "3.9" 67 | toxenv: "py39" 68 | steps: 69 | - uses: actions/checkout@v3 70 | 71 | - uses: actions/setup-python@v4.0.0 72 | with: 73 | python-version: ${{ matrix.python.version }} 74 | 75 | - name: Install tox 76 | run: python -m pip install tox 77 | 78 | - name: Run pytest 79 | run: tox -e ${{ matrix.python.toxenv }} 80 | working-directory: ch07/first-python-package # You don't need this in your package 81 | 82 | build_source_dist: 83 | name: Build source distribution 84 | runs-on: ubuntu-latest 85 | steps: 86 | - uses: actions/checkout@v3 87 | 88 | - uses: actions/setup-python@v4.0.0 89 | with: 90 | python-version: "3.10" 91 | 92 | - name: Install build 93 | run: python -m pip install build 94 | 95 | - name: Run build 96 | run: python -m build --sdist 97 | working-directory: ch07/first-python-package # You don't need this in your package 98 | 99 | - uses: actions/upload-artifact@v3 100 | with: 101 | path: ch07/first-python-package/dist/*.tar.gz 102 | 103 | build_wheels: 104 | name: Build wheels on ${{ matrix.os }} 105 | runs-on: ${{ matrix.os }} 106 | strategy: 107 | matrix: 108 | os: [ubuntu-20.04, windows-2019, macOS-10.15] 109 | 110 | steps: 111 | - uses: actions/checkout@v3 112 | 113 | - uses: actions/setup-python@v4.0.0 114 | with: 115 | python-version: "3.10" 116 | 117 | - name: Install cibuildwheel 118 | run: python -m pip install cibuildwheel==2.3.1 119 | 120 | - name: Build wheels 121 | run: python -m cibuildwheel --output-dir wheels 122 | working-directory: ch07/first-python-package # You don't need this in your package 123 | 124 | - uses: actions/upload-artifact@v3 125 | with: 126 | path: ./ch07/first-python-package/wheels/*.whl # Update to match root of package 127 | 128 | publish: 129 | name: Publish package 130 | if: startsWith(github.event.ref, 'refs/tags/v') 131 | needs: 132 | - format 133 | - lint 134 | - typecheck 135 | - test 136 | - build_source_dist 137 | - build_wheels 138 | runs-on: ubuntu-latest 139 | 140 | steps: 141 | - uses: actions/download-artifact@v3 142 | with: 143 | name: artifact 144 | path: ./ch07/first-python-package/dist # Update to match root of package 145 | 146 | - uses: pypa/gh-action-pypi-publish@v1.5.0 147 | with: 148 | user: __token__ 149 | password: ${{ secrets.PYPI_API_TOKEN }} 150 | packages_dir: ./ch07/first-python-package/dist/ # You don't need this in your package 151 | -------------------------------------------------------------------------------- /.github/workflows/ch08-packaging.yml: -------------------------------------------------------------------------------- 1 | name: Packaging (chapter 8) 2 | 3 | on: 4 | - push 5 | 6 | jobs: 7 | format: 8 | name: Check formatting 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | - uses: actions/setup-python@v4.0.0 14 | with: 15 | python-version: "3.10" 16 | 17 | - name: Install tox 18 | run: python -m pip install tox 19 | 20 | - name: Run black 21 | run: tox -e format 22 | working-directory: ch08/first-python-package # You don't need this in your package 23 | 24 | lint: 25 | name: Lint 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - uses: actions/setup-python@v4.0.0 31 | with: 32 | python-version: "3.10" 33 | 34 | - name: Install tox 35 | run: python -m pip install tox 36 | 37 | - name: Run flake8 38 | run: tox -e lint 39 | working-directory: ch08/first-python-package # You don't need this in your package 40 | 41 | typecheck: 42 | name: Type check 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v3 46 | 47 | - uses: actions/setup-python@v4.0.0 48 | with: 49 | python-version: "3.10" 50 | 51 | - name: Install tox 52 | run: python -m pip install tox 53 | 54 | - name: Run mypy 55 | run: python -m tox -e typecheck 56 | working-directory: ch08/first-python-package # You don't need this in your package 57 | 58 | test: 59 | name: Test 60 | runs-on: ubuntu-latest 61 | strategy: 62 | matrix: 63 | python: 64 | - version: "3.10" 65 | toxenv: "py310" 66 | - version: "3.9" 67 | toxenv: "py39" 68 | steps: 69 | - uses: actions/checkout@v3 70 | 71 | - uses: actions/setup-python@v4.0.0 72 | with: 73 | python-version: ${{ matrix.python.version }} 74 | 75 | - name: Install tox 76 | run: python -m pip install tox 77 | 78 | - name: Run pytest 79 | run: tox -e ${{ matrix.python.toxenv }} 80 | working-directory: ch08/first-python-package # You don't need this in your package 81 | 82 | build_source_dist: 83 | name: Build source distribution 84 | runs-on: ubuntu-latest 85 | steps: 86 | - uses: actions/checkout@v3 87 | 88 | - uses: actions/setup-python@v4.0.0 89 | with: 90 | python-version: "3.10" 91 | 92 | - name: Install build 93 | run: python -m pip install build 94 | 95 | - name: Run build 96 | run: python -m build --sdist 97 | working-directory: ch08/first-python-package # You don't need this in your package 98 | 99 | - uses: actions/upload-artifact@v3 100 | with: 101 | path: ch08/first-python-package/dist/*.tar.gz 102 | 103 | build_wheels: 104 | name: Build wheels on ${{ matrix.os }} 105 | runs-on: ${{ matrix.os }} 106 | strategy: 107 | matrix: 108 | os: [ubuntu-20.04, windows-2019, macOS-10.15] 109 | 110 | steps: 111 | - uses: actions/checkout@v3 112 | 113 | - uses: actions/setup-python@v4.0.0 114 | with: 115 | python-version: "3.10" 116 | 117 | - name: Install cibuildwheel 118 | run: python -m pip install cibuildwheel==2.3.1 119 | 120 | - name: Build wheels 121 | run: python -m cibuildwheel --output-dir wheels 122 | working-directory: ch08/first-python-package # You don't need this in your package 123 | 124 | - uses: actions/upload-artifact@v3 125 | with: 126 | path: ./ch08/first-python-package/wheels/*.whl # Update to match root of package 127 | 128 | # Avoid double-publishing from other workflows 129 | # publish: 130 | # name: Publish package 131 | # if: startsWith(github.event.ref, 'refs/tags/v') 132 | # needs: 133 | # - format 134 | # - lint 135 | # - typecheck 136 | # - test 137 | # - build_source_dist 138 | # - build_wheels 139 | # runs-on: ubuntu-latest 140 | 141 | # steps: 142 | # - uses: actions/download-artifact@v2 143 | # with: 144 | # name: artifact 145 | # path: ./ch08/first-python-package/dist # Update to match root of package 146 | 147 | # - uses: pypa/gh-action-pypi-publish@v1.4.2 148 | # with: 149 | # user: __token__ 150 | # password: ${{ secrets.PYPI_API_TOKEN }} 151 | # packages_dir: ./ch08/first-python-package/dist/ # You don't need this in your package 152 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | schedule: 9 | - cron: '37 17 * * 0' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ 'python' ] 24 | 25 | steps: 26 | - name: Checkout repository 27 | uses: actions/checkout@v3 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v2 31 | with: 32 | languages: ${{ matrix.language }} 33 | 34 | - name: Autobuild 35 | uses: github/codeql-action/autobuild@v2 36 | 37 | - name: Perform CodeQL Analysis 38 | uses: github/codeql-action/analyze@v2 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Compiled extensions 132 | *.c 133 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/asottile/pyupgrade 3 | rev: v2.31.0 4 | hooks: 5 | - id: pyupgrade 6 | args: ['--py39-plus'] 7 | 8 | - repo: https://github.com/psf/black 9 | rev: 22.1.0 10 | hooks: 11 | - id: black 12 | language_version: python3.10 13 | args: ['--config=ch09/first-python-package/pyproject.toml'] # Just use '--config=pyproject.toml' in your project 14 | 15 | - repo: https://github.com/pycqa/flake8 16 | rev: 4.0.1 17 | hooks: 18 | - id: flake8 19 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: ch08/first-python-package/docs/conf.py 5 | 6 | formats: 7 | - htmlzip 8 | 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.10" 13 | 14 | python: 15 | install: 16 | - method: pip 17 | path: ch08/first-python-package 18 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual identity 11 | and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the 27 | overall community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or 32 | advances of any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email 36 | address, without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available 127 | at [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dane Hillard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exercises for Publishing Python Packages: Test, share, and automate your projects 🐍 📦 ⬆️ 2 | 3 | ![](cover.jpg) 4 | 5 | This repository contains the source code for the examples and exercises contained in [_Publishing Python Packages: Test, share, and automate your projects_](https://pypackages.com). The repository is a template repository, so if you'd like to follow along with the book you can [make your own copy](https://github.com/daneah/publishing-python-packages/generate). 6 | 7 | Each chapter's examples are in their own directory. In some chapters, you'll find multiple snippets in a single module. These won't always produce output when you run them, and are occasionally meant only as snippets to demonstrate a concept. In later chapters, some modules act as an entrypoint to run a program from the command line, importing other modules along the way. Follow along in the book for more context! 8 | 9 | ## Errata and questions 10 | 11 | If you find an error in the code or the book, or if you have a question about the content, please read the [contribution guidelines](.github/CONTRIBUTING.md) to understand the best course of action. Errata found after the book has been printed will be published on [the book's homepage](https://pypackages.com). 12 | -------------------------------------------------------------------------------- /ch03/first-python-package/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Compiled extensions 132 | *.c 133 | -------------------------------------------------------------------------------- /ch03/first-python-package/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Dane Hillard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /ch03/first-python-package/MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src 2 | recursive-exclude __pycache__ *.py[cod] 3 | -------------------------------------------------------------------------------- /ch03/first-python-package/README.md: -------------------------------------------------------------------------------- 1 | # first-python-package 2 | 3 | This package does amazing things. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ python -m pip install first-python-package 9 | ``` 10 | -------------------------------------------------------------------------------- /ch03/first-python-package/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /ch03/first-python-package/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = first-python-package 3 | version = 0.0.1 4 | description = This package does amazing things. 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | url = https://github.com/daneah/publishing-python-packages 8 | author = Dane Hillard 9 | author_email = "Dane Hillard" 10 | license = MIT 11 | license_files = LICENSE 12 | classifiers = 13 | License :: OSI Approved :: MIT License 14 | 15 | [options] 16 | package_dir = 17 | =src 18 | packages = find: 19 | include_package_data = True 20 | 21 | [options.packages.find] 22 | where = src 23 | exclude = 24 | test* 25 | -------------------------------------------------------------------------------- /ch03/first-python-package/src/imppkg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch03/first-python-package/src/imppkg/__init__.py -------------------------------------------------------------------------------- /ch03/first-python-package/src/imppkg/data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch03/first-python-package/src/imppkg/data.json -------------------------------------------------------------------------------- /ch03/first-python-package/src/imppkg/hello.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch03/first-python-package/src/imppkg/hello.py -------------------------------------------------------------------------------- /ch04/first-python-package/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Compiled extensions 132 | *.c 133 | -------------------------------------------------------------------------------- /ch04/first-python-package/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Dane Hillard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /ch04/first-python-package/MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src 2 | recursive-exclude __pycache__ *.py[cod] 3 | -------------------------------------------------------------------------------- /ch04/first-python-package/README.md: -------------------------------------------------------------------------------- 1 | # first-python-package 2 | 3 | This package does amazing things. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ python -m pip install first-python-package 9 | ``` 10 | -------------------------------------------------------------------------------- /ch04/first-python-package/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "cython"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /ch04/first-python-package/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = first-python-package 3 | version = 0.0.1 4 | description = This package does amazing things. 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | url = https://github.com/daneah/publishing-python-packages 8 | author = Dane Hillard 9 | author_email = "Dane Hillard" 10 | license = MIT 11 | license_files = LICENSE 12 | classifiers = 13 | License :: OSI Approved :: MIT License 14 | 15 | [options] 16 | package_dir = 17 | =src 18 | packages = find: 19 | include_package_data = True 20 | install_requires = 21 | termcolor>=1.1.0,<2 22 | 23 | [options.packages.find] 24 | where = src 25 | exclude = 26 | test* 27 | 28 | [options.entry_points] 29 | console_scripts = 30 | harmony = imppkg.harmony:main 31 | -------------------------------------------------------------------------------- /ch04/first-python-package/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | from Cython.Build import cythonize 4 | 5 | setup( 6 | ext_modules=cythonize("src/imppkg/harmonic_mean.pyx"), 7 | ) 8 | -------------------------------------------------------------------------------- /ch04/first-python-package/src/imppkg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch04/first-python-package/src/imppkg/__init__.py -------------------------------------------------------------------------------- /ch04/first-python-package/src/imppkg/data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch04/first-python-package/src/imppkg/data.json -------------------------------------------------------------------------------- /ch04/first-python-package/src/imppkg/harmonic_mean.pyx: -------------------------------------------------------------------------------- 1 | def harmonic_mean(nums): 2 | return len(nums) / sum(1 / num for num in nums) 3 | -------------------------------------------------------------------------------- /ch04/first-python-package/src/imppkg/harmony.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from termcolor import cprint 4 | 5 | from imppkg.harmonic_mean import harmonic_mean 6 | 7 | 8 | def main(): 9 | result = 0.0 10 | 11 | try: 12 | nums = [float(num) for num in sys.argv[1:]] 13 | except ValueError: 14 | nums = [] 15 | 16 | try: 17 | result = harmonic_mean(nums) 18 | except ZeroDivisionError: 19 | pass 20 | 21 | cprint(result, 'red', 'on_cyan', attrs=['bold']) 22 | -------------------------------------------------------------------------------- /ch04/first-python-package/src/imppkg/hello.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch04/first-python-package/src/imppkg/hello.py -------------------------------------------------------------------------------- /ch05/first-python-package/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Compiled extensions 132 | *.c 133 | -------------------------------------------------------------------------------- /ch05/first-python-package/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Dane Hillard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /ch05/first-python-package/MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src 2 | recursive-exclude __pycache__ *.py[cod] 3 | -------------------------------------------------------------------------------- /ch05/first-python-package/README.md: -------------------------------------------------------------------------------- 1 | # first-python-package 2 | 3 | This package does amazing things. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ python -m pip install first-python-package 9 | ``` 10 | -------------------------------------------------------------------------------- /ch05/first-python-package/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "cython"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /ch05/first-python-package/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = first-python-package 3 | version = 0.0.1 4 | description = This package does amazing things. 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | url = https://github.com/daneah/publishing-python-packages 8 | author = Dane Hillard 9 | author_email = "Dane Hillard" 10 | license = MIT 11 | license_files = LICENSE 12 | classifiers = 13 | License :: OSI Approved :: MIT License 14 | 15 | [options] 16 | package_dir = 17 | =src 18 | packages = find: 19 | include_package_data = True 20 | install_requires = 21 | termcolor>=1.1.0,<2 22 | 23 | [options.packages.find] 24 | where = src 25 | exclude = 26 | test* 27 | 28 | [options.entry_points] 29 | console_scripts = 30 | harmony = imppkg.harmony:main 31 | 32 | ###################### 33 | # Tool configuration # 34 | ###################### 35 | 36 | [tool:pytest] 37 | testpaths = test 38 | addopts = --cov --strict-markers 39 | xfail_strict = True 40 | 41 | [coverage:run] 42 | source = imppkg 43 | branch = True 44 | 45 | [coverage:report] 46 | show_missing = True 47 | skip_covered = True 48 | 49 | [coverage:paths] 50 | source = 51 | src/imppkg 52 | */site-packages/imppkg 53 | 54 | [tox:tox] 55 | envlist = py39,py310 56 | isolated_build = True 57 | 58 | [testenv] 59 | deps = 60 | pytest 61 | pytest-cov 62 | commands = 63 | pytest {posargs} 64 | -------------------------------------------------------------------------------- /ch05/first-python-package/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | from Cython.Build import cythonize 4 | 5 | setup( 6 | ext_modules=cythonize("src/imppkg/harmonic_mean.pyx"), 7 | ) 8 | -------------------------------------------------------------------------------- /ch05/first-python-package/src/imppkg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch05/first-python-package/src/imppkg/__init__.py -------------------------------------------------------------------------------- /ch05/first-python-package/src/imppkg/data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch05/first-python-package/src/imppkg/data.json -------------------------------------------------------------------------------- /ch05/first-python-package/src/imppkg/harmonic_mean.pyx: -------------------------------------------------------------------------------- 1 | def harmonic_mean(nums): 2 | return len(nums) / sum(1 / num for num in nums) 3 | -------------------------------------------------------------------------------- /ch05/first-python-package/src/imppkg/harmony.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from termcolor import cprint 4 | 5 | from imppkg.harmonic_mean import harmonic_mean 6 | 7 | 8 | def main(): 9 | result = 0.0 10 | 11 | try: 12 | nums = [float(num) for num in sys.argv[1:]] 13 | except ValueError: 14 | nums = [] 15 | 16 | try: 17 | result = harmonic_mean(nums) 18 | except ZeroDivisionError: 19 | pass 20 | 21 | cprint(result, 'red', 'on_cyan', attrs=['bold']) 22 | -------------------------------------------------------------------------------- /ch05/first-python-package/src/imppkg/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch05/first-python-package/src/imppkg/py.typed -------------------------------------------------------------------------------- /ch05/first-python-package/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch05/first-python-package/test/__init__.py -------------------------------------------------------------------------------- /ch05/first-python-package/test/test_harmonic_mean.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | from termcolor import colored 5 | 6 | from imppkg.harmony import main 7 | 8 | 9 | @pytest.mark.parametrize( 10 | "inputs, expected", 11 | [ 12 | (["3", "3", "3"], 3.0), 13 | ([], 0.0), 14 | (["foo", "bar"], 0.0), 15 | ], 16 | ) 17 | def test_harmony_parametrized(inputs, monkeypatch, capsys, expected): 18 | monkeypatch.setattr(sys, "argv", ["harmony"] + inputs) 19 | main() 20 | assert capsys.readouterr().out.strip() == colored(expected, "red", "on_cyan", attrs=["bold"]) 21 | 22 | 23 | FRUITS = ["apple"] 24 | 25 | 26 | def test_len(): 27 | assert len(FRUITS) == 1 28 | 29 | 30 | def test_append(): 31 | FRUITS.append("banana") 32 | assert FRUITS == ["apple", "banana"] 33 | -------------------------------------------------------------------------------- /ch06/first-python-package/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Compiled extensions 132 | *.c 133 | -------------------------------------------------------------------------------- /ch06/first-python-package/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Dane Hillard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /ch06/first-python-package/MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src 2 | recursive-exclude __pycache__ *.py[cod] 3 | -------------------------------------------------------------------------------- /ch06/first-python-package/README.md: -------------------------------------------------------------------------------- 1 | # first-python-package 2 | 3 | This package does amazing things. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ python -m pip install first-python-package 9 | ``` 10 | -------------------------------------------------------------------------------- /ch06/first-python-package/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "cython"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.black] 6 | line-length = 120 7 | target-version = ['py39', 'py310'] 8 | -------------------------------------------------------------------------------- /ch06/first-python-package/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = first-python-package 3 | version = 0.0.1 4 | description = This package does amazing things. 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | url = https://github.com/daneah/publishing-python-packages 8 | author = Dane Hillard 9 | author_email = "Dane Hillard" 10 | license = MIT 11 | license_files = LICENSE 12 | classifiers = 13 | License :: OSI Approved :: MIT License 14 | 15 | [options] 16 | package_dir = 17 | =src 18 | packages = find: 19 | include_package_data = True 20 | install_requires = 21 | termcolor>=1.1.0,<2 22 | 23 | [options.packages.find] 24 | where = src 25 | exclude = 26 | test* 27 | 28 | [options.entry_points] 29 | console_scripts = 30 | harmony = imppkg.harmony:main 31 | 32 | ###################### 33 | # Tool configuration # 34 | ###################### 35 | 36 | [mypy] 37 | python_version = 3.10 38 | warn_unused_configs = True 39 | show_error_context = True 40 | pretty = True 41 | namespace_packages = True 42 | check_untyped_defs = True 43 | 44 | [flake8] 45 | max-line-length = 120 46 | 47 | [tool:pytest] 48 | testpaths = test 49 | addopts = --cov --strict-markers 50 | xfail_strict = True 51 | 52 | [coverage:run] 53 | source = imppkg 54 | branch = True 55 | 56 | [coverage:report] 57 | show_missing = True 58 | skip_covered = True 59 | 60 | [coverage:paths] 61 | source = 62 | src/imppkg 63 | */site-packages/imppkg 64 | 65 | [tox:tox] 66 | envlist = py39,py310 67 | isolated_build = True 68 | 69 | [testenv] 70 | deps = 71 | pytest 72 | pytest-cov 73 | commands = 74 | pytest {posargs} 75 | 76 | [testenv:typecheck] 77 | deps = 78 | mypy 79 | pytest 80 | types-termcolor 81 | commands = 82 | mypy --ignore-missing-imports {posargs:src test} 83 | 84 | [testenv:format] 85 | skip_install = True 86 | deps = 87 | black 88 | commands = 89 | black {posargs:--check --diff src test} 90 | 91 | [testenv:lint] 92 | skip_install = True 93 | deps = 94 | flake8 95 | flake8-bugbear 96 | commands = 97 | flake8 {posargs:src test} 98 | -------------------------------------------------------------------------------- /ch06/first-python-package/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | from Cython.Build import cythonize 4 | 5 | setup( 6 | ext_modules=cythonize("src/imppkg/harmonic_mean.pyx"), 7 | ) 8 | -------------------------------------------------------------------------------- /ch06/first-python-package/src/imppkg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch06/first-python-package/src/imppkg/__init__.py -------------------------------------------------------------------------------- /ch06/first-python-package/src/imppkg/data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch06/first-python-package/src/imppkg/data.json -------------------------------------------------------------------------------- /ch06/first-python-package/src/imppkg/harmonic_mean.pyx: -------------------------------------------------------------------------------- 1 | def harmonic_mean(nums): 2 | return len(nums) / sum(1 / num for num in nums) 3 | -------------------------------------------------------------------------------- /ch06/first-python-package/src/imppkg/harmony.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from termcolor import colored 4 | 5 | from imppkg.harmonic_mean import harmonic_mean 6 | 7 | 8 | def _parse_nums(inputs: list[str]) -> list[float]: 9 | try: 10 | return [float(num) for num in inputs] 11 | except ValueError: 12 | return [] 13 | 14 | 15 | def _calculate_result(nums: list[float]) -> float: 16 | try: 17 | return harmonic_mean(nums) 18 | except ZeroDivisionError: 19 | return 0.0 20 | 21 | 22 | def _format_output(result: float) -> str: 23 | return colored(str(result), "red", "on_cyan", attrs=["bold"]) 24 | 25 | 26 | def main() -> None: 27 | nums = _parse_nums(sys.argv[1:]) 28 | result = _calculate_result(nums) 29 | print(_format_output(result)) 30 | -------------------------------------------------------------------------------- /ch06/first-python-package/src/imppkg/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch06/first-python-package/src/imppkg/py.typed -------------------------------------------------------------------------------- /ch06/first-python-package/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch06/first-python-package/test/__init__.py -------------------------------------------------------------------------------- /ch06/first-python-package/test/test_harmonic_mean.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | from termcolor import colored 5 | 6 | from imppkg.harmony import main 7 | 8 | 9 | @pytest.mark.parametrize( 10 | "inputs, expected", 11 | [ 12 | (["3", "3", "3"], 3.0), 13 | ([], 0.0), 14 | (["foo", "bar"], 0.0), 15 | ], 16 | ) 17 | def test_harmony_parametrized(inputs, monkeypatch, capsys, expected): 18 | monkeypatch.setattr(sys, "argv", ["harmony"] + inputs) 19 | main() 20 | assert capsys.readouterr().out.strip() == colored(expected, "red", "on_cyan", attrs=["bold"]) 21 | 22 | 23 | FRUITS = ["apple"] 24 | 25 | 26 | def test_len(): 27 | assert len(FRUITS) == 1 28 | 29 | 30 | def test_append(): 31 | FRUITS.append("banana") 32 | assert FRUITS == ["apple", "banana"] 33 | -------------------------------------------------------------------------------- /ch07/first-python-package/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Compiled extensions 132 | *.c 133 | -------------------------------------------------------------------------------- /ch07/first-python-package/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Dane Hillard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /ch07/first-python-package/MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src 2 | recursive-exclude __pycache__ *.py[cod] 3 | -------------------------------------------------------------------------------- /ch07/first-python-package/README.md: -------------------------------------------------------------------------------- 1 | # pubpypack-harmony-dane-hillard 2 | 3 | This package does amazing things. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ python -m pip install pubpypack-harmony-dane-hillard 9 | ``` 10 | -------------------------------------------------------------------------------- /ch07/first-python-package/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "cython"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.black] 6 | line-length = 120 7 | target-version = ['py39', 'py310'] 8 | -------------------------------------------------------------------------------- /ch07/first-python-package/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = pubpypack-harmony-dane-hillard 3 | version = 0.0.2 4 | description = This package does amazing things. 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | url = https://github.com/daneah/publishing-python-packages 8 | author = Dane Hillard 9 | author_email = "Dane Hillard" 10 | license = MIT 11 | license_files = LICENSE 12 | classifiers = 13 | License :: OSI Approved :: MIT License 14 | 15 | [options] 16 | python_requires = >=3.9 17 | package_dir = 18 | =src 19 | packages = find: 20 | include_package_data = True 21 | install_requires = 22 | termcolor>=1.1.0,<2 23 | 24 | [options.packages.find] 25 | where = src 26 | exclude = 27 | test* 28 | 29 | [options.entry_points] 30 | console_scripts = 31 | harmony = imppkg.harmony:main 32 | 33 | ###################### 34 | # Tool configuration # 35 | ###################### 36 | 37 | [mypy] 38 | python_version = 3.10 39 | warn_unused_configs = True 40 | show_error_context = True 41 | pretty = True 42 | namespace_packages = True 43 | check_untyped_defs = True 44 | 45 | [flake8] 46 | max-line-length = 120 47 | 48 | [tool:pytest] 49 | testpaths = test 50 | addopts = --cov --strict-markers 51 | xfail_strict = True 52 | 53 | [coverage:run] 54 | source = imppkg 55 | branch = True 56 | 57 | [coverage:report] 58 | show_missing = True 59 | skip_covered = True 60 | 61 | [coverage:paths] 62 | source = 63 | src/imppkg 64 | */site-packages/imppkg 65 | 66 | [tox:tox] 67 | envlist = py39,py310 68 | isolated_build = True 69 | 70 | [testenv] 71 | deps = 72 | pytest 73 | pytest-cov 74 | commands = 75 | pytest {posargs} 76 | 77 | [testenv:typecheck] 78 | deps = 79 | mypy 80 | pytest 81 | types-termcolor 82 | commands = 83 | mypy --ignore-missing-imports {posargs:src test} 84 | 85 | [testenv:format] 86 | skip_install = True 87 | deps = 88 | black 89 | commands = 90 | black {posargs:--check --diff src test} 91 | 92 | [testenv:lint] 93 | skip_install = True 94 | deps = 95 | flake8 96 | flake8-bugbear 97 | commands = 98 | flake8 {posargs:src test} 99 | -------------------------------------------------------------------------------- /ch07/first-python-package/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | from Cython.Build import cythonize 4 | 5 | setup( 6 | ext_modules=cythonize("src/imppkg/harmonic_mean.pyx"), 7 | ) 8 | -------------------------------------------------------------------------------- /ch07/first-python-package/src/imppkg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch07/first-python-package/src/imppkg/__init__.py -------------------------------------------------------------------------------- /ch07/first-python-package/src/imppkg/data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch07/first-python-package/src/imppkg/data.json -------------------------------------------------------------------------------- /ch07/first-python-package/src/imppkg/harmonic_mean.pyx: -------------------------------------------------------------------------------- 1 | def harmonic_mean(nums): 2 | return len(nums) / sum(1 / num for num in nums) 3 | -------------------------------------------------------------------------------- /ch07/first-python-package/src/imppkg/harmony.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from termcolor import colored 4 | 5 | from imppkg.harmonic_mean import harmonic_mean 6 | 7 | 8 | def _parse_nums(inputs: list[str]) -> list[float]: 9 | try: 10 | return [float(num) for num in inputs] 11 | except ValueError: 12 | return [] 13 | 14 | 15 | def _calculate_result(nums: list[float]) -> float: 16 | try: 17 | return harmonic_mean(nums) 18 | except ZeroDivisionError: 19 | return 0.0 20 | 21 | 22 | def _format_output(result: float) -> str: 23 | return colored(str(result), "red", "on_cyan", attrs=["bold"]) 24 | 25 | 26 | def main() -> None: 27 | nums = _parse_nums(sys.argv[1:]) 28 | result = _calculate_result(nums) 29 | print(_format_output(result)) 30 | -------------------------------------------------------------------------------- /ch07/first-python-package/src/imppkg/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch07/first-python-package/src/imppkg/py.typed -------------------------------------------------------------------------------- /ch07/first-python-package/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch07/first-python-package/test/__init__.py -------------------------------------------------------------------------------- /ch07/first-python-package/test/test_harmonic_mean.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | from termcolor import colored 5 | 6 | from imppkg.harmony import main 7 | 8 | 9 | @pytest.mark.parametrize( 10 | "inputs, expected", 11 | [ 12 | (["3", "3", "3"], 3.0), 13 | ([], 0.0), 14 | (["foo", "bar"], 0.0), 15 | ], 16 | ) 17 | def test_harmony_parametrized(inputs, monkeypatch, capsys, expected): 18 | monkeypatch.setattr(sys, "argv", ["harmony"] + inputs) 19 | main() 20 | assert capsys.readouterr().out.strip() == colored(expected, "red", "on_cyan", attrs=["bold"]) 21 | 22 | 23 | FRUITS = ["apple"] 24 | 25 | 26 | def test_len(): 27 | assert len(FRUITS) == 1 28 | 29 | 30 | def test_append(): 31 | FRUITS.append("banana") 32 | assert FRUITS == ["apple", "banana"] 33 | -------------------------------------------------------------------------------- /ch08/first-python-package/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | docs/reference/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # pipenv 89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 92 | # install all needed dependencies. 93 | #Pipfile.lock 94 | 95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 96 | __pypackages__/ 97 | 98 | # Celery stuff 99 | celerybeat-schedule 100 | celerybeat.pid 101 | 102 | # SageMath parsed files 103 | *.sage.py 104 | 105 | # Environments 106 | .env 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | .dmypy.json 127 | dmypy.json 128 | 129 | # Pyre type checker 130 | .pyre/ 131 | 132 | # Compiled extensions 133 | *.c 134 | -------------------------------------------------------------------------------- /ch08/first-python-package/.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: docs/conf.py 5 | 6 | formats: 7 | - htmlzip 8 | 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.10" 13 | 14 | python: 15 | install: 16 | - method: pip 17 | path: . 18 | -------------------------------------------------------------------------------- /ch08/first-python-package/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Dane Hillard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /ch08/first-python-package/MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src 2 | recursive-exclude __pycache__ *.py[cod] 3 | -------------------------------------------------------------------------------- /ch08/first-python-package/README.md: -------------------------------------------------------------------------------- 1 | # first-python-package 2 | 3 | This package does amazing things. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ python -m pip install first-python-package 9 | ``` 10 | -------------------------------------------------------------------------------- /ch08/first-python-package/docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | from importlib import metadata 15 | # import sys 16 | # sys.path.insert(0, os.path.abspath('.')) 17 | 18 | 19 | # -- Project information ----------------------------------------------------- 20 | 21 | project = 'pubpypack-harmony-dane-hillard' 22 | copyright = '2022, Dane Hillard' 23 | author = 'Dane Hillard' 24 | PACKAGE_VERSION = metadata.version("pubpypack-harmony-dane-hillard") 25 | version = PACKAGE_VERSION 26 | release = PACKAGE_VERSION 27 | 28 | 29 | # -- General configuration --------------------------------------------------- 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [ 35 | 'sphinx.ext.autodoc', 36 | 'sphinx.ext.autodoc.typehints', 37 | ] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ['_templates'] 41 | 42 | # List of patterns, relative to source directory, that match files and 43 | # directories to ignore when looking for source files. 44 | # This pattern also affects html_static_path and html_extra_path. 45 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 46 | 47 | 48 | # -- Options for HTML output ------------------------------------------------- 49 | 50 | # The theme to use for HTML and HTML Help pages. See the documentation for 51 | # a list of builtin themes. 52 | # 53 | html_theme = 'alabaster' 54 | 55 | # Add any paths that contain custom static files (such as style sheets) here, 56 | # relative to this directory. They are copied after the builtin static files, 57 | # so a file named "default.css" will overwrite the builtin "default.css". 58 | html_static_path = ['_static'] 59 | 60 | 61 | # -- Setup for sphinx-apidoc ------------------------------------------------- 62 | 63 | # Read the Docs doesn't support running arbitrary commands like tox. 64 | # sphinx-apidoc needs to be called manually if Sphinx is running there. 65 | # https://github.com/readthedocs/readthedocs.org/issues/1139 66 | 67 | if os.environ.get("READTHEDOCS") == "True": 68 | from pathlib import Path 69 | 70 | PROJECT_ROOT = Path(__file__).parent.parent 71 | PACKAGE_ROOT = PROJECT_ROOT / "src" / "imppkg" 72 | 73 | def run_apidoc(_): 74 | from sphinx.ext import apidoc 75 | apidoc.main([ 76 | "--force", 77 | "--implicit-namespaces", 78 | "--module-first", 79 | "--separate", 80 | "-o", 81 | str(PROJECT_ROOT / "docs" / "reference"), 82 | str(PACKAGE_ROOT), 83 | str(PACKAGE_ROOT / "*.c"), 84 | str(PACKAGE_ROOT / "*.so"), 85 | ]) 86 | 87 | def setup(app): 88 | app.connect('builder-inited', run_apidoc) 89 | -------------------------------------------------------------------------------- /ch08/first-python-package/docs/index.rst: -------------------------------------------------------------------------------- 1 | .. pubpypack-harmony-dane-hillard documentation master file, created by 2 | sphinx-quickstart on Sun Jan 23 15:50:36 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to pubpypack-harmony-dane-hillard's documentation! 7 | ========================================================== 8 | 9 | This package provides utilities for calculating the `harmonic mean `_ of a dataset. 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | :caption: Contents: 14 | 15 | reference/modules 16 | 17 | 18 | Indices and tables 19 | ================== 20 | 21 | * :ref:`genindex` 22 | * :ref:`modindex` 23 | * :ref:`search` 24 | -------------------------------------------------------------------------------- /ch08/first-python-package/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "cython"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.black] 6 | line-length = 120 7 | target-version = ['py39', 'py310'] 8 | -------------------------------------------------------------------------------- /ch08/first-python-package/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = pubpypack-harmony-dane-hillard 3 | version = 0.0.2 4 | description = This package does amazing things. 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | url = https://github.com/daneah/publishing-python-packages 8 | author = Dane Hillard 9 | author_email = "Dane Hillard" 10 | license = MIT 11 | license_files = LICENSE 12 | classifiers = 13 | License :: OSI Approved :: MIT License 14 | 15 | [options] 16 | python_requires = >=3.9 17 | package_dir = 18 | =src 19 | packages = find: 20 | include_package_data = True 21 | install_requires = 22 | termcolor>=1.1.0,<2 23 | 24 | [options.packages.find] 25 | where = src 26 | exclude = 27 | test* 28 | 29 | [options.entry_points] 30 | console_scripts = 31 | harmony = imppkg.harmony:main 32 | 33 | ###################### 34 | # Tool configuration # 35 | ###################### 36 | 37 | [mypy] 38 | python_version = 3.10 39 | warn_unused_configs = True 40 | show_error_context = True 41 | pretty = True 42 | namespace_packages = True 43 | check_untyped_defs = True 44 | 45 | [flake8] 46 | max-line-length = 120 47 | 48 | [tool:pytest] 49 | testpaths = test 50 | addopts = --cov --strict-markers 51 | xfail_strict = True 52 | 53 | [coverage:run] 54 | source = imppkg 55 | branch = True 56 | 57 | [coverage:report] 58 | show_missing = True 59 | skip_covered = True 60 | 61 | [coverage:paths] 62 | source = 63 | src/imppkg 64 | */site-packages/imppkg 65 | 66 | [tox:tox] 67 | envlist = py39,py310 68 | isolated_build = True 69 | 70 | [testenv] 71 | deps = 72 | pytest 73 | pytest-cov 74 | commands = 75 | pytest {posargs} 76 | 77 | [testenv:typecheck] 78 | deps = 79 | mypy 80 | pytest 81 | types-termcolor 82 | commands = 83 | mypy --ignore-missing-imports {posargs:src test} 84 | 85 | [testenv:format] 86 | skip_install = True 87 | deps = 88 | black 89 | commands = 90 | black {posargs:--check --diff src test} 91 | 92 | [testenv:lint] 93 | skip_install = True 94 | deps = 95 | flake8 96 | flake8-bugbear 97 | commands = 98 | flake8 {posargs:src test} 99 | 100 | [testenv:docs] 101 | deps = 102 | sphinx 103 | commands = 104 | sphinx-apidoc \ 105 | --force \ 106 | --implicit-namespaces \ 107 | --module-first \ 108 | --separate \ 109 | -o docs/reference/ \ 110 | src/imppkg/ \ 111 | src/imppkg/*.c \ 112 | src/imppkg/*.so 113 | sphinx-build -n -W --keep-going -b html docs/ docs/_build/ 114 | 115 | [testenv:devdocs] 116 | deps = 117 | sphinx 118 | sphinx-autobuild 119 | commands = 120 | sphinx-apidoc \ 121 | --force \ 122 | --implicit-namespaces \ 123 | --module-first \ 124 | --separate \ 125 | -o docs/reference/ \ 126 | src/imppkg/ \ 127 | src/imppkg/*.c \ 128 | src/imppkg/*.so 129 | sphinx-autobuild -n -W -b html docs/ docs/_build/ 130 | -------------------------------------------------------------------------------- /ch08/first-python-package/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | from Cython.Build import cythonize 4 | 5 | setup( 6 | ext_modules=cythonize("src/imppkg/harmonic_mean.pyx"), 7 | ) 8 | -------------------------------------------------------------------------------- /ch08/first-python-package/src/imppkg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch08/first-python-package/src/imppkg/__init__.py -------------------------------------------------------------------------------- /ch08/first-python-package/src/imppkg/data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch08/first-python-package/src/imppkg/data.json -------------------------------------------------------------------------------- /ch08/first-python-package/src/imppkg/harmonic_mean.pyx: -------------------------------------------------------------------------------- 1 | def harmonic_mean(nums): 2 | """https://en.wikipedia.org/wiki/Harmonic_mean""" 3 | return len(nums) / sum(1 / num for num in nums) 4 | -------------------------------------------------------------------------------- /ch08/first-python-package/src/imppkg/harmony.py: -------------------------------------------------------------------------------- 1 | """ 2 | A command-line interface for calculating the harmonic mean of user-provided numbers. 3 | """ 4 | 5 | import sys 6 | 7 | from termcolor import colored 8 | 9 | from imppkg.harmonic_mean import harmonic_mean 10 | 11 | 12 | def _parse_nums(inputs: list[str]) -> list[float]: 13 | try: 14 | return [float(num) for num in inputs] 15 | except ValueError: 16 | return [] 17 | 18 | 19 | def _calculate_result(nums: list[float]) -> float: 20 | try: 21 | return harmonic_mean(nums) 22 | except ZeroDivisionError: 23 | return 0.0 24 | 25 | 26 | def _format_output(result: float) -> str: 27 | return colored(str(result), "red", "on_cyan", attrs=["bold"]) 28 | 29 | 30 | def main() -> None: 31 | nums = _parse_nums(sys.argv[1:]) 32 | result = _calculate_result(nums) 33 | print(_format_output(result)) 34 | -------------------------------------------------------------------------------- /ch08/first-python-package/src/imppkg/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch08/first-python-package/src/imppkg/py.typed -------------------------------------------------------------------------------- /ch08/first-python-package/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch08/first-python-package/test/__init__.py -------------------------------------------------------------------------------- /ch08/first-python-package/test/test_harmonic_mean.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | from termcolor import colored 5 | 6 | from imppkg.harmony import main 7 | 8 | 9 | @pytest.mark.parametrize( 10 | "inputs, expected", 11 | [ 12 | (["3", "3", "3"], 3.0), 13 | ([], 0.0), 14 | (["foo", "bar"], 0.0), 15 | ], 16 | ) 17 | def test_harmony_parametrized(inputs, monkeypatch, capsys, expected): 18 | monkeypatch.setattr(sys, "argv", ["harmony"] + inputs) 19 | main() 20 | assert capsys.readouterr().out.strip() == colored(expected, "red", "on_cyan", attrs=["bold"]) 21 | 22 | 23 | FRUITS = ["apple"] 24 | 25 | 26 | def test_len(): 27 | assert len(FRUITS) == 1 28 | 29 | 30 | def test_append(): 31 | FRUITS.append("banana") 32 | assert FRUITS == ["apple", "banana"] 33 | -------------------------------------------------------------------------------- /ch09/first-python-package/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | docs/reference/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # pipenv 89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 92 | # install all needed dependencies. 93 | #Pipfile.lock 94 | 95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 96 | __pypackages__/ 97 | 98 | # Celery stuff 99 | celerybeat-schedule 100 | celerybeat.pid 101 | 102 | # SageMath parsed files 103 | *.sage.py 104 | 105 | # Environments 106 | .env 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | .dmypy.json 127 | dmypy.json 128 | 129 | # Pyre type checker 130 | .pyre/ 131 | 132 | # Compiled extensions 133 | *.c 134 | -------------------------------------------------------------------------------- /ch09/first-python-package/.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: docs/conf.py 5 | 6 | formats: 7 | - htmlzip 8 | 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.10" 13 | 14 | python: 15 | install: 16 | - method: pip 17 | path: . 18 | -------------------------------------------------------------------------------- /ch09/first-python-package/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Dane Hillard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /ch09/first-python-package/MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src 2 | recursive-exclude __pycache__ *.py[cod] 3 | -------------------------------------------------------------------------------- /ch09/first-python-package/README.md: -------------------------------------------------------------------------------- 1 | # first-python-package 2 | 3 | This package does amazing things. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ python -m pip install first-python-package 9 | ``` 10 | -------------------------------------------------------------------------------- /ch09/first-python-package/docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | from importlib import metadata 15 | # import sys 16 | # sys.path.insert(0, os.path.abspath('.')) 17 | 18 | 19 | # -- Project information ----------------------------------------------------- 20 | 21 | project = 'pubpypack-harmony-dane-hillard' 22 | copyright = '2022, Dane Hillard' 23 | author = 'Dane Hillard' 24 | PACKAGE_VERSION = metadata.version("pubpypack-harmony-dane-hillard") 25 | version = PACKAGE_VERSION 26 | release = PACKAGE_VERSION 27 | 28 | 29 | # -- General configuration --------------------------------------------------- 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [ 35 | 'sphinx.ext.autodoc', 36 | 'sphinx.ext.autodoc.typehints', 37 | ] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ['_templates'] 41 | 42 | # List of patterns, relative to source directory, that match files and 43 | # directories to ignore when looking for source files. 44 | # This pattern also affects html_static_path and html_extra_path. 45 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 46 | 47 | 48 | # -- Options for HTML output ------------------------------------------------- 49 | 50 | # The theme to use for HTML and HTML Help pages. See the documentation for 51 | # a list of builtin themes. 52 | # 53 | html_theme = 'alabaster' 54 | 55 | # Add any paths that contain custom static files (such as style sheets) here, 56 | # relative to this directory. They are copied after the builtin static files, 57 | # so a file named "default.css" will overwrite the builtin "default.css". 58 | html_static_path = ['_static'] 59 | 60 | 61 | # -- Setup for sphinx-apidoc ------------------------------------------------- 62 | 63 | # Read the Docs doesn't support running arbitrary commands like tox. 64 | # sphinx-apidoc needs to be called manually if Sphinx is running there. 65 | # https://github.com/readthedocs/readthedocs.org/issues/1139 66 | 67 | if os.environ.get("READTHEDOCS") == "True": 68 | from pathlib import Path 69 | 70 | PROJECT_ROOT = Path(__file__).parent.parent 71 | PACKAGE_ROOT = PROJECT_ROOT / "src" / "imppkg" 72 | 73 | def run_apidoc(_): 74 | from sphinx.ext import apidoc 75 | apidoc.main([ 76 | "--force", 77 | "--implicit-namespaces", 78 | "--module-first", 79 | "--separate", 80 | "-o", 81 | str(PROJECT_ROOT / "docs" / "reference"), 82 | str(PACKAGE_ROOT), 83 | str(PACKAGE_ROOT / "*.c"), 84 | str(PACKAGE_ROOT / "*.so"), 85 | ]) 86 | 87 | def setup(app): 88 | app.connect('builder-inited', run_apidoc) 89 | -------------------------------------------------------------------------------- /ch09/first-python-package/docs/index.rst: -------------------------------------------------------------------------------- 1 | .. pubpypack-harmony-dane-hillard documentation master file, created by 2 | sphinx-quickstart on Sun Jan 23 15:50:36 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to pubpypack-harmony-dane-hillard's documentation! 7 | ========================================================== 8 | 9 | This package provides utilities for calculating the `harmonic mean `_ of a dataset. 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | :caption: Contents: 14 | 15 | reference/modules 16 | 17 | 18 | Indices and tables 19 | ================== 20 | 21 | * :ref:`genindex` 22 | * :ref:`modindex` 23 | * :ref:`search` 24 | -------------------------------------------------------------------------------- /ch09/first-python-package/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "cython"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.black] 6 | line-length = 120 7 | target-version = ['py39', 'py310'] 8 | -------------------------------------------------------------------------------- /ch09/first-python-package/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = pubpypack-harmony-dane-hillard 3 | version = 0.0.2 4 | description = This package does amazing things. 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | url = https://github.com/daneah/publishing-python-packages 8 | author = Dane Hillard 9 | author_email = "Dane Hillard" 10 | license = MIT 11 | license_files = LICENSE 12 | classifiers = 13 | License :: OSI Approved :: MIT License 14 | 15 | [options] 16 | python_requires = >=3.9 17 | package_dir = 18 | =src 19 | packages = find: 20 | include_package_data = True 21 | install_requires = 22 | termcolor>=1.1.0,<2 23 | 24 | [options.packages.find] 25 | where = src 26 | exclude = 27 | test* 28 | 29 | [options.entry_points] 30 | console_scripts = 31 | harmony = imppkg.harmony:main 32 | 33 | ###################### 34 | # Tool configuration # 35 | ###################### 36 | 37 | [mypy] 38 | python_version = 3.10 39 | warn_unused_configs = True 40 | show_error_context = True 41 | pretty = True 42 | namespace_packages = True 43 | check_untyped_defs = True 44 | 45 | [flake8] 46 | max-line-length = 120 47 | 48 | [tool:pytest] 49 | testpaths = test 50 | addopts = --cov --strict-markers 51 | xfail_strict = True 52 | 53 | [coverage:run] 54 | source = imppkg 55 | branch = True 56 | 57 | [coverage:report] 58 | fail_under = 100.0 59 | show_missing = True 60 | skip_covered = True 61 | 62 | [coverage:paths] 63 | source = 64 | src/imppkg 65 | */site-packages/imppkg 66 | 67 | [tox:tox] 68 | envlist = py39,py310 69 | isolated_build = True 70 | 71 | [testenv] 72 | deps = 73 | pytest 74 | pytest-cov 75 | commands = 76 | pytest {posargs} 77 | 78 | [testenv:typecheck] 79 | deps = 80 | mypy 81 | pytest 82 | types-termcolor 83 | commands = 84 | mypy --ignore-missing-imports {posargs:src test} 85 | 86 | [testenv:format] 87 | skip_install = True 88 | deps = 89 | black 90 | commands = 91 | black {posargs:--check --diff src test} 92 | 93 | [testenv:lint] 94 | skip_install = True 95 | deps = 96 | flake8 97 | flake8-bugbear 98 | commands = 99 | flake8 {posargs:src test} 100 | 101 | [testenv:docs] 102 | deps = 103 | sphinx 104 | commands = 105 | sphinx-apidoc \ 106 | --force \ 107 | --implicit-namespaces \ 108 | --module-first \ 109 | --separate \ 110 | -o docs/reference/ \ 111 | src/imppkg/ \ 112 | src/imppkg/*.c \ 113 | src/imppkg/*.so 114 | sphinx-build -n -W --keep-going -b html docs/ docs/_build/ 115 | 116 | [testenv:devdocs] 117 | deps = 118 | sphinx 119 | sphinx-autobuild 120 | commands = 121 | sphinx-apidoc \ 122 | --force \ 123 | --implicit-namespaces \ 124 | --module-first \ 125 | --separate \ 126 | -o docs/reference/ \ 127 | src/imppkg/ \ 128 | src/imppkg/*.c \ 129 | src/imppkg/*.so 130 | sphinx-autobuild -n -W -b html docs/ docs/_build/ 131 | -------------------------------------------------------------------------------- /ch09/first-python-package/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | from Cython.Build import cythonize 4 | 5 | setup( 6 | ext_modules=cythonize("src/imppkg/harmonic_mean.pyx"), 7 | ) 8 | -------------------------------------------------------------------------------- /ch09/first-python-package/src/imppkg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch09/first-python-package/src/imppkg/__init__.py -------------------------------------------------------------------------------- /ch09/first-python-package/src/imppkg/data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch09/first-python-package/src/imppkg/data.json -------------------------------------------------------------------------------- /ch09/first-python-package/src/imppkg/harmonic_mean.pyx: -------------------------------------------------------------------------------- 1 | def harmonic_mean(nums): 2 | """https://en.wikipedia.org/wiki/Harmonic_mean""" 3 | return len(nums) / sum(1 / num for num in nums) 4 | -------------------------------------------------------------------------------- /ch09/first-python-package/src/imppkg/harmony.py: -------------------------------------------------------------------------------- 1 | """ 2 | A command-line interface for calculating the harmonic mean of user-provided numbers. 3 | """ 4 | 5 | import sys 6 | 7 | from termcolor import colored 8 | 9 | from imppkg.harmonic_mean import harmonic_mean 10 | 11 | 12 | def _parse_nums(inputs: list[str]) -> list[float]: 13 | try: 14 | return [float(num) for num in inputs] 15 | except ValueError: 16 | return [] 17 | 18 | 19 | def _calculate_result(nums: list[float]) -> float: 20 | try: 21 | return harmonic_mean(nums) 22 | except ZeroDivisionError: 23 | return 0.0 24 | 25 | 26 | def _format_output(result: float) -> str: 27 | return colored(str(result), "red", "on_cyan", attrs=["bold"]) 28 | 29 | 30 | def main() -> None: 31 | nums = _parse_nums(sys.argv[1:]) 32 | result = _calculate_result(nums) 33 | print(_format_output(result)) 34 | -------------------------------------------------------------------------------- /ch09/first-python-package/src/imppkg/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch09/first-python-package/src/imppkg/py.typed -------------------------------------------------------------------------------- /ch09/first-python-package/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch09/first-python-package/test/__init__.py -------------------------------------------------------------------------------- /ch09/first-python-package/test/test_harmonic_mean.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | from termcolor import colored 5 | 6 | from imppkg.harmony import main 7 | 8 | 9 | @pytest.mark.parametrize( 10 | "inputs, expected", 11 | [ 12 | (["3", "3", "3"], 3.0), 13 | ([], 0.0), 14 | (["foo", "bar"], 0.0), 15 | ], 16 | ) 17 | def test_harmony_parametrized(inputs, monkeypatch, capsys, expected): 18 | monkeypatch.setattr(sys, "argv", ["harmony"] + inputs) 19 | main() 20 | assert capsys.readouterr().out.strip() == colored(expected, "red", "on_cyan", attrs=["bold"]) 21 | 22 | 23 | FRUITS = ["apple"] 24 | 25 | 26 | def test_len(): 27 | assert len(FRUITS) == 1 28 | 29 | 30 | def test_append(): 31 | FRUITS.append("banana") 32 | assert FRUITS == ["apple", "banana"] 33 | -------------------------------------------------------------------------------- /ch10/python-package-template/cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "package_distribution_name": "my-python-package", 3 | "package_import_name": [ 4 | "{{ cookiecutter.package_distribution_name.lower().replace('-', '') }}", 5 | "{{ cookiecutter.package_distribution_name.lower().replace('-', '_') }}" 6 | ], 7 | "package_description": "The {{ cookiecutter.package_distribution_name }} package does amazing things.", 8 | "package_license": ["MIT License", "BSD License", "ISC License (ISCL)", "GNU General Public License v3 (GPLv3)", "Apache Software License"], 9 | "author_name": "First Last", 10 | "author_email": "first@last.com" 11 | } 12 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | docs/reference/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # pipenv 89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 92 | # install all needed dependencies. 93 | #Pipfile.lock 94 | 95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 96 | __pypackages__/ 97 | 98 | # Celery stuff 99 | celerybeat-schedule 100 | celerybeat.pid 101 | 102 | # SageMath parsed files 103 | *.sage.py 104 | 105 | # Environments 106 | .env 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | .dmypy.json 127 | dmypy.json 128 | 129 | # Pyre type checker 130 | .pyre/ 131 | 132 | # Compiled extensions 133 | *.c 134 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: docs/conf.py 5 | 6 | formats: 7 | - htmlzip 8 | 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.10" 13 | 14 | python: 15 | install: 16 | - method: pip 17 | path: . 18 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/LICENSE: -------------------------------------------------------------------------------- 1 | **REPLACE THIS CONTENT WITH THE LICENSE LOCATED AT THE FOLLOWING URL**: 2 | 3 | {% if cookiecutter.package_license == 'MIT License' %} 4 | https://opensource.org/licenses/MIT 5 | {% elif cookiecutter.package_license == 'BSD License' %} 6 | https://opensource.org/licenses/0BSD 7 | {% elif cookiecutter.package_license == 'ISC License (ISCL)' %} 8 | https://opensource.org/licenses/ISC 9 | {% elif cookiecutter.package_license == 'GNU General Public License v3 (GLPv3)' %} 10 | https://opensource.org/licenses/GPL-3.0 11 | {% elif cookiecutter.package_license == 'Apache Software License' %} 12 | https://opensource.org/licenses/Apache-2.0 13 | {% endif %} 14 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src 2 | recursive-exclude __pycache__ *.py[cod] 3 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/README.md: -------------------------------------------------------------------------------- 1 | # {{ cookiecutter.package_distribution_name }} 2 | 3 | This package does amazing things. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ python -m pip install {{ cookiecutter.package_distribution_name }} 9 | ``` 10 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | from importlib import metadata 15 | # import sys 16 | # sys.path.insert(0, os.path.abspath('.')) 17 | 18 | 19 | # -- Project information ----------------------------------------------------- 20 | 21 | project = '{{ cookiecutter.package_distribution_name }}' 22 | copyright = '2022, Dane Hillard' 23 | author = 'Dane Hillard' 24 | PACKAGE_VERSION = metadata.version("{{ cookiecutter.package_distribution_name }}") 25 | version = PACKAGE_VERSION 26 | release = PACKAGE_VERSION 27 | 28 | 29 | # -- General configuration --------------------------------------------------- 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [ 35 | 'sphinx.ext.autodoc', 36 | 'sphinx.ext.autodoc.typehints', 37 | ] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ['_templates'] 41 | 42 | # List of patterns, relative to source directory, that match files and 43 | # directories to ignore when looking for source files. 44 | # This pattern also affects html_static_path and html_extra_path. 45 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 46 | 47 | 48 | # -- Options for HTML output ------------------------------------------------- 49 | 50 | # The theme to use for HTML and HTML Help pages. See the documentation for 51 | # a list of builtin themes. 52 | # 53 | html_theme = 'alabaster' 54 | 55 | # Add any paths that contain custom static files (such as style sheets) here, 56 | # relative to this directory. They are copied after the builtin static files, 57 | # so a file named "default.css" will overwrite the builtin "default.css". 58 | html_static_path = ['_static'] 59 | 60 | 61 | # -- Setup for sphinx-apidoc ------------------------------------------------- 62 | 63 | # Read the Docs doesn't support running arbitrary commands like tox. 64 | # sphinx-apidoc needs to be called manually if Sphinx is running there. 65 | # https://github.com/readthedocs/readthedocs.org/issues/1139 66 | 67 | if os.environ.get("READTHEDOCS") == "True": 68 | from pathlib import Path 69 | 70 | PROJECT_ROOT = Path(__file__).parent.parent 71 | PACKAGE_ROOT = PROJECT_ROOT / "src" / "{{ cookiecutter.package_import_name }}" 72 | 73 | def run_apidoc(_): 74 | from sphinx.ext import apidoc 75 | apidoc.main([ 76 | "--force", 77 | "--implicit-namespaces", 78 | "--module-first", 79 | "--separate", 80 | "-o", 81 | str(PROJECT_ROOT / "docs" / "reference"), 82 | str(PACKAGE_ROOT), 83 | str(PACKAGE_ROOT / "*.c"), 84 | str(PACKAGE_ROOT / "*.so"), 85 | ]) 86 | 87 | def setup(app): 88 | app.connect('builder-inited', run_apidoc) 89 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to {{ cookiecutter.package_distribution_name }}'s documentation! 2 | ========================================================== 3 | 4 | {{ cookiecutter.package_description }} 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | :caption: Contents: 9 | 10 | reference/modules 11 | 12 | 13 | Indices and tables 14 | ================== 15 | 16 | * :ref:`genindex` 17 | * :ref:`modindex` 18 | * :ref:`search` 19 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.black] 6 | line-length = 120 7 | target-version = ['py39', 'py310'] 8 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = {{ cookiecutter.package_distribution_name }} 3 | version = 0.0.1 4 | description = {{ cookiecutter.package_description }} 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | url = https://github.com/daneah/publishing-python-packages 8 | author = {{ cookiecutter.author_name }} 9 | author_email = "{{ cookiecutter.author_name }}" <{{ cookiecutter.author_email }}> 10 | license = {{ cookiecutter.package_license }} 11 | license_files = LICENSE 12 | classifiers = 13 | License :: OSI Approved :: {{ cookiecutter.package_license }} 14 | 15 | [options] 16 | python_requires = >=3.9 17 | package_dir = 18 | =src 19 | packages = find_namespace: 20 | include_package_data = True 21 | 22 | [options.packages.find] 23 | where = src 24 | exclude = 25 | test* 26 | 27 | ###################### 28 | # Tool configuration # 29 | ###################### 30 | 31 | [mypy] 32 | python_version = 3.10 33 | warn_unused_configs = True 34 | show_error_context = True 35 | pretty = True 36 | namespace_packages = True 37 | check_untyped_defs = True 38 | 39 | [flake8] 40 | max-line-length = 120 41 | 42 | [tool:pytest] 43 | testpaths = test 44 | addopts = --cov --strict-markers 45 | xfail_strict = True 46 | 47 | [coverage:run] 48 | source = {{ cookiecutter.package_import_name }} 49 | branch = True 50 | 51 | [coverage:report] 52 | fail_under = 100.0 53 | show_missing = True 54 | skip_covered = True 55 | 56 | [coverage:paths] 57 | source = 58 | src/{{ cookiecutter.package_import_name }} 59 | */site-packages/{{ cookiecutter.package_import_name }} 60 | 61 | [tox:tox] 62 | envlist = py39,py310 63 | isolated_build = True 64 | 65 | [testenv] 66 | deps = 67 | pytest 68 | pytest-cov 69 | commands = 70 | pytest {posargs} 71 | 72 | [testenv:typecheck] 73 | deps = 74 | mypy 75 | pytest 76 | types-termcolor 77 | commands = 78 | mypy --ignore-missing-imports {posargs:src test} 79 | 80 | [testenv:format] 81 | skip_install = True 82 | deps = 83 | black 84 | commands = 85 | black {posargs:--check --diff src test} 86 | 87 | [testenv:lint] 88 | skip_install = True 89 | deps = 90 | flake8 91 | flake8-bugbear 92 | commands = 93 | flake8 {posargs:src test} 94 | 95 | [testenv:docs] 96 | deps = 97 | sphinx 98 | commands = 99 | sphinx-apidoc \ 100 | --force \ 101 | --implicit-namespaces \ 102 | --module-first \ 103 | --separate \ 104 | -o docs/reference/ \ 105 | src/{{ cookiecutter.package_import_name }}/ \ 106 | src/{{ cookiecutter.package_import_name }}/*.c \ 107 | src/{{ cookiecutter.package_import_name }}/*.so 108 | sphinx-build -n -W --keep-going -b html docs/ docs/_build/ 109 | 110 | [testenv:devdocs] 111 | deps = 112 | sphinx 113 | sphinx-autobuild 114 | commands = 115 | sphinx-apidoc \ 116 | --force \ 117 | --implicit-namespaces \ 118 | --module-first \ 119 | --separate \ 120 | -o docs/reference/ \ 121 | src/{{ cookiecutter.package_import_name }}/ \ 122 | src/{{ cookiecutter.package_import_name }}/*.c \ 123 | src/{{ cookiecutter.package_import_name }}/*.so 124 | sphinx-autobuild -n -W -b html docs/ docs/_build/ 125 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup() 4 | -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/src/{{cookiecutter.package_import_name}}/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch10/python-package-template/{{cookiecutter.package_distribution_name}}/src/{{cookiecutter.package_import_name}}/__init__.py -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/src/{{cookiecutter.package_import_name}}/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch10/python-package-template/{{cookiecutter.package_distribution_name}}/src/{{cookiecutter.package_import_name}}/py.typed -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/ch10/python-package-template/{{cookiecutter.package_distribution_name}}/test/__init__.py -------------------------------------------------------------------------------- /ch10/python-package-template/{{cookiecutter.package_distribution_name}}/test/test_{{cookiecutter.package_import_name}}.py: -------------------------------------------------------------------------------- 1 | def test_package_is_importable(): 2 | import {{ cookiecutter.package_import_name }} 3 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daneah/publishing-python-packages/66ba04cbe28d26ac7ff63440db96a44d9a373bd5/cover.jpg --------------------------------------------------------------------------------