├── .all-contributorsrc ├── .circleci └── config.yml ├── .github ├── dependabot.yml └── workflows │ ├── artifact_redirect.yml │ ├── build-book.yml │ ├── crowdin_sync.yml │ └── main.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .vale.ini ├── .zenodo.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── TRANSLATING.md ├── _ext ├── __init__.py └── rss.py ├── _static ├── fonts │ ├── itim-v14-latin-regular.woff2 │ ├── poppins-v20-latin-500.woff2 │ ├── poppins-v20-latin-600.woff2 │ ├── poppins-v20-latin-700.woff2 │ ├── poppins-v20-latin-700italic.woff2 │ ├── poppins-v20-latin-italic.woff2 │ └── poppins-v20-latin-regular.woff2 ├── language_select.js ├── logo-dark-mode.png ├── logo-light-mode.png ├── matomo.js ├── pyopensci-logo-package-guide.png ├── pyopensci-python-package-guide.png ├── pyos.css └── translation_stats.json ├── _templates ├── code_of_conduct.html └── language-selector.html ├── bibliography.bib ├── codespell-ignore.txt ├── conf.py ├── continuous-integration ├── ci.md └── index.md ├── crowdin.yml ├── documentation ├── hosting-tools │ ├── intro.md │ ├── myst-markdown-rst-doc-syntax.md │ ├── publish-documentation-online.md │ ├── sphinx-python-package-documentation-tools.md │ └── website-hosting-optimizing-your-docs.md ├── index.md ├── repository-files │ ├── changelog-file.md │ ├── code-of-conduct-file.md │ ├── contributing-file.md │ ├── development-guide.md │ ├── intro.md │ ├── license-files.md │ └── readme-file-best-practices.md └── write-user-documentation │ ├── create-package-tutorials.md │ ├── document-your-code-api-docstrings.md │ ├── get-started.md │ └── intro.md ├── examples ├── extension-hatch │ ├── README │ ├── examplePy │ │ ├── __init__.py │ │ ├── meson.build │ │ └── temperature.c │ ├── meson.build │ └── pyproject.toml ├── pure-hatch │ ├── .pre-commit-config.yaml │ ├── pyproject.toml │ └── src │ │ └── examplePy │ │ ├── temperature.py │ │ ├── temporal-raw.py │ │ └── temporal.py └── pure-setuptools │ └── pyproject.toml ├── images ├── code-cov-stravalib.png ├── conda-channels-geohackweek.jpeg ├── conda-forge-staged-recipes-ci.png ├── contributing │ ├── clone-repository.png │ ├── commit-changes.png │ ├── edit-button-pencil.png │ ├── edit-file.png │ ├── new-pull-request.png │ ├── preview-changes.png │ ├── pull-requests-checks-fails.png │ ├── pull-requests-checks.png │ └── pull-requests-tab.png ├── flower-puzzle-pyopensci.jpg ├── geopandas-documentation-landing-page.png ├── license-github-root-dir.png ├── moving-pandas-python-package-github-community-standards.png ├── moving-pandas-python-package-github-main-repo.png ├── moving-pandas-python-package-snyk-health.png ├── packaging-lifecycle.png ├── pandera-python-package-readme-github.png ├── precommit-hook-python-code.png ├── publish-python-package-pypi-conda.png ├── pyopensci-puzzle-pieces-tests.png ├── pyopensci-python-package-pypi-to-conda-forge.png ├── pypi-project-landing-page-no-meta.png ├── python-build-package │ ├── pypi-metadata-classifiers.png │ ├── pypi-metadata-keywords-license.png │ └── pypi-metadata-maintainers.png ├── python-dependency-conflicts-xkcd.png ├── python-flying-xkcd.png ├── python-package-dependencies.png ├── python-package-dependency-types.png ├── python-package-development-process.png ├── python-package-documentation-nb_sphinx-gallery-output.png ├── python-package-tools-2022-survey-pypa.png ├── python-package-tools-decision-tree.png ├── python-pypi-conda-channels.png ├── python-tests-puzzle-fit.png ├── python-tests-puzzle.png ├── sphinx-gallery-overview.png ├── sphinx-gallery-tutorial.png ├── sphinx-rendering-extent-to-json-earthpy.png └── tutorials │ ├── code-to-python-package.png │ ├── environment-package-install.png │ ├── github-new-repo.png │ ├── package-components.png │ ├── packaging-101-outline.png │ ├── packaging-elements.png │ ├── packaging-lifecycle.png │ ├── publish-package-pypi-conda.png │ ├── test-pypi-package.png │ ├── testpypi-search.png │ ├── toolbox.png │ └── view-license-github.png ├── index.md ├── locales ├── es │ └── LC_MESSAGES │ │ ├── CONTRIBUTING.po │ │ ├── TRANSLATING.po │ │ ├── continuous-integration.po │ │ ├── documentation.po │ │ ├── index.po │ │ ├── package-structure-code.po │ │ ├── tests.po │ │ └── tutorials.po └── ja │ └── LC_MESSAGES │ ├── CONTRIBUTING.po │ ├── TRANSLATING.po │ ├── continuous-integration.po │ ├── documentation.po │ ├── index.po │ ├── package-structure-code.po │ ├── tests.po │ └── tutorials.po ├── noxfile.py ├── package-structure-code ├── code-style-linting-format.md ├── complex-python-package-builds.md ├── declare-dependencies.md ├── intro.md ├── publish-python-package-pypi-conda.md ├── pyproject-toml-python-package-metadata.md ├── python-package-build-tools.md ├── python-package-distribution-files-sdist-wheel.md ├── python-package-structure.md └── python-package-versions.md ├── pyproject.toml ├── scripts └── translation_stats.py ├── tests ├── code-cov.md ├── index.md ├── run-tests.md ├── test-types.md ├── tests-ci.md └── write-tests.md ├── tutorials ├── add-license-coc.md ├── add-readme.md ├── command-line-reference.md ├── get-to-know-hatch.md ├── installable-code.md ├── intro.md ├── publish-conda-forge.md ├── publish-pypi.md ├── pyproject-toml.md └── setup-py-to-pyproject-toml.md └── vale-styles ├── config └── vocabularies │ └── sample │ ├── accept.txt │ └── reject.txt ├── package-guide-test └── PyPI.yml └── write-good ├── Cliches.yml ├── E-Prime.yml ├── Illusions.yml ├── Passive.yml ├── README.md ├── So.yml ├── ThereIs.yml ├── TooWordy.yml ├── Weasel.yml └── meta.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | jobs: 3 | build-book: 4 | docker: 5 | - image: cimg/python:3.13 6 | steps: 7 | - checkout 8 | - run: 9 | name: setup environment 10 | command: | 11 | pip install --upgrade pip 12 | pip install nox 13 | pip list 14 | - run: 15 | name: Build book html 16 | command: nox -s docs 17 | 18 | - store_artifacts: 19 | path: _build/html 20 | destination: html 21 | 22 | # Tell CircleCI to use this workflow when it builds the site 23 | workflows: 24 | version: 2 25 | build-book: 26 | jobs: 27 | - build-book 28 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/" 5 | insecure-external-code-execution: allow 6 | schedule: 7 | interval: "daily" 8 | open-pull-requests-limit: 100 9 | labels: 10 | - "maintenance" 11 | - "dependencies" 12 | groups: 13 | pip: 14 | patterns: 15 | - "*" 16 | commit-message: 17 | prefix: "chore" 18 | - package-ecosystem: "github-actions" 19 | directory: "/" 20 | schedule: 21 | interval: "daily" 22 | open-pull-requests-limit: 100 23 | labels: 24 | - "maintenance" 25 | - "dependencies" 26 | groups: 27 | actions: 28 | patterns: 29 | - "*" 30 | commit-message: 31 | prefix: "chore" 32 | -------------------------------------------------------------------------------- /.github/workflows/artifact_redirect.yml: -------------------------------------------------------------------------------- 1 | name: Book Preview Redirect 2 | on: [status] 3 | 4 | concurrency: 5 | group: docs-preview-${{ github.ref }} 6 | cancel-in-progress: true 7 | 8 | jobs: 9 | circleci_artifacts_redirector_job: 10 | runs-on: ubuntu-latest 11 | # For testing this action on a fork, remove the "github.repository =="" condition. 12 | if: "github.event.context == 'ci/circleci: build-book'" 13 | permissions: 14 | statuses: write 15 | name: Run CircleCI artifacts redirector 16 | steps: 17 | - name: GitHub Action step 18 | id: step1 19 | uses: larsoner/circleci-artifacts-redirector-action@master 20 | with: 21 | repo-token: ${{ secrets.GITHUB_TOKEN }} 22 | api-token: ${{ secrets.CIRCLECI_TOKEN }} 23 | artifact-path: 0/html/index.html 24 | circleci-jobs: build-book 25 | job-title: View rendered book here! 26 | -------------------------------------------------------------------------------- /.github/workflows/build-book.yml: -------------------------------------------------------------------------------- 1 | name: build-test-deploy-book 2 | 3 | # Only build PRs, the main branch, and releases. Pushes to branches will only 4 | # be built when a PR is opened. This avoids duplicated builds in PRs coming 5 | # from branches in the origin repository (1 for PR and 1 for push). 6 | # This came from Leo's work with fatiando 7 | on: 8 | pull_request: 9 | push: 10 | branches: 11 | - main 12 | 13 | # This job installs dependencies, build the book, and pushes it to `gh-pages` 14 | jobs: 15 | build-test-book: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Setup Python 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: "3.13" 24 | 25 | - name: Upgrade pip 26 | run: | 27 | # install pip=>20.1 to use "pip cache dir" 28 | python3 -m pip install --upgrade pip 29 | - name: Get pip cache dir 30 | id: pip-cache 31 | run: echo "::set-output name=dir::$(pip cache dir)" 32 | 33 | - name: Cache dependencies 34 | uses: actions/cache@v4 35 | with: 36 | path: ${{ steps.pip-cache.outputs.dir }} 37 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} 38 | restore-keys: | 39 | ${{ runner.os }}-pip- 40 | 41 | - name: Install dependencies 42 | run: python3 -m pip install nox 43 | 44 | - name: Build book 45 | run: nox -s docs-test 46 | 47 | # Save html as artifact 48 | - name: Save book html as artifact for viewing 49 | uses: actions/upload-artifact@v4 50 | with: 51 | name: book-html 52 | path: | 53 | _build/html/ 54 | 55 | # Push the book's HTML to github-pages 56 | - name: Push to GitHub Pages 57 | # Only push if on main branch 58 | if: github.ref == 'refs/heads/main' 59 | uses: peaceiris/actions-gh-pages@v4.0.0 60 | with: 61 | github_token: ${{ secrets.GITHUB_TOKEN }} 62 | publish_dir: ./_build/html 63 | 64 | # Test for bad links and ensure alt tags for usability 65 | - name: Check HTML using htmlproofer 66 | uses: chabad360/htmlproofer@master 67 | with: 68 | directory: "_build/html" 69 | arguments: | 70 | --ignore-files "/.+\/_static\/.+/,/genindex.html/" 71 | --ignore-status-codes "0, 200, 403, 429, 503" 72 | -------------------------------------------------------------------------------- /.github/workflows/crowdin_sync.yml: -------------------------------------------------------------------------------- 1 | name: Crowdin Sync 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | sync-translations: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout repo 12 | uses: actions/checkout@v4 13 | 14 | - name: Sync with Crowdin 15 | uses: crowdin/github-action@v2 16 | with: 17 | upload_sources: true 18 | upload_translations: false 19 | download_translations: true 20 | localization_branch_name: l10n_crowdin_translations 21 | create_pull_request: true 22 | pull_request_title: "Crowdin: New translations" 23 | pull_request_body: "Crowdin synced updated translations via GitHub Action." 24 | pull_request_base_branch_name: "main" 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN_CROWDIN }} 27 | CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} 28 | CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} 29 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Add help-wanted issues to help wanted board 2 | 3 | on: 4 | issues: 5 | types: 6 | - labeled 7 | 8 | jobs: 9 | add-help-wanted: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Add issue to project 13 | id: add-to-project 14 | uses: actions/add-to-project@v1.0.2 15 | with: 16 | project-url: https://github.com/orgs/pyOpenSci/projects/3 17 | # This is a organization level token so it can be used across all repos in our org 18 | github-token: ${{ secrets.GHPROJECT_HELP_WANTED }} 19 | labeled: help wanted, sprintable 20 | label-operator: OR 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Vscode 2 | .vscode/ 3 | 4 | # Python 5 | tmp/ 6 | .DS_Store 7 | *notes-from-review.md 8 | *.idea* 9 | # Grammar / syntax checkers 10 | styles/ 11 | # Exclude translation .mo files 12 | locales/*/LC_MESSAGES/*.mo 13 | 14 | # Exclude Jupyter Notebook checkpoints 15 | .ipynb_checkpoints/ 16 | */.ipynb_checkpoints/* 17 | 18 | # Exclude Python bytecode 19 | __pycache__/ 20 | 21 | # Exclude build directories 22 | _build/ 23 | 24 | # Exclude virtual environments 25 | venv/ 26 | env/ 27 | ENV/ 28 | .venv/ 29 | .ENV/ 30 | */venv/ 31 | .nox 32 | */.nox/ 33 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # pre-commit is a tool that you run locally 2 | # to perform a predefined set of tasks manually and/or 3 | # automatically before git commits are made. 4 | # Here we are using pre-commit with the precommit.ci bot to implement 5 | # code fixes automagically in pr's. You will still want to install pre-commit 6 | # to run locally 7 | # Config reference: https://pre-commit.com/#pre-commit-configyaml---top-level 8 | # To run on a pr, add a comment with the text "pre-commit.ci run" 9 | # Common tasks 10 | # 11 | # - Run on all files: pre-commit run --all-files 12 | # - Register git hooks: pre-commit install --install-hooks 13 | 14 | repos: 15 | # Misc commit checks 16 | - repo: https://github.com/pre-commit/pre-commit-hooks 17 | rev: v5.0.0 18 | # ref: https://github.com/pre-commit/pre-commit-hooks#hooks-available 19 | hooks: 20 | # Autoformat: Makes sure files end in a newline and only a newline. 21 | - id: end-of-file-fixer 22 | # Lint: Check for files with names that would conflict on a 23 | # case-insensitive filesystem like MacOS HFS+ or Windows FAT. 24 | - id: check-case-conflict 25 | - id: trailing-whitespace 26 | 27 | - repo: https://github.com/codespell-project/codespell 28 | rev: v2.4.1 29 | hooks: 30 | - id: codespell 31 | additional_dependencies: 32 | - tomli 33 | exclude: > 34 | (?x)^( 35 | (.*vale-styles.*)|(.*\.po) 36 | )$ 37 | 38 | - repo: https://github.com/errata-ai/vale 39 | rev: v3.11.2 40 | hooks: 41 | - id: vale 42 | 43 | - repo: https://github.com/rbubley/mirrors-prettier 44 | rev: v3.5.3 45 | hooks: 46 | - id: prettier 47 | types_or: [yaml, html, css, scss, javascript, json, toml] 48 | 49 | ci: 50 | autofix_prs: false 51 | #skip: [flake8, end-of-file-fixer] 52 | autofix_commit_msg: | 53 | '[pre-commit.ci 🤖] Apply code format tools to PR' 54 | # Update hook versions every month (so we don't get hit with weekly update pr's) 55 | autoupdate_schedule: monthly 56 | -------------------------------------------------------------------------------- /.vale.ini: -------------------------------------------------------------------------------- 1 | # Configuration file for using Vale in the python-package-guide repository 2 | # 3 | # To disable checks on parts of a MarkDown or HTML file, delimit the section 4 | # using these HTML comments: 5 | # to disabled Vale checks after this line: 6 | # to enable Vale checks after this line: 7 | # 8 | # To disable checks based on MarkDown scope, see IgnoredScopes. 9 | # To disable checks on certain HTML elements, see IgnoredClasses. 10 | # 11 | # More information about the configuration can be found here: 12 | # https://vale.sh/docs/topics/config 13 | 14 | 15 | # Path to the styles directory, where style rules are defined 16 | StylesPath = vale-styles 17 | 18 | # Path to a dictionary folders inside the StylesPath config subdirectory. This 19 | # folder can contain two files, accept.txt and reject.txt, with one word per 20 | # line. These words will be used to check for spelling mistakes in addition to 21 | # the internal dictionary, if the 'Vale' ruleset is enabled (see below) 22 | # See https://vale.sh/docs/topics/vocab/#folder-structure for more details 23 | Vocab = sample 24 | 25 | 26 | # Checks are defined in sections by file type, like the one below for 27 | # MarkDown. In each section you can enable groups of style rules, defined in folders 28 | # inside the StylesPath directory. 29 | # Use 'Vale' to enable the internal style rules and checks. 30 | 31 | [*.md] 32 | BasedOnStyles = package-guide-test 33 | -------------------------------------------------------------------------------- /.zenodo.json: -------------------------------------------------------------------------------- 1 | { 2 | "creators": [ 3 | { 4 | "affiliation": "pyOpenSci", 5 | "name": "Wasser, Leah", 6 | "orcid": "0000-0002-8177-6550" 7 | }, 8 | { 9 | "affiliation": "", 10 | "name": "Nicholson, David", 11 | "orcid": "0000-0002-4261-4719" 12 | }, 13 | { 14 | "affiliation": "", 15 | "name": "Moreno, Felipe" 16 | }, 17 | { 18 | "affiliation": "Hasso Plattner Institute", 19 | "name": "Sasso, Ariane", 20 | "orcid": "0000-0002-3669-4599" 21 | }, 22 | { 23 | "affiliation": "", 24 | "name": "Batisse, Alexandre", 25 | "orcid": "0000-0001-7912-3422" 26 | }, 27 | { 28 | "affiliation": "", 29 | "name": "Molinsky, Simon" 30 | }, 31 | { 32 | "affiliation": "", 33 | "name": "Marmo, Chiara", 34 | "orcid": "0000-0003-2843-6044 " 35 | }, 36 | { 37 | "affiliation": "", 38 | "name": "Fernandes, Filipe" 39 | }, 40 | { 41 | "affiliation": "", 42 | "name": "Saunders, Jonny" 43 | }, 44 | { 45 | "affiliation": "Willing Consulting", 46 | "name": "Willing, Carol", 47 | "orcid": "0000-0002-9817-8485" 48 | }, 49 | { 50 | "affiliation": "Ouranos", 51 | "name": "Smith, Trevor James", 52 | "orcid": "0000-0001-5393-8359" 53 | }, 54 | { 55 | "affiliation": "Ansys", 56 | "name": "Pastor Muela, Roberto" 57 | }, 58 | { 59 | "affiliation": "pyOpenSci", 60 | "name": "Mostipak, Jesse", 61 | "orcid": "0000-0003-1915-9612" 62 | }, 63 | { 64 | "affiliation": "", 65 | "name": "Paige, Jeremy" 66 | }, 67 | { 68 | "affiliation": "", 69 | "name": "Murphy, Nick" 70 | }, 71 | { 72 | "affiliation": "", 73 | "name": "Broderick, Billy", 74 | "orcid": "0000-0002-8999-9003" 75 | }, 76 | { 77 | "affiliation": "Posit, PBC", 78 | "name": "Zimmerman, Isabel", 79 | "orcid": "0009-0007-1803-9391" 80 | }, 81 | { 82 | "affiliation": "", 83 | "name": "Beber, Moritz E." 84 | }, 85 | { 86 | "affiliation": "NVIDIA", 87 | "name": "Welch, Erik", 88 | "orcid": "0000-0003-3694-3783" 89 | }, 90 | { 91 | "affiliation": "Alaska Satellite Facility, University of Alaska Fairbanks", 92 | "name": "Kennedy, Joseph H.", 93 | "orcid": "0000-0002-9348-693X" 94 | }, 95 | { 96 | "affiliation": "Ansys", 97 | "name": "Venugopal, Revathy" 98 | }, 99 | { 100 | "affiliation": "", 101 | "name": "Tetsuo, Koyama", 102 | "orcid": "0000-0001-9859-9565" 103 | } 104 | ], 105 | "contributors": [ 106 | { 107 | "type": "Other", 108 | "affiliation": "Quansight", 109 | "name": "Pamphile T., Roy", 110 | "orcid": "0000-0001-9816-1416" 111 | }, 112 | { 113 | "type": "Other", 114 | "affiliation": "", 115 | "name": "Döring, Randy" 116 | }, 117 | { 118 | "type": "Other", 119 | "affiliation": "", 120 | "name": "Cano Rodríguez, Juan Luis", 121 | "orcid": "0000-0002-2187-161X" 122 | }, 123 | { 124 | "type": "Other", 125 | "affiliation": "Princeton University", 126 | "name": "Schreiner, Henry", 127 | "orcid": "0000-0002-7833-783X" 128 | }, 129 | { 130 | "type": "Other", 131 | "affiliation": "", 132 | "name": "Van der Walt, Stéfan" 133 | }, 134 | { 135 | "type": "Other", 136 | "affiliation": "Quansight", 137 | "name": "Gommers, Ralf", 138 | "orcid": "0000-0002-0300-3333" 139 | }, 140 | { 141 | "type": "Other", 142 | "affiliation": "", 143 | "name": "Gedam, Pradyun" 144 | }, 145 | { 146 | "type": "Other", 147 | "affiliation": "", 148 | "name": "Lef, Ofek" 149 | }, 150 | { 151 | "type": "Other", 152 | "affiliation": "", 153 | "name": "Tocknell, James" 154 | }, 155 | { 156 | "type": "Other", 157 | "affiliation": "", 158 | "name": "Ming, Frost" 159 | }, 160 | { 161 | "type": "Other", 162 | "affiliation": "", 163 | "name": "van Kemenade, Hugo", 164 | "orcid": "0000-0001-5715-8632" 165 | }, 166 | { 167 | "type": "Other", 168 | "affiliation": "", 169 | "name": "Hall, Matt" 170 | }, 171 | { 172 | "type": "Other", 173 | "affiliation": "", 174 | "name": "Leidel, Jannis" 175 | }, 176 | { 177 | "type": "Other", 178 | "affiliation": "", 179 | "name": "Hirschfeld, Dave" 180 | }, 181 | { 182 | "type": "Other", 183 | "affiliation": "", 184 | "name": "Bravalheri, Anderson" 185 | }, 186 | { 187 | "type": "Other", 188 | "affiliation": "", 189 | "name": "Possenriede, Daniel" 190 | }, 191 | { 192 | "type": "Other", 193 | "affiliation": "", 194 | "name": "Yang, Ruoxi" 195 | }, 196 | { 197 | "type": "Other", 198 | "affiliation": "", 199 | "name": "Éric" 200 | }, 201 | { 202 | "type": "Other", 203 | "affiliation": "", 204 | "name": "Cranston, Karen" 205 | }, 206 | { 207 | "type": "Other", 208 | "affiliation": "", 209 | "name": "Pawson, Inessa" 210 | }, 211 | { 212 | "type": "Other", 213 | "affiliation": "", 214 | "name": "Knorps, Maria" 215 | }, 216 | { 217 | "type": "Other", 218 | "affiliation": "", 219 | "name": "Schwartz, Eli" 220 | }, 221 | { 222 | "type": "Other", 223 | "affiliation": "", 224 | "name": "Burns, Jackson" 225 | }, 226 | { 227 | "type": "Other", 228 | "affiliation": "", 229 | "name": "Jaimergp" 230 | }, 231 | { 232 | "type": "Other", 233 | "affiliation": "", 234 | "name": "h-vetinari" 235 | }, 236 | { 237 | "type": "Other", 238 | "affiliation": "", 239 | "name": "Ogasawara, Ivan" 240 | }, 241 | { 242 | "type": "Other", 243 | "affiliation": "", 244 | "name": "Russell, Tom" 245 | }, 246 | { 247 | "type": "Other", 248 | "affiliation": "", 249 | "name": "A., Philipp" 250 | } 251 | ], 252 | "keywords": [ 253 | "open source software", 254 | "Python", 255 | "open peer review", 256 | "data science", 257 | "open reproducible science", 258 | "science" 259 | ], 260 | "license": "CC-BY-SA-4.0", 261 | "upload_type": "publication", 262 | "publication_type": "book" 263 | } 264 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | [pyOpenSci Community Code of Conduct](https://github.com/pyOpenSci/handbook/blob/main/CODE_OF_CONDUCT.md) 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | All content in this book (ie, any files and content in the `content/` folder) 2 | is licensed under the 3 | [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/) (CC BY-SA 4.0) license. 4 | -------------------------------------------------------------------------------- /_ext/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_ext/__init__.py -------------------------------------------------------------------------------- /_ext/rss.py: -------------------------------------------------------------------------------- 1 | """ 2 | Create an RSS feed of tutorials 3 | 4 | Cribbed from: https://github.com/python/peps/blob/main/pep_sphinx_extensions/generate_rss.py 5 | """ 6 | 7 | from dataclasses import dataclass, asdict 8 | from datetime import datetime, UTC 9 | from email.utils import format_datetime 10 | from html import escape 11 | from pprint import pformat 12 | from typing import TYPE_CHECKING 13 | from urllib.parse import urljoin 14 | 15 | if TYPE_CHECKING: 16 | from sphinx.application import Sphinx 17 | 18 | 19 | def _format_rfc_2822(dt: datetime) -> str: 20 | datetime = dt.replace(tzinfo=UTC) 21 | return format_datetime(datetime, usegmt=True) 22 | 23 | 24 | @dataclass 25 | class RSSItem: 26 | title: str 27 | date: datetime 28 | description: str 29 | url: str 30 | author: str = "pyOpenSci" 31 | 32 | @classmethod 33 | def from_meta(cls, page_name: str, meta: dict, app: "Sphinx") -> "RSSItem": 34 | """Create from a page's metadata""" 35 | url = urljoin(app.config.html_baseurl, app.builder.get_target_uri(page_name)) 36 | # purposely don't use `get` here because we want to error if these fields are absent 37 | return RSSItem( 38 | title=meta[":og:title"], 39 | description=meta[":og:description"], 40 | date=datetime.fromisoformat(meta["date"]), 41 | author=meta.get(":og:author", "pyOpenSci"), 42 | url=url, 43 | ) 44 | 45 | def render(self) -> str: 46 | return f"""\ 47 | 48 | {escape(self.title, quote=False)} 49 | {escape(self.url, quote=False)} 50 | {escape(self.description, quote=False)} 51 | {escape(self.author, quote=False)} 52 | {self.url} 53 | {_format_rfc_2822(self.date)} 54 | """ 55 | 56 | 57 | @dataclass 58 | class RSSFeed: 59 | items: list[RSSItem] 60 | last_build_date: datetime = datetime.now() 61 | title: str = "pyOpenSci Tutorials" 62 | link: str = "https://www.pyopensci.org/python-package-guide/tutorials/intro.html" 63 | self_link: str = "https://www.pyopensci.org/python-package-guide/tutorials.rss" 64 | description: str = "Tutorials for learning python i guess!!!" 65 | language: str = "en" 66 | 67 | def render(self) -> str: 68 | items = sorted(self.items, key=lambda i: i.date, reverse=True) 69 | items = "\n".join([item.render() for item in items]) 70 | return f"""\ 71 | 72 | 73 | 74 | {self.title} 75 | {self.link} 76 | 77 | {self.description} 78 | {self.language} 79 | {_format_rfc_2822(self.last_build_date)} 80 | {items} 81 | 82 | 83 | """ 84 | 85 | 86 | def generate_tutorials_feed(app: "Sphinx"): 87 | from sphinx.util import logging 88 | 89 | logger = logging.getLogger("_ext.rss") 90 | logger.info("Generating RSS feed for tutorials") 91 | metadata = app.builder.env.metadata 92 | tutorials = [t for t in metadata if t.startswith("tutorials/")] 93 | feed_items = [RSSItem.from_meta(t, metadata[t], app) for t in tutorials] 94 | feed = RSSFeed(items=feed_items) 95 | with open(app.outdir / "tutorials.rss", "w") as f: 96 | f.write(feed.render()) 97 | 98 | logger.info( 99 | f"Generated RSS feed for tutorials, wrote to {app.outdir / 'tutorials.rss'}" 100 | ) 101 | logger.debug(f"feed items: \n{pformat([asdict(item) for item in feed_items])}") 102 | -------------------------------------------------------------------------------- /_static/fonts/itim-v14-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/fonts/itim-v14-latin-regular.woff2 -------------------------------------------------------------------------------- /_static/fonts/poppins-v20-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/fonts/poppins-v20-latin-500.woff2 -------------------------------------------------------------------------------- /_static/fonts/poppins-v20-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/fonts/poppins-v20-latin-600.woff2 -------------------------------------------------------------------------------- /_static/fonts/poppins-v20-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/fonts/poppins-v20-latin-700.woff2 -------------------------------------------------------------------------------- /_static/fonts/poppins-v20-latin-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/fonts/poppins-v20-latin-700italic.woff2 -------------------------------------------------------------------------------- /_static/fonts/poppins-v20-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/fonts/poppins-v20-latin-italic.woff2 -------------------------------------------------------------------------------- /_static/fonts/poppins-v20-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/fonts/poppins-v20-latin-regular.woff2 -------------------------------------------------------------------------------- /_static/language_select.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | let selectors = document.querySelectorAll("#language-selector"); 3 | selectors.forEach((selector) => { 4 | selector.addEventListener("change", (event) => { 5 | let target = event.target.value; 6 | if (target.startsWith("https")) { 7 | window.location.href = target; 8 | } else { 9 | window.location.pathname = target; 10 | } 11 | }); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /_static/logo-dark-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/logo-dark-mode.png -------------------------------------------------------------------------------- /_static/logo-light-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/logo-light-mode.png -------------------------------------------------------------------------------- /_static/matomo.js: -------------------------------------------------------------------------------- 1 | // Matomo analytics 2 | var _paq = (window._paq = window._paq || []); 3 | /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ 4 | _paq.push(["trackPageView"]); 5 | _paq.push(["enableLinkTracking"]); 6 | (function () { 7 | var u = "https://pyopensci.matomo.cloud/"; 8 | _paq.push(["setTrackerUrl", u + "matomo.php"]); 9 | _paq.push(["setSiteId", "2"]); 10 | var d = document, 11 | g = d.createElement("script"), 12 | s = d.getElementsByTagName("script")[0]; 13 | g.async = true; 14 | g.src = "//cdn.matomo.cloud/pyopensci.matomo.cloud/matomo.js"; 15 | s.parentNode.insertBefore(g, s); 16 | })(); 17 | -------------------------------------------------------------------------------- /_static/pyopensci-logo-package-guide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/pyopensci-logo-package-guide.png -------------------------------------------------------------------------------- /_static/pyopensci-python-package-guide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/_static/pyopensci-python-package-guide.png -------------------------------------------------------------------------------- /_static/translation_stats.json: -------------------------------------------------------------------------------- 1 | { 2 | "es": { 3 | "continuous-integration": { 4 | "total": 38, 5 | "translated": 0, 6 | "fuzzy": 0, 7 | "untranslated": 38, 8 | "percentage": 0.0 9 | }, 10 | "CONTRIBUTING": { 11 | "total": 124, 12 | "translated": 0, 13 | "fuzzy": 0, 14 | "untranslated": 124, 15 | "percentage": 0.0 16 | }, 17 | "documentation": { 18 | "total": 9, 19 | "translated": 8, 20 | "fuzzy": 1, 21 | "untranslated": 0, 22 | "percentage": 88.89 23 | }, 24 | "index": { 25 | "total": 14, 26 | "translated": 11, 27 | "fuzzy": 1, 28 | "untranslated": 2, 29 | "percentage": 78.57 30 | }, 31 | "package-structure-code": { 32 | "total": 131, 33 | "translated": 126, 34 | "fuzzy": 1, 35 | "untranslated": 4, 36 | "percentage": 96.18 37 | }, 38 | "tests": { 39 | "total": 1, 40 | "translated": 0, 41 | "fuzzy": 1, 42 | "untranslated": 0, 43 | "percentage": 0.0 44 | }, 45 | "TRANSLATING": { 46 | "total": 108, 47 | "translated": 0, 48 | "fuzzy": 0, 49 | "untranslated": 108, 50 | "percentage": 0.0 51 | }, 52 | "tutorials": { 53 | "total": 12, 54 | "translated": 11, 55 | "fuzzy": 1, 56 | "untranslated": 0, 57 | "percentage": 91.67 58 | } 59 | }, 60 | "ja": { 61 | "continuous-integration": { 62 | "total": 38, 63 | "translated": 38, 64 | "fuzzy": 0, 65 | "untranslated": 0, 66 | "percentage": 100.0 67 | }, 68 | "CONTRIBUTING": { 69 | "total": 124, 70 | "translated": 124, 71 | "fuzzy": 0, 72 | "untranslated": 0, 73 | "percentage": 100.0 74 | }, 75 | "documentation": { 76 | "total": 468, 77 | "translated": 467, 78 | "fuzzy": 1, 79 | "untranslated": 0, 80 | "percentage": 99.79 81 | }, 82 | "index": { 83 | "total": 14, 84 | "translated": 11, 85 | "fuzzy": 1, 86 | "untranslated": 2, 87 | "percentage": 78.57 88 | }, 89 | "package-structure-code": { 90 | "total": 87, 91 | "translated": 86, 92 | "fuzzy": 1, 93 | "untranslated": 0, 94 | "percentage": 98.85 95 | }, 96 | "tests": { 97 | "total": 1, 98 | "translated": 0, 99 | "fuzzy": 1, 100 | "untranslated": 0, 101 | "percentage": 0.0 102 | }, 103 | "TRANSLATING": { 104 | "total": 25, 105 | "translated": 24, 106 | "fuzzy": 1, 107 | "untranslated": 0, 108 | "percentage": 96.0 109 | }, 110 | "tutorials": { 111 | "total": 12, 112 | "translated": 11, 113 | "fuzzy": 1, 114 | "untranslated": 0, 115 | "percentage": 91.67 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /_templates/code_of_conduct.html: -------------------------------------------------------------------------------- 1 |

2 | pyOpensci is dedicated to creating a welcoming, supportive and diverse 3 | community around the open source Python tools that drive open science. Our 4 | Code of Conduct 9 | defines expected behavior and guidelines that help create such a community. 10 |

11 | -------------------------------------------------------------------------------- /_templates/language-selector.html: -------------------------------------------------------------------------------- 1 | {%- macro langlink(lang, selected=False) -%} {%- if lang == "en" %} 2 | 13 | {%- else %} 14 | 25 | {%- endif -%} {%- endmacro -%} 26 | 31 | -------------------------------------------------------------------------------- /bibliography.bib: -------------------------------------------------------------------------------- 1 | @article{hunterReclaimingComputingCommons2016, 2 | title = {Reclaiming the {{Computing Commons}}}, 3 | author = {Hunter, Rob}, 4 | year = {2016}, 5 | month = may, 6 | journal = {Jacobin}, 7 | url = {https://jacobin.com/2016/02/free-software-movement-richard-stallman-linux-open-source-enclosure/}, 8 | urldate = {2023-03-09}, 9 | abstract = {Resisting the commodification of information is a political struggle, not a technical one.}, 10 | archive = {https://web.archive.org/web/20230309005744/https://jacobin.com/2016/02/free-software-movement-richard-stallman-linux-open-source-enclosure/}, 11 | langid = {american}, 12 | keywords = {foss culture,copyleft} 13 | } 14 | 15 | @misc{gnuprojectWhatFreeSoftware2019, 16 | title = {What Is {{Free Software}}?}, 17 | author = {{GNU Project}}, 18 | year = {2019}, 19 | month = jul, 20 | journal = {Free Software Foundation}, 21 | url = {https://www.gnu.org/philosophy/free-sw.html}, 22 | urldate = {2024-03-01} 23 | } 24 | 25 | @misc{gnuprojectWhatCopyleft2022, 26 | title = {What Is {{Copyleft}}?}, 27 | author = {{GNU Project}}, 28 | year = {2022}, 29 | month = jan, 30 | journal = {Free Software Foundation}, 31 | url = {https://www.gnu.org/copyleft/}, 32 | urldate = {2024-03-01} 33 | } 34 | 35 | @misc{creativecommonsShareAlikeCompatibilityGPLv32015, 36 | title = {{{ShareAlike}} Compatibility: {{GPLv3}}}, 37 | author = {{Creative Commons}}, 38 | year = {2015}, 39 | month = sep, 40 | journal = {Creative Commons Wiki}, 41 | url = {https://wiki.creativecommons.org/wiki/ShareAlike_compatibility:_GPLv3}, 42 | urldate = {2024-03-02} 43 | } 44 | -------------------------------------------------------------------------------- /codespell-ignore.txt: -------------------------------------------------------------------------------- 1 | aways 2 | Soler 3 | -------------------------------------------------------------------------------- /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 | import sys 15 | sys.path.insert(0, os.path.abspath('.')) 16 | from datetime import datetime 17 | import subprocess 18 | import os 19 | from typing import TYPE_CHECKING 20 | from _ext import rss 21 | 22 | if TYPE_CHECKING: 23 | from sphinx.application import Sphinx 24 | 25 | current_year = datetime.now().year 26 | organization_name = "pyOpenSci" 27 | 28 | # env vars 29 | sphinx_env = os.environ.get("SPHINX_ENV", "development") 30 | language_env = os.environ.get("SPHINX_LANG", "en") 31 | 32 | 33 | # -- Project information ----------------------------------------------------- 34 | 35 | project = "pyOpenSci Python Package Guide" 36 | copyright = f"{current_year}, {organization_name}" 37 | author = "pyOpenSci Community" 38 | 39 | # Language of the current build 40 | # language can later be overridden (eg with the -D flag) 41 | # but we need it set here so it can make it into the html_context 42 | language = language_env 43 | # all languages that have .po files generated for them 44 | # (excluding english) 45 | languages = ["es", "ja"] 46 | # the languages that will be included in a production build 47 | # (also excluding english) 48 | release_languages = ["ja"] 49 | 50 | # languages that will be included in the language dropdown 51 | # (ie. all that are being built in this nox build session) 52 | if sphinx_env == "production": 53 | build_languages = ["en"] + release_languages 54 | else: 55 | build_languages = ["en"] + languages 56 | 57 | # Get the latest Git tag - there might be a prettier way to do this but... 58 | try: 59 | release_value = ( 60 | subprocess.check_output(["git", "describe", "--tags"]) 61 | .decode("utf-8") 62 | .strip() 63 | ) 64 | release_value = release_value[:4] 65 | except subprocess.CalledProcessError: 66 | release_value = "0.1" # Default value in case there's no tag 67 | 68 | # Update the release value 69 | release = release_value 70 | 71 | # -- General configuration --------------------------------------------------- 72 | 73 | # Add any Sphinx extension module names here, as strings. They can be 74 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 75 | # ones. 76 | extensions = [ 77 | "myst_nb", 78 | "sphinx_design", 79 | "sphinx_copybutton", 80 | "sphinx.ext.intersphinx", 81 | "sphinx.ext.todo", 82 | "sphinx_sitemap", 83 | "sphinxext.opengraph", 84 | "sphinx_favicon", 85 | "sphinxcontrib.bibtex", 86 | ] 87 | 88 | # colon fence for card support in md 89 | myst_enable_extensions = [ 90 | "colon_fence", 91 | "deflist", 92 | "attrs_block", 93 | ] 94 | myst_heading_anchors = 3 95 | myst_footnote_transition = False 96 | 97 | # Sphinx_favicon is used now in favor of built in support 98 | # https://pypi.org/project/sphinx-favicon/ 99 | favicons = [ 100 | {"href": "https://www.pyopensci.org/images/favicon.ico"}, 101 | ] 102 | 103 | html_baseurl = "https://www.pyopensci.org/python-package-guide/" 104 | lang_selector_baseurl = "/python-package-guide/" 105 | if not sphinx_env == "production": 106 | # for links in language selector when developing locally 107 | lang_selector_baseurl = "/" 108 | 109 | html_theme_options = { 110 | "announcement": "

We run peer review of scientific Python software. Learn more.

", 111 | # "navbar_center": ["nav"], this can be a way to override the default navigation structure 112 | "external_links": [ 113 | { 114 | "url": "https://www.pyopensci.org", 115 | "name": "pyOpenSci Website", 116 | }, 117 | { 118 | "url": "https://www.pyopensci.org/software-peer-review", 119 | "name": "Peer Review Guide", 120 | }, 121 | { 122 | "url": "https://pyopensci.org/handbook", 123 | "name": "Handbook", 124 | }, 125 | ], 126 | "icon_links": [ 127 | { 128 | "name": "Mastodon", 129 | "url": "https://fosstodon.org/@pyOpenSci", 130 | "icon": "fa-brands fa-mastodon", 131 | }, 132 | ], 133 | "logo": { 134 | # "text": "Python Packaging", 135 | "image_dark": "logo-dark-mode.png", 136 | "image_light": "logo-light-mode.png", 137 | "alt_text": "pyOpenSci Python Package Guide. The pyOpenSci logo is a purple flower with pyOpenSci under it. The o in open sci is the center of the flower", 138 | }, 139 | "header_links_before_dropdown": 5, 140 | "use_edit_page_button": True, 141 | "show_nav_level": 2, 142 | "navigation_depth": 3, 143 | "show_toc_level": 1, 144 | # "navbar_align": "left", # [left, content, right] For testing that the navbar items align properly 145 | "github_url": "https://github.com/pyopensci/python-package-guide", 146 | "footer_start": ["code_of_conduct", "copyright"], 147 | "footer_end": [], 148 | "navbar_persistent": ["language-selector", "search-button"] 149 | } 150 | 151 | html_context = { 152 | "github_user": "pyopensci", 153 | "github_repo": "python-package-guide", 154 | "github_version": "main", 155 | "language": language, 156 | "languages": build_languages, 157 | "baseurl": lang_selector_baseurl, 158 | } 159 | 160 | # Add any paths that contain templates here, relative to this directory. 161 | templates_path = ["_templates"] 162 | 163 | # List of patterns, relative to source directory, that match files and 164 | # directories to ignore when looking for source files. 165 | # This pattern also affects html_static_path and html_extra_path. 166 | exclude_patterns = [ 167 | "_build", 168 | "Thumbs.db", 169 | ".DS_Store", 170 | ".github", 171 | ".nox", 172 | "README.md", 173 | "styles/write-good/README.md", 174 | "styles/*", 175 | ".pytest_cache/README.md", 176 | "vale-styles/*", 177 | "CODE_OF_CONDUCT.md", 178 | ] 179 | 180 | # For sitemap generation 181 | 182 | sitemap_url_scheme = "{link}" 183 | 184 | # -- Options for HTML output ------------------------------------------------- 185 | 186 | # The theme to use for HTML and HTML Help pages. See the documentation for 187 | # a list of builtin themes. 188 | # 189 | html_theme = "pydata_sphinx_theme" 190 | html_static_path = ["_static"] 191 | html_css_files = ["pyos.css"] 192 | html_title = "Python Packaging Guide" 193 | html_js_files = ["matomo.js", "language_select.js"] 194 | 195 | 196 | # Social cards 197 | ogp_site_url = "https://www.pyopensci.org/python-package-guide/" 198 | ogp_social_cards = { 199 | "line_color": "#6D597A", 200 | "image": "_static/pyopensci-logo-package-guide.png", 201 | } 202 | 203 | # Bibliographies 204 | bibtex_bibfiles = ["bibliography.bib"] 205 | # myst complains about bibtex footnotes because of render order 206 | suppress_warnings = ["myst.footnote"] 207 | 208 | 209 | def _post_build(app: "Sphinx", exception: Exception | None) -> None: 210 | rss.generate_tutorials_feed(app) 211 | 212 | 213 | def setup(app: "Sphinx"): 214 | app.connect("build-finished", _post_build) 215 | 216 | # Parallel safety: https://www.sphinx-doc.org/en/master/extdev/index.html#extension-metadata 217 | return {"parallel_read_safe": True, "parallel_write_safe": True} 218 | -------------------------------------------------------------------------------- /continuous-integration/ci.md: -------------------------------------------------------------------------------- 1 | (ci-cd)= 2 | # Continuous Integration and Continuous Deployment (CI/CD) For Python Packages 3 | 4 | When you develop, work on, and contribute to software, there is more to consider than 5 | just writing code. Having tests and checks ensures that your code 6 | runs reliably and follows a consistent format is also important. You can use 7 | **Continuous Integration (CI)** and **Continuous 8 | Deployment (CD)** to run tests and checks on your code every time someone suggests a change online 9 | in a platform like GitHub or GitLab. 10 | 11 | - **Continuous Integration (CI):** Automates the process of running tests, 12 | code checks, and other workflows each time code is updated. 13 | - **Continuous Deployment (CD):** Extends CI by allowing you to automate publishing your package to PyPI, publishing your documentation, and more. 14 | 15 | CI and CD streamline software development by automating repetitive 16 | tasks and ensuring code quality and consistency. Having CI setup also makes it easier for new contributors 17 | to contribute to your code base without setting up all your test suites and 18 | other local checks. 19 | 20 | ## What is continuous integration? 21 | 22 | When you’re ready to publish your code online, you can set up Continuous Integration (CI). CI is a platform that allows you to specify and run jobs or workflows you define. 23 | These workflows include: 24 | 25 | - Running your test suite 26 | - Running code checkers / linters / spellcheck 27 | - Building your documentation 28 | 29 | CI allows you to automate running workflows across a suite of environments, including: 30 | 31 | - environments containing different Python versions and 32 | - different operating systems (Mac, Linux, Windows). 33 | 34 | ## What is Continuous Deployment (CD)? 35 | 36 | Continuous deployment (CD) extends the CI process by automating the deployment of code changes to production or staging environments. In the case of your open source tool, CD can be used to: 37 | 38 | - Automate publishing to PyPI 39 | - Automate publishing your documentation to GitHub Pages or Read the Docs. 40 | 41 | It is also used once your conda-forge recipe is set up to keep your package up to date on conda-forge. 42 | 43 | ### Why use CI 44 | 45 | CI can be configured to run a workflow on every commit pushed to GitHub and every pull request opened. This ensures that any changes made to your package are tested across environments before merging into the main branch of your code. 46 | 47 | These checks are particularly useful if someone new is contributing to your code. Every contributor's change will be tested when pushed to your code repository. 48 | 49 | Together, CI and CD streamline the process of building, testing, and deploying code. They aim to improve software development and publication efficiency, quality, and reliability. 50 | 51 | ```{note} 52 | All pyOpenSci packages must use some form of continuous integration. Even if you are not planning to go through peer review, we strongly recommend that you use continuous integration, too! 53 | ``` 54 | 55 | In the case of GitHub actions (which we will focus on here), CI workflows are running on online servers that support GitHub. 56 | 57 | ## CI / CD platforms 58 | 59 | There are numerous platforms available for CI/CD. Here, we will focus on GitHub Actions (GHA), built into GitHub. GitHub is the most commonly used platform to store scientific open-source software. 60 | 61 | :::{note} 62 | If you use [GitLab](https://about.gitlab.com/) CI/CD, many of the principles described here will apply. However, the workflow files may look different. 63 | ::: 64 | 65 | ### If you aren't sure, use GitHub Actions 66 | 67 | While you are welcome to use the continuous integration platform of your choice, 68 | we recommend GitHub Actions because it is free-to-use and integrated tightly 69 | into the GitHub user interface. There is also an entire store of GitHub action 70 | templates that you can easily use and adapt to your own needs. 71 | 72 | :::{admonition} Other platforms that you may run into 73 | :class: info 74 | 75 | - [Appveyor:](https://www.appveyor.com/): Supports running tests on Windows operating systems and predated the release of GitHub Actions. Today, AppVeyor supports operating systems beyond Windows. 76 | - [Travis CI:](https://www.travis-ci.com/) had been a common CI platform choice in our ecosystem. Usage dropped after Travis CI ended free support for open-source projects. 77 | - [CircleCI:](https://circleci.com/) CircleCI can be useful for automated builds of websites and documentation since it offers a preview of the PR changes. 78 | ::: 79 | 80 | ## Embrace automation 81 | 82 | By embracing CI/CD, you can ensure that your code runs as you expect it to across the diverse landscapes of user environments. Further, you can 83 | automate certain checks (and, in some cases, code fixes), including linting and code style. You can even automate spell-checking your documentation 84 | and docstrings! 85 | -------------------------------------------------------------------------------- /continuous-integration/index.md: -------------------------------------------------------------------------------- 1 | (ci-cd-intro)= 2 | # Continuous Integration (CI) and Continuous Deployment (CD) for your Python package 3 | 4 | 5 | :::{toctree} 6 | :caption: Continuous Integration 7 | 8 | 9 | What is CI? 10 | ::: 11 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | project_id: 785976 2 | api_token: "${{ secrets.CROWDIN_PERSONAL_TOKEN }}" 3 | files: 4 | - source: /locales/en/LC_MESSAGES/messages.po 5 | translation: /locales/%two_letters_code%/LC_MESSAGES/messages.po 6 | type: gettext 7 | -------------------------------------------------------------------------------- /documentation/hosting-tools/intro.md: -------------------------------------------------------------------------------- 1 | # Tools to Build and Host your Documentation 2 | 3 | The most common tool for building documentation in the Python 4 | ecosystem currently is Sphinx. However, some maintainers 5 | are using tools like [mkdocs](https://www.mkdocs.org/) for documentation. It is 6 | up to you to use the platform that you prefer for your documentation! 7 | 8 | In this section, we introduce Sphinx as a common tool to 9 | build documentation. We talk about various syntax options that you can use 10 | when writing Sphinx documentation including mySt and rST. 11 | 12 | We also talk about ways to publish your 13 | documentation online and Sphinx tools that might help you optimize 14 | your documentation website. 15 | -------------------------------------------------------------------------------- /documentation/hosting-tools/myst-markdown-rst-doc-syntax.md: -------------------------------------------------------------------------------- 1 | # Documentation syntax: markdown vs. myST vs. rst syntax to create your docs 2 | 3 | There are three commonly used syntaxes for creating Python documentation: 4 | 1. [markdown](https://www.markdownguide.org/): Markdown is an easy-to-learn text 5 | syntax. It is the default syntax used in Jupyter Notebooks. There are tools that you can add to a Sphinx website that allow it to render markdown as html. However, using markdown to write documentation has limitations. For instance if you want to add references, 6 | colored call out blocks and other custom elements to your documentation, you will 7 | need to use either **myST** or **rST**. 8 | 1. [rST (ReStructured Text):](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html). **rST** is the native syntax that sphinx supports. rST was the default syntax used for documentation for many years. However, in recent years myST has risen to the top as a favorite for documentation given the flexibility that it allows. 9 | 1. [myST:](https://myst-parser.readthedocs.io/en/latest/intro.html) myST is a combination of `markdown` and `rST` syntax. It is a nice option if you are comfortable writing markdown. `myst` is preferred by many because it offers both the rich functionality 10 | of rST combined with a simple-to-write markdown syntax. 11 | 12 | While you can chose to use any of the syntaxes listed above, we suggest using 13 | `myST` because: 14 | 15 | * It is a simpler syntax and thus easier to learn; 16 | * The above simplicity will make it easier for more people to contribute to your documentation. 17 | * Most of your core Python package text files, such as your README.md file, are already in `.md` format 18 | * `GitHub` and `Jupyter Notebooks` support markdown thus it's more widely used in the scientific ecosystem. 19 | 20 | 21 | ```{tip} 22 | If you are on the fence about myST vs rst, you might find that **myST** is easier 23 | for more people to contribute to. 24 | ``` 25 | 26 | 30 | -------------------------------------------------------------------------------- /documentation/hosting-tools/publish-documentation-online.md: -------------------------------------------------------------------------------- 1 | # How to publish your Python package documentation online 2 | 3 | We suggest that you setup a hosting service for your Python package 4 | documentation. Two free and commonly used ways to 5 | quickly create a documentation website hosting environment are below. 6 | 7 | 1. You can host your documentation yourself using [GitHub Pages](https://pages.github.com/) or another online hosting service. 8 | 1. You can host your documentation using [Read the Docs](https://readthedocs.org/). 9 | 10 | ## What is Read the Docs ? 11 | [Read the Docs](https://readthedocs.org/) is a documentation hosting service that supports publishing your project's documentation. 12 | 13 | Read the Docs is a fully featured, free, documentation hosting 14 | service. Some of its many features include: 15 | 16 | * Is free to host your documentation (but there are also paid tiers if you wish to customize hosting) 17 | * Automates building your documentation 18 | * Allows you to turn on integration with pull requests where you can view documentation build progress (success vs failure). 19 | * Supports versioning of your documentation which allows users to refer to older tagged versions of the docs if they are using older versions of your package. 20 | * Supports downloading of documentation in PDF and other formats. 21 | * You can customize the documentation build using a **.readthedocs.yaml** file in your GitHub repository. 22 | 23 | 24 | ## What is GitHub Pages? 25 | [GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages) is a free web 26 | hosting service offered by GitHub. Using GitHub pages, you can build your 27 | documentation locally or using a Continuous Integration setup, and then push 28 | to a branch in your GitHub repository that is setup to run the GitHub Pages 29 | web build. 30 | 31 | 32 | 33 | ## Read the Docs vs GitHub Pages 34 | 35 | GitHub pages is a great option for your documentation deployment. 36 | However, you will need to do a bit more work to build and deploy your 37 | documentation if you use GitHub pages. 38 | 39 | Read the Docs can be setup in your Read the Docs user account. The service 40 | automates the entire process of building and deploying your documentation. 41 | 42 | If you don't want to maintain a documentation website for your Python package, 43 | we suggest using the Read the Docs website. 44 | -------------------------------------------------------------------------------- /documentation/hosting-tools/sphinx-python-package-documentation-tools.md: -------------------------------------------------------------------------------- 1 | # Using Sphinx to Build Python Package Documentation 2 | 3 | 5 | 16 | 17 | On this page we discuss using [Sphinx](https://www.sphinx-doc.org/) to build your user-facing 18 | package documentation. While Sphinx is currently the most 19 | commonly-used tool in the scientific Python ecosystem, you 20 | are welcome to explore other tools to build documentation 21 | such as [mkdocs](https://www.mkdocs.org/) which is gaining 22 | popularity in the Python packaging ecosystem. 23 | 24 | ```{tip} 25 | Examples of documentation websites that we love: 26 | 27 | * [GeoPandas](https://geopandas.org/en/stable/) 28 | * [View rst to create landing page](https://raw.githubusercontent.com/geopandas/geopandas/main/doc/source/index.rst) 29 | * [verde](https://www.fatiando.org/verde/latest/) 30 | * [View verde landing page code - rst file.](https://github.com/fatiando/verde/blob/main/doc/index.rst) 31 | * [Here is our documentation if you want to see a myST example of a landing page.](https://github.com/pyOpenSci/python-package-guide/blob/main/index.md) 32 | ``` 33 | 34 | ## Sphinx - a static site generator 35 | 36 | Sphinx is a [static-site generator](https://www.cloudflare.com/learning/performance/static-site-generator/). A static site generator is a tool that creates 37 | html for a website based upon a set of templates. The html files are then served "Statically" which means that there is no generation or modification of the files on the fly. 38 | 39 | Sphinx is written using Python. 40 | 41 | ### Sphinx sites can be customized using extensions and themes 42 | 43 | The functionality of Sphinx can be extended using extensions 44 | and themes. A few examples include: 45 | 46 | * You can apply documentation themes for quick generation of beautiful documentation. 47 | * You can [automatically create documentation for your package's functions and classes (the package's API) from docstrings in your code using the autodoc extension](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html) 48 | * You can [run and test code examples in your docstrings using the doctest extension](https://www.sphinx-doc.org/en/master/usage/extensions/doctest.html) 49 | * While Sphinx natively supports the `rST` syntax, you can add custom syntax parsers to support easier-to-write syntax using tools such as [the MyST parser](https://myst-parser.readthedocs.io/). 50 | 51 | ### Commonly used Sphinx themes 52 | 53 | You are free to use whatever Sphinx theme that you prefer. 54 | However, the most common Sphinx themes used in the Python 55 | scientific community include: 56 | 57 | * [pydata-sphinx-theme](https://pydata-sphinx-theme.readthedocs.io/) 58 | * [sphinx-book-theme](https://sphinx-book-theme.readthedocs.io/) 59 | * [furo](https://pradyunsg.me/furo/quickstart/) 60 | 61 | 62 | ```{tip} 63 | This book is created using Sphinx and the `furo` theme. 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /documentation/hosting-tools/website-hosting-optimizing-your-docs.md: -------------------------------------------------------------------------------- 1 | # Optimizing your documentation so search engines (and other users) find it 2 | 3 | If you are interested in more people finding your package, you may want to 4 | add some core Sphinx extensions (and theme settings) that will help search 5 | engines such as Google find your documentation. 6 | 7 | ## Google Analytics 8 | 9 | ```{important} 10 | 11 | Google analytics [is not compliant with the European General Data Protection Regulation (GDPR)](https://matomo.org/blog/2022/05/google-analytics-4-gdpr/). While there are many components to this regulation, one of the core elements is that you have to let users know on your site that you are collecting data and they have to consent. While it is possible to add infrastructure around Google Analytics to make it close to following GDPR regulations, the community is slowly shifting away from Google using open tools such as [Plausible](https://plausible.io/), [Cloudflare Web Analytics](https://www.cloudflare.com/web-analytics/) and [Matomo](https://matomo.org) for web analytics. 12 | 13 | pyOpenSci is currently looking into free options for open source 14 | developers. 15 | ``` 16 | Some of the [sphinx themes such as the `pydata-sphinx-theme` and 17 | sphinx-book-theme have built in support for Google Analytics](https://pydata-sphinx-theme.readthedocs.io/en/latest/user_guide/analytics.html#google-analytics). However, if the theme that you chose does not offer 18 | Google Analytics support, you can use the [`sphinxcontrib-gtagjs` extension](https://github.com/attakei/sphinxcontrib-gtagjs). 19 | This extension will add a Google Analytics site tag to each page of your 20 | documentation. 21 | 22 | ### [sphinx-sitemap](https://sphinx-sitemap.readthedocs.io/en/latest/index.html) for search engine optimization 23 | 24 | While we are trying to move away from Google Analytics do 25 | to compliance and privacy issues, search engine optimization 26 | is still important. Google is the most popular search engine. 27 | And if your documentation is search optimized, users are more 28 | likely to find your package! 29 | 30 | If you are interested in optimizing your documentation for 31 | search engines such as Google, you want a **sitemap.xml** file. 32 | You can submit this sitemap to Google and it will index your 33 | entire site. This over time can make the content on your site 34 | more visible to others when they search. 35 | 36 | This extension is lightweight. 37 | 38 | It [requires that you to add it to your Sphinx `conf.py` extension list and site your documentation base url](https://sphinx-sitemap.readthedocs.io/en/latest/getting-started.html). 39 | 40 | ### [sphinxext.opengraph](https://github.com/wpilibsuite/sphinxext-opengraph) 41 | 42 | OpenGraph is an extension that allows you to add metadata to your documentation 43 | content pages. [The OpenGraph protocol allows other websites to provide a 44 | useful preview of the content on your page when shared](https://www.freecodecamp.org/news/what-is-open-graph-and-how-can-i-use-it-for-my-website/#heading-what-is-open-graph). This is important 45 | for when the pages in your documentation are shared on social 46 | media sites like Twitter and Mastodon and even for shares on 47 | tools like Slack and Discourse. 48 | -------------------------------------------------------------------------------- /documentation/index.md: -------------------------------------------------------------------------------- 1 | # Documentation for your Open Source Python Package 2 | 3 | :::{toctree} 4 | :hidden: 5 | :caption: Intro 6 | 7 | Documentation Overview 8 | ::: 9 | 10 | ```{toctree} 11 | :hidden: 12 | :maxdepth: 2 13 | :caption: Write User Documentation 14 | 15 | Intro 16 | Create Your Docs 17 | Document Your Code (API) 18 | Create Package Tutorials 19 | ``` 20 | 21 | ```{toctree} 22 | :hidden: 23 | :caption: Docs for Contributors & Maintainers 24 | :maxdepth: 2 25 | 26 | Intro 27 | Contributing File 28 | Development Guide 29 | Changelog File 30 | ``` 31 | 32 | ```{toctree} 33 | :hidden: 34 | :caption: Community Docs 35 | :maxdepth: 2 36 | 37 | README file 38 | Code of Conduct File 39 | LICENSE files 40 | ``` 41 | 42 | ```{toctree} 43 | :maxdepth: 2 44 | :hidden: 45 | :caption: Publication tools for your docs 46 | 47 | Intro 48 | Sphinx for Docs 49 | myST vs Markdown vs rst 50 | Publish Your Docs 51 | Website Hosting and Optimization 52 | ``` 53 | 54 | ```{important} 55 | Please note that the tools discussed here are those that 56 | we see commonly used in the community. As tools evolve we 57 | will update this guide. If you are submitting a package for 58 | pyOpenSci peer review and use other tools that are not listed 59 | in our guide to build your package you can still submit for 60 | review! The tools listed here are suggestions, not 61 | requirements. Our requirements are focused on the 62 | documentation content of your package. 63 | ``` 64 | 65 | ## Documentation is critical for your Python package's success 66 | 67 | Documentation is as important to the success of your Python open source package 68 | as the code itself. 69 | 70 | Quality code is of course valuable as its how your package gets the tasks done. However, if users don't understand 71 | how to use your package in their workflows, then they won't use it. 72 | 73 | Further, explicitly documenting how to contribute is important if you wish 74 | to build a base of contributors to your package. 75 | 76 | ## Two types of Python package users 77 | 78 | The documentation that you write for your 79 | package should target two types of users: 80 | 81 | ### 1. Basic Tool Users 82 | 83 | Basic tool users are the people who will use your package code in their 84 | Python workflows. They might be new(er) to Python and/or data science. Or 85 | expert programmers. But they might not have a background in software 86 | development. These users need to know: 87 | 88 | - How to install your package 89 | - How to install dependencies that your package requires 90 | - How to get started using the code base 91 | - Information on how to cite your code / give you credit if they are using it 92 | in a research application. 93 | - Information on the license that your code uses so they know how they can 94 | or can't use the code in an operational setting. 95 | 96 | ### 2. Potential tool contributors 97 | 98 | The other subset of users are more experienced and/or more engaged 99 | with your package. As such they are 100 | potential contributors. These users: 101 | 102 | - might have a software development background, 103 | - might also be able to contribute bug fixes to your package or updates to your documentation 104 | - might also just be users who will find spelling errors in your documentation, or bugs in your tutorials. 105 | 106 | These users need all of the things that a basic user needs. But, they 107 | also need to understand how you'd like for them to contribute to your 108 | package. These potential contributors need: 109 | 110 | - A development guide to help them understand the infrastructure used in your package repository. 111 | - Contributing guidelines that clarify the types of contributions that you welcome and how you'd prefer those contributions to be submitted. 112 | 113 | ```{important} 114 | It's important to remember that the definition of what a contribution is can be 115 | broad. A contribution could be something as simple as a bug report. Or fixing a 116 | spelling issue in your documentation. Or it could be a code fix that includes a 117 | new test that covers an edge-case that they discovered. 118 | ``` 119 | 120 | ## Documentation elements that pyOpenSci looks for reviewing a Python package 121 | 122 | In the pyOpenSci open peer review, we look for 123 | a documentation structure that supports both your tool users and potential 124 | contributors. The files and elements that we look for specifically can be 125 | found in our peer review check list (see link below). 126 | 127 | In this guide, we discuss each required element, and also discuss other elements 128 | that you should consider in your package's documentation in more detail. 129 | 130 | 131 | ```{button-link} https://www.pyopensci.org/software-peer-review/how-to/editor-in-chief-guide.html#editor-checklist-template 132 | :color: primary 133 | :class: sd-rounded-pill float-left 134 | View pyOpenSci peer review check list 135 | ``` 136 | 137 | 138 | ```{figure} ../images/moving-pandas-python-package-github-main-repo.png 139 | --- 140 | name: moving-pandas-github-repo-image 141 | width: 80% 142 | alt: Image showing the files in the the MovingPandas GitHub repository. Files in the image include code of conduct.md contributing.md license.txt and readme.md. 143 | --- 144 | An example from the MovingPandas GitHub repository with all of the major files in it including CONTRIBUTING.md, README.md, CODE_OF_CONDUCT.md and a LICENSE.txt file. *(screen shot taken Nov 23 2022)* 145 | ``` 146 | 147 | ## What's next in this Python package documentation section? 148 | 149 | In this section of the pyOpenSci package guide, we will walk 150 | you through best practices for setting up 151 | documentation for your Python package. We will also suggest 152 | tools that you can use to build your user-facing documentation website. 153 | 154 | :::{todo} 155 | 156 | Python version support 157 | You should always be explicit about which versions of Python your package supports. 158 | Keeping compatibility with old Python versions can be difficult as functionality changes. 159 | A good rule of thumb is that the package should support, at least, 160 | the latest three Python versions (e.g., 3.8, 3.7, 3.6). 161 | ::: 162 | -------------------------------------------------------------------------------- /documentation/repository-files/changelog-file.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG.md Guide 2 | 3 | ## Introduction 4 | 5 | The `CHANGELOG.md` document serves as a valuable resource for developers and users alike to track the evolution of a project over time. Understanding the structure and purpose of a changelog helps users and contributors stay informed about new features, bug fixes, and other changes introduced in each release. 6 | 7 | ## What is CHANGELOG.md? 8 | 9 | The primary purpose of `CHANGELOG.md` is to provide a record of notable changes made to the project with each new release. This document helps users understand what has been added, fixed, modified, or removed with each version of the software. 10 | 11 | [Keep a CHAGELOG.md](https://keepachangelog.com/en/1.1.0/) is a great, simple resource for understanding what a changelog is and how to create a good changelog. It also includes examples of things to avoid. 12 | 13 | ```{admonition} Versioning your Python package and semantic versioning 14 | :class: tip 15 | 16 | An important component of a package that serves as the backbone behind the changelog file is a good versioning scheme. Semantic Versioning is widely used across Python packages. 17 | * [Creating New Versions of Your Python Package](../../package-structure-code/python-package-versions.md) 18 | * [Semantic Versioning](https://semver.org) 19 | ``` 20 | 21 | ## Why is it important? 22 | 23 | A well-maintained changelog is essential for transparent communication with users and developers. It serves as a centralized hub for documenting changes and highlights the progress made in each release. By keeping the changelog up-to-date, project maintainers can build trust with their user base and demonstrate their commitment to improving the software. 24 | 25 | ## What does it include? 26 | 27 | The contents of a changelog.md file typically follow a structured format, detailing the changes introduced in each release. While the exact format may vary depending on the project's conventions, some common elements found in changelogs for Python packages include: 28 | 29 | - **Versioning**: Clear identification of each release version using semantic versioning or another versioning scheme adopted by the project. 30 | 31 | - **Release Date**: The date when each version was released to the public, providing context for the timeline of changes. 32 | 33 | - **Change Categories**: Organizing changes into categories such as "Added," "Changed," "Fixed," and "Removed" to facilitate navigation and understanding. 34 | 35 | - **Description of Changes**: A concise description of the changes made in each category, including new features, enhancements, bug fixes, and deprecated functionality. 36 | 37 | - **Links to Issues or Pull Requests**: References to relevant issue tracker items or pull requests associated with each change, enabling users to access more detailed information if needed. 38 | 39 | - **Upgrade Instructions**: Guidance for users on how to upgrade to the latest version, including any breaking changes or migration steps they need to be aware of. 40 | 41 | - **Contributor Recognition**: Acknowledgment of contributors who made significant contributions to the release, fostering a sense of community and appreciation for their efforts. 42 | 43 | ## How do maintainers use it? 44 | 45 | Often you will see a changelog that documents a few things: 46 | 47 | ### Unreleased Section 48 | 49 | Unreleased commits are at the top of the changelog, commonly in an `Unreleased` section. This is where you can add new fixes, updates and features that have been added to the package since the last release. 50 | 51 | This section might look something like this: 52 | 53 | ```markdown 54 | ## Unreleased 55 | * Fix: Fixed a bug.... more here. (@github_username, #issuenumber) 56 | * Add: new feature to... more here. (@github_username, #issuenumber) 57 | ``` 58 | 59 | ### Release Sections 60 | 61 | When you are ready to make a new release, you can move the elements into a section that is specific to that new release number. 62 | 63 | This specific release section will sit below the unreleased section and can include any updates, additions, deprecations and contributors. 64 | 65 | The unreleased section then always lives at the top of the file and new features continue to be added there. At the same time, after releasing a version like v1.0 all of its features remain in that specific section. 66 | 67 | ```markdown 68 | ## Unreleased 69 | 70 | ## v1.0 71 | 72 | ### Updates 73 | * Fix: Fixed a bug.... more here. (@github_username, #issuenumber) 74 | 75 | ### Additions 76 | * Add: new feature to ...more here (@github_username, #issuenumber) 77 | 78 | ### Deprecations 79 | 80 | ### Contributors to this release 81 | ``` 82 | 83 | ## What does it look like? 84 | 85 | This example comes from [Devicely](https://github.com/hpi-dhc/devicely/blob/main/CHANGELOG.md), a pyOpenSci accepted package. 86 | 87 | ```markdown 88 | # Changelog 89 | All notable changes to this project will be documented in this file. 90 | 91 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 92 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 93 | 94 | ## [Unreleased] 95 | 96 | ## [1.1.0] - 2021-08-24 97 | - removed acceleration magnitude from devicely.EmpaticaReader and devicely.FarosReader since it was out of the scope of the package 98 | - added more flexibility to missing files (e.g. ACC.csv, EDA.csv) to devicely.EmpaticaReader 99 | - changed TagsReader to TimeStampReader to be more consistent with the class naming structure in devicely 100 | - deprecated methods in devicely.SpacelabsReader: set_window and drop_EB 101 | - fixed issue with the timestamp index and fixed column names in devicely.SpacelabsReader 102 | 103 | ## [1.0.0] - 2021-07-19 104 | ### Added 105 | - devicely.FarosReader can both read from and write to EDF files and directories 106 | - devicely.FarosReader has as attributes the individual dataframes (ACC, ECG, ...) and not only the joined dataframe 107 | 108 | ### Changed 109 | - in devicely.SpacelabsReader, use xml.etree from the standard library instead of third-party "xmltodict" 110 | - switch from setuptools to Poetry 111 | 112 | ### Removed 113 | - removed setup.py because static project files such as pyproject.toml are preferred 114 | ``` 115 | -------------------------------------------------------------------------------- /documentation/repository-files/code-of-conduct-file.md: -------------------------------------------------------------------------------- 1 | (coc-file)= 2 | 3 | # The CODE_OF_CONDUCT file - Python Packaging 4 | 5 | ```{admonition} Example CODE_OF_CONDUCT files 6 | :class: tip 7 | 8 | * [SciPy Code of Conduct file - notice they included theirs in their documentation](https://docs.scipy.org/doc/scipy/dev/conduct/code_of_conduct.html) 9 | * [fatiando CODE_OF_CONDUCT.md file](https://github.com/fatiando/community/blob/main/CODE_OF_CONDUCT.md) 10 | ``` 11 | 12 | Your package should have a `CODE_OF_CONDUCT.md` file located 13 | the root of the repository. Once you have people using your 14 | package, you can consider the package itself as having a community 15 | around it. Some of this community uses your tool. These users 16 | may have questions or encounter challenges using your package. 17 | 18 | Others in the community might want to contribute to your tool. 19 | They might fix bugs, update documentation and engage with the 20 | maintainer team. 21 | 22 | ## Why you need a CODE_OF_CONDUCT 23 | 24 | In order to keep this community healthy and to protect yourself, 25 | your maintainer team and your users from unhealthy behavior, 26 | it is important to have a [`CODE_OF_CONDUCT`](https://opensource.guide/code-of-conduct/). 27 | 28 | The `CODE_OF_CONDUCT` is important 29 | as it establishes what you expect in terms of how users and 30 | contributors interact with maintainers and each other. It also 31 | establishes rules and expectations which can then be enforced 32 | if need be to protect others from harmful and/or negative behaviors. 33 | 34 | If you are not comfortable 35 | with creating your own `CODE_OF_CONDUCT` text, we encourage you to adopt the 36 | `CODE_OF_CONDUCT` language used in the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). 37 | [Many other communities](https://www.contributor-covenant.org/adopters/) have adopted this `CODE_OF_CONDUCT` as 38 | their own. See the [Fatiando a Terra Geoscience Python community's example here.](https://github.com/fatiando/community/blob/main/CODE_OF_CONDUCT.md) 39 | -------------------------------------------------------------------------------- /documentation/repository-files/contributing-file.md: -------------------------------------------------------------------------------- 1 | (contributing-file)= 2 | # Your Python Package CONTRIBUTING File 3 | 4 | The **CONTRIBUTING.md** is the landing page guide for your project's contributors. It outlines how contributors can get involved, the contribution types that you welcome, and how contributors should interact or engage with you and your maintainer team. The contributor guide should also link to get-started resources that overview how to set up development environments, what type of workflow you expect on GitHub/GitLab, and anything else that contributors might need to get started. 5 | 6 | This file benefits maintainers and contributors. For contributors, it provides a roadmap that helps them get started and makes their first contribution easier. For maintainers, it answers commonly asked questions and reduces the burden of explaining your process to every person who wants to contribute. This document creates a more collaborative and efficient development process for everyone. 7 | 8 | ## CONTRIBUTING files lower barriers to entry 9 | 10 | The contributing file lowers barriers to entry for new and seasoned contributors as it provides a roadmap. 11 | 12 | - **For Contributors**: It provides clear instructions on contributing, from reporting issues to submitting pull requests. 13 | - **For Maintainers**: It streamlines contributions by setting expectations and standardizing processes, reducing the time spent clarifying common questions or handling incomplete issues or pull requests. 14 | 15 | Including a well-written CONTRIBUTING.md file in your project is one way of making it more welcoming and open to new and seasoned contributors. It also helps create a smoother workflow for everyone involved. 16 | 17 | ## Make it welcoming 18 | 19 | Make the guide welcoming. Use accessible language to encourage participation from contributors of all experience levels. For example: 20 | 21 | - Avoid technical jargon or explain terms when necessary (for example, "fork the repository"). 22 | - Include a friendly introduction, such as "Thank you for your interest in contributing! We're excited to collaborate with you." 23 | - Highlight that all contributions, no matter how small, are valued. 24 | 25 | ## What a CONTRIBUTING.md file should contain 26 | 27 | :::{admonition} Example contributing files 28 | :class: tip 29 | 30 | - [PyGMT contributing file](https://github.com/GenericMappingTools/pygmt/blob/main/CONTRIBUTING.md) 31 | - [Verde's contributing file](https://github.com/fatiando/verde/blob/main/CONTRIBUTING.md) 32 | ::: 33 | 34 | Your Python package should include a file called **CONTRIBUTING.md** located in the 35 | root of your repository next to [your **README.md** file](readme-file). 36 | 37 | The CONTRIBUTING.md file should include information about: 38 | 39 | - The types of contributions that you welcome 40 | 41 | > Example: We welcome contributions of all kinds. If you want to address an existing issue, check out our issues in this repository and comment on the one that you'd like to help with. Otherwise, you can open a new issue... 42 | 43 | - How you'd like contributions to happen. Clearly outline your contribution process. For example: 44 | - Should contributors address open issues 45 | - Are new issues welcome? 46 | - Should contributors open a pull request (PR) directly or discuss changes first? 47 | 48 | - Include instructions for the fork and pull request workflow and link to resources or guides explaining these steps (if available). 49 | - Guidelines that you have in place for users submitting issues, pull requests, or asking questions. 50 | 51 | If you have a [development guide](development-guide), link to it. This guide should provide clear instructions on how to set up your development environment locally. It also should overview CI tools that you have that could simplify the contribution process (for example, pre-[commit.ci bot](https://www.pyopensci.org/python-package-guide/package-structure-code/code-style-linting-format.html#pre-commit-ci), and so on), [linters, code formatters](https://www.pyopensci.org/python-package-guide/package-structure-code/code-style-linting-format.html#code-linting-formatting-and-styling-tools), and so on. 52 | 53 | This guide should also include information for someone 54 | interested in asking questions. Some projects accept questions as GitHub or GitLab issues. Others use GitHub discussions, Discourse, or even a Discord server. 55 | 56 | The contributing file should also include: 57 | 58 | - A link to your [code of conduct](coc-file) 59 | - A link to your project's [LICENSE](license-file) 60 | - A link to a [development guide](development-guide) if you have one 61 | 62 | ## Summary 63 | 64 | A well-crafted CONTRIBUTING.md file is welcome mat for your project! By providing clear instructions, helpful resources, and a welcoming tone, you make it easier for contributors to get involved and build a stronger, more collaborative community around your project. 65 | -------------------------------------------------------------------------------- /documentation/repository-files/development-guide.md: -------------------------------------------------------------------------------- 1 | (pyos-development-guide)= 2 | # What the development guide for your Python package should contain 3 | 4 | Ideally, your package should also have a development guide. This file may live in your package documentation and should be linked to from your CONTRIBUTING.md file (discussed above). 5 | A development guide should clearly show 6 | technically proficient users how to: 7 | 8 | * Set up a development environment locally to work on your package 9 | * Run the test suite 10 | * Build documentation locally 11 | 12 | The development guide should also have guidelines for: 13 | 14 | * code standards including docstring style, code format and any specific code approaches that the package follows. 15 | 16 | It's also helpful to specify the types of tests you request if a contributor submits a new feature or a change to an existing feature that will not be covered by your existing test suite. 17 | 18 | If you have time to document it, it's also helpful to document your maintainer workflow and release processes. 19 | 20 | ## Why a development guide is important 21 | 22 | It's valuable to have a development guide, in the 23 | case that you wish to: 24 | 25 | * Onboard new maintainers. 26 | * Allow technically inclined contributors to make thoughtful and useful code based pull requests to your repository. 27 | 28 | It also is important to pyOpenSci that the maintenance workflow is 29 | documented in the case that we need to help you onboard new 30 | maintainers in the future. 31 | 32 | ```{note} 33 | A well thought out continuous integration setup in your repository 34 | can allow users to skip building the package locally (especially if they are just updating text). 35 | ``` 36 | 37 | ```{tip} 38 | A development guide, while strongly recommended, is not a file that 39 | pyOpenSci requires a package to have in order to be eligible for 40 | review. Some maintainers may also opt to include the development information in their contributing guide. 41 | ``` 42 | 43 | ```{tip} 44 | [The Mozilla Science Lab website has a nice outline of things to consider when 45 | creating a contributing guide](https://mozillascience.github.io/working-open-workshop/contributing/) 46 | ``` 47 | 48 | 59 | -------------------------------------------------------------------------------- /documentation/repository-files/intro.md: -------------------------------------------------------------------------------- 1 | # Documentation Files That Should be in your Python Package Repository 2 | 3 | In this section of the Python packaging guide, we review all of the files that 4 | you should have in your Python package repository. Your Python package should, 5 | at a minimum have the following files: 6 | 7 | The files mentions above (README, Code of Conduct, license 8 | file, etc) are used as a measure of package community health 9 | on many online platforms. Below, you can see an example how GitHub 10 | evaluates community health. This community health link is available for 11 | all GitHub repositories. 12 | 13 | ```{figure} /images/moving-pandas-python-package-github-community-standards.png 14 | --- 15 | name: moving-pandas-github-community 16 | width: 80% 17 | alt: Image showing that the MovingPandas GitHub repository community health page with green checks next to each file including a description, README, code of conduct, contributing, license and issue templates. Note that Security policy has a yellow circle next to it as that is missing from the repo. 18 | --- 19 | GitHub community health looks for a readme file among other elements when it evaluates the community level health of your repository. This example is from the [MovingPandas GitHub repo](https://github.com/movingpandas/movingpandas/community) *(screen shot taken Nov 23 2022)* 20 | ``` 21 | 22 | [Snyk](https://snyk.io/advisor/python) is another well-known company that 23 | keeps tabs on package health. Below you can see a similar evaluation of files 24 | in the GitHub repo as a measure of community health. 25 | 26 | ```{figure} /images/moving-pandas-python-package-snyk-health.png 27 | --- 28 | name: moving-pandas-snyk 29 | width: 80% 30 | alt: Screenshot of the Snyk page for movingpandas. It shows that the repository has a README file, contributing file, code of conduct. It also shows that it has 30 contributors and no funding. The package health score is 78/100. 31 | --- 32 | Screenshot showing [SNYK](https://snyk.io/advisor/python/movingpandas) package health for moving pandas. Notice both platforms look for a README file. *(screen shot taken Nov 23 2022)* 33 | ``` 34 | -------------------------------------------------------------------------------- /documentation/repository-files/license-files.md: -------------------------------------------------------------------------------- 1 | --- 2 | bibliography: 3 | - ../../bibliography.bib 4 | --- 5 | 6 | (license-file)= 7 | 8 | # License files for Python open source software 9 | 10 | :::{button-link} 11 | :color: primary 12 | :class: sd-rounded-pill float-left 13 | 14 | Want to learn how to add a license file to your GitHub repository? Check out this lesson. 15 | ::: 16 | 17 | ## What is a Open Source License file? 18 | 19 | When we talk about LICENSE files, we are referring to a file in your 20 | GitHub or GitLab repository that contains legally binding language 21 | that describes to your users how they can legally use (and not use) your package. 22 | 23 | ## Why licenses are important 24 | 25 | A license file is important for all open source projects because it protects both you as a maintainer and your users. The license file helps your users and the community understand: 26 | 27 | 1. How they can use your software 28 | 2. Whether the software can be reused or adapted for other purposes 29 | 3. How people can contribute to your project 30 | 31 | and more. 32 | 33 | [Read more about why license files are critical in protecting both you as a maintainer and your users of your scientific Python open source package.](https://opensource.guide/legal/#just-give-me-the-tldr-on-what-i-need-to-protect-my-project) 34 | 35 | ## Where to store your license 36 | 37 | Your `LICENSE` file should be stored at root of your GitHub / GitLab repository. 38 | 39 | Some maintainers customize the language in their license files for specific reasons. However, if you are just getting started, we suggest that you select a 40 | permissive license and then use the legal language templates provided both by GitHub and/or the [choosealicense.com](https://choosealicense.com/) website. 41 | 42 | Licenses are legally binding, as such you should avoid trying to create your own license unless you have the guidance of legal council. 43 | 44 | ### Use open permissive licenses when possible 45 | 46 | We generally suggest that you use a permissive, license that is [Open Software Initiative (OSI) approved](https://opensource.org/licenses/). If you are 47 | [submitting your package to pyOpenSci for peer review](https://www.pyopensci.org/about-peer-review/index.html), then we require an OSI approved 48 | license. 49 | 50 | :::{admonition} Copyleft licenses 51 | The other major category of licenses are ["copyleft" licenses](https://en.wikipedia.org/wiki/Copyleft). 52 | Copyleft licenses require people that use your work to redistribute it with the same (or greater) rights to modify, copy, share, and redistribute it. 53 | In other words, copyleft licenses prohibit someone taking your work, making a proprietary version of it, and redistributing it without providing the source code so others can do the same. 54 | Copyleft licenses are "sticky" in that they are designed to ensure that more free software is created. 55 | 56 | The difference between copyleft and permissive licenses is an important cultural divide in free and open source software (e.g., see {footcite}`hunterReclaimingComputingCommons2016`, {footcite}`gnuprojectWhatFreeSoftware2019`, {footcite}`gnuprojectWhatCopyleft2022`). 57 | It is important to understand this difference when choosing your license. Copyleft licenses represents the "free" part of "free and open source software". 58 | Free and open source software is intrinsically political, and it is important to be aware of power dynamics in computing as well as the practical problems of license compatibility (discussed below). 59 | ::: 60 | 61 | ### How to choose a license 62 | 63 | To select your license, we suggest that you use GitHub's 64 | [Choose a License tool](https://choosealicense.com/). 65 | 66 | If you choose your license when creating a new GitHub repository, you can also 67 | automatically get a text copy of the license file to add to your repository. However 68 | in some cases the license that you want is not available through that online 69 | process. 70 | 71 | :::{admonition} License recommendations from the SciPy package 72 | [The SciPy documentation has an excellent overview of licenses.](https://docs.scipy.org/doc/scipy/dev/core-dev/index.html#licensing) One of the key elements 73 | that these docs recommend is ensuring that the license that you select is 74 | compatible with licenses used in many parts of the scientific Python ecosystem. 75 | Below is a highlight of this text which outlines license that are compatible 76 | with the modified BSD license that SciPy uses. 77 | 78 | > Other licenses that are compatible with the modified BSD license that SciPy uses are 2-clause BSD, MIT and PSF. Incompatible licenses are GPL, Apache and custom licenses that require attribution/citation or prohibit use for commercial purposes. 79 | 80 | If your primary goal is for your code to be used by other, major packages in the scientific ecosystem, we also recommend 81 | that you consider using either BSD or MIT as your 82 | license. If you are unsure, the MIT license tends to be a simpler easier-to-understand option. 83 | ::: 84 | 85 | ## Important: make sure that you closely follow the guidelines outlines by the License that you chose 86 | 87 | Every license has different guidelines in terms of what code 88 | you can use in your package and also how others can (or can not) use the code in your package. 89 | 90 | If you borrow code from other tools or online sources, make 91 | sure that the license for the code that you are using also complies 92 | with the license that you selected for your package. 93 | 94 | A useful way to think about license compatibility is the distinction between **"inbound"** and **"outbound"** compatibility. 95 | "Inbound" licenses are those that cover the software you plan to include in your package. 96 | Your package is protected by an "outbound" license. 97 | 98 | **Permissive licenses** like BSD and MIT have few **outbound** restrictions - they can be used in any way by downstream consumers, including making them proprietary. 99 | This is why they are favored by many businesses and large packages that want to be adopted by businesses. 100 | Permissive licenses have more **inbound** restrictions - they can't use software that requires more freedoms to be preserved than they do, like copyleft licenses. 101 | A package licensed under MIT needs to take special care when including or modifying a package licensed under the GPL-3. 102 | 103 | **Copyleft licenses** like GPL-3 have more **outbound** restrictions - they require more of packages that include, use, modify, and reproduce them. 104 | This is the purpose of copyleft licenses, to ensure that derivative works remain free and open source. 105 | They have fewer **inbound** restrictions - a GPL-3 licensed package can include any other permissively licensed and most copyleft licensed packages. 106 | 107 | | Compatible | Dependency
("Inbound") | Your Package | Downstream Package
("Outbound") | 108 | |----------------------------------------------------------------:|-----------------------------|--------------|--------------------------------------| 109 | | | Permissive | Permissive | | 110 | | | Copyleft | Permissive | | 111 | | | | Permissive | Permissive | 112 | | | | Permissive | Copyleft | 113 | | | Permissive | Copyleft | | 114 | | | Copyleft | Copyleft | | 115 | | | | Copyleft | Permissive | 116 | | | | Copyleft | Copyleft | 117 | 118 | :::{admonition} An example of how a license determine how code can be reused 119 | :class: note 120 | 121 | Let's use StackOverflow as an example that highlights how a license determines how code can or can not be used. 122 | 123 | [Stack Overflow uses a Creative Commons Share Alike license.](https://stackoverflow.com/help/licensing). The sharealike license requires you to use the same sharealike license when you reuse any code from Stack Overflow. 124 | 125 | This means that from a legal perspective, if you copy code from the Stack Overflow website and use it in your package that is licensed differently, say with a MIT license, you are violating Stack Overflow's license requirements! 126 | This would not be true with a GPL licensed package. `GPL-3` packages can include code licensed by `CC-BY-SA` {footcite}`creativecommonsShareAlikeCompatibilityGPLv32015`. 127 | 128 | 🚨 Proceed with caution! 🚨 129 | ::: 130 | 131 | ## What about software citation? 132 | 133 | While many permissive licenses do not require citation we STRONG encourage that you cite all software that you use in papers, blogs and other publications. You tell your users how to cite your package by using a [citation.cff file](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-citation-files). We will cover this topic when we talk about creating DOI's for your package using Zenodo. 134 | 135 | 140 | 141 | # References 142 | 143 | ```{footbibliography} 144 | ``` 145 | -------------------------------------------------------------------------------- /documentation/repository-files/readme-file-best-practices.md: -------------------------------------------------------------------------------- 1 | (readme-file)= 2 | 3 | # README File Guidelines and Resources 4 | 5 | Your **README.md** file should be located in the root of your GitHub repository. 6 | The **README.md** file is important as it is often the first thing that someone 7 | sees before they install your package. 8 | 9 | The README.md file is the landing page of: 10 | 11 | * Your package as it appears on a repository site such as PyPI or Anaconda.org 12 | * Your package's GitHub repository 13 | 14 | Your README.md file is also used as a measure of package and community 15 | health on sites such as: 16 | 17 | * [GitHub community health for MovingPandas (available for all repositories)](https://github.com/movingpandas/movingpandas/community) and [Snyk - MovingPandas example](https://snyk.io/advisor/python/movingpandas) 18 | 19 | ```{figure} /images/pandera-python-package-readme-github.png 20 | --- 21 | name: pandera-readme 22 | width: 80% 23 | alt: README landing page screenshot for the Pandera package. It has the Pandera logo at the top - which has two arrows in a chevron pattern pointing downward within a circle. Subtitle is statistical data testing toolkit. A data validation library for scientists, engineering, and analytics seeking correctness. Below that are a series of badges including CI tests passing, docs passing, version of Pandera on pypi (0.13.4), MIT license and that it has been pyOpenSci peer reviewed. There are numerous badges below that. Finally below the badges the text says, Pandera provides a flexible and expressive API for performing data validation on dataframe-like objects to make data processing pipelines more readable and robust. 24 | --- 25 | Your GitHub repository landing page highlights the README.md file. Here you can see the README.md file for the pyOpenSci package [Pandera](https://github.com/unionai-oss/pandera). *(screen shot taken Nov 23 2022)* 26 | ``` 27 | 28 | Thus, it is important that you spend some time up front creating a high quality 29 | **README.md** file for your Python package. 30 | 31 | ````{note} 32 | An editor or the editor in chief will ask you to revise your README file 33 | before a review begins if it does not meet the criteria specified below. 34 | 35 | Please go through this list before submitting your package to pyOpenSci 36 | 37 | ``` 38 | ### pyOpenSci README checklist 39 | 40 | Your README file should have the following information: 41 | - [ ] The name of the package 42 | - [ ] Badges for the packages current published version, documentation and test suite build. (OPTIONAL: test coverage) 43 | - [ ] Easy-to-understand explanation (2-4 sentences) of what your tool does 44 | - [ ] Context for how the tool fits into the broader ecosystem 45 | - [ ] If your library/package "wraps" around another package, link to the package that it is wrapping and any associated documentation. *(HINT: If you don't know what a wrapper is, this probably doesn't apply to you!)* 46 | - [ ] A simple quick-start code example that a user can follow to provide a demonstration of what the package can do for them 47 | - [ ] Links to your package's documentation / website. 48 | - [ ] A few descriptive links to any tutorials you've created for your package. 49 | ``` 50 | ```` 51 | 52 | ## What your README.md file should contain 53 | 54 | Your **README.md** file should contain the following things (listed from top to bottom): 55 | 56 | ### ✔️ Your package's name 57 | 58 | Ideally your GitHub repository's name is also the name of your package. The more 59 | self explanatory that name is, the better. 60 | 61 | ### ✔️ Badges for current package version, continuous integration and test coverage 62 | 63 | Badges are a useful way to draw attention to the quality of your project. Badges 64 | assure users that your package is well-designed, tested, and maintained. They 65 | are also a useful maintenance tool to evaluate if things are building properly. 66 | A great example of this is adding a [Read the Docs status badge](https://docs.readthedocs.io/en/stable/badges.html) to your README.md file to quickly 67 | see when the build on that site fails. 68 | 69 | It is common to provide a collection of badges towards the top of your 70 | README file for others to quickly browse. 71 | 72 | Some badges that you might consider adding to your README file include: 73 | 74 | * Current version of the package on PyPI / Anaconda.org 75 | 76 | Example: [![PyPI version shields.io](https://img.shields.io/pypi/v/pandera.svg)](https://pypi.org/project/pandera/) 77 | 78 | * Status of tests (pass or fail) - Example: [![CI Build](https://github.com/pandera-dev/pandera/workflows/CI%20Tests/badge.svg?branch=main)](https://github.com/pandera-dev/pandera/actions?query=workflow%3A%22CI+Tests%22+branch%3Amain) 79 | 80 | * Documentation build - Example: ![Docs Building](https://github.com/pyOpenSci/python-package-guide/actions/workflows/build-book.yml/badge.svg) 81 | 82 | * DOI (for citation) Example: [![DOI](https://zenodo.org/badge/556814582.svg)](https://zenodo.org/badge/latestdoi/556814582) 83 | 84 | ```{tip} 85 | Once you package is accepted to pyOpenSci, we will provide you with 86 | a badge to add to your repository that shows that it has been reviewed. 87 | [![pyOpenSci](https://pyopensci.org/badges/peer-reviewed.svg)](https://github.com/pyOpenSci/software-review/issues/12) 88 | 89 | ``` 90 | 91 | ```{caution} 92 | Beware of the overuse of badges! There is such a thing as too much of a good thing (which can overload a potential user!). 93 | ``` 94 | 95 | ### ✔️ A short, easy-to-understand description of what your package does 96 | 97 | At the top of your README file you should have a short, easy-to-understand, 1-3 98 | sentence description of what your package does. This section should clearly 99 | state your goals for the package. The language in this description should use 100 | less technical terms so that a variety of users with varying scientific (and 101 | development) backgrounds can understand it. 102 | 103 | In this description, it's useful to let users know how your package fits within 104 | the broader scientific Python package ecosystem. If there are other similar packages 105 | or complementary package mentions them here in 1-2 sentences. 106 | 107 | ```{tip} 108 | Consider writing for a high school level (or equivalent) level. This 109 | level of writing is often considered an appropriate level for scientific content that 110 | serves a variety of users with varying backgrounds. 111 | 112 | The goal of this description is to maximize accessibility of your **README** 113 | file. 114 | ``` 115 | 116 | ### ✔️ Installation instructions 117 | 118 | Include instructions for installing your package. If you have published 119 | the package on both PyPI and Anaconda.org, be sure to include instructions for both. 120 | 121 | ### ✔️ Document any additional setup required 122 | 123 | Add any additional setup required such as authentication tokens, to 124 | get started using your package. If setup is complex, consider linking to an 125 | installation page in your online documentation here rather than over complicating 126 | your README file. 127 | 128 | ### ✔️ Brief demonstration of how to use the package 129 | 130 | This description ideally includes a brief, quick start code 131 | example that shows a user how to get started using your package. 132 | 133 | ### ✔️ Descriptive links to package documentation, short tutorials 134 | 135 | Include descriptive links to: 136 | 137 | * The package's documentation page. 138 | * Short tutorials that demonstrate application of your package. 139 | 140 | ```{admonition} Too Much Of A Good Thing 141 | :class: tip 142 | 143 | Try to avoid including several tutorials in the README.md file itself. This too will overwhelm the user with information. 144 | 145 | A short quick-start code example that shows someone how to use your package 146 | is plenty of content for the README file. All other tutorials and 147 | documentation 148 | should be presented as descriptive links. 149 | ``` 150 | 151 | ### ✔️ A Community Section with Links to Contributing Guide, Code of Conduct 152 | 153 | Use your README.md file to direct users to more information on: 154 | 155 | * Contributing to your package 156 | * Development setup for more advanced technical contributors 157 | * Your code of conduct 158 | * Licensing information 159 | 160 | All of the above files are important for building community around your 161 | project. 162 | 163 | ### ✔️ Citation information 164 | 165 | Finally be sure to include instructions on how to cite your package. 166 | Citation should include the DOI that you want used when citing your package, 167 | and any language that you'd like to see associated with the citation. 168 | 169 | :::{admonition} README Resources 170 | :class: tip 171 | 172 | Below are some resources on creating great README.md files that you 173 | might find helpful. 174 | 175 | * [How to Write a Great README - Bane Sullivan](https://github.com/banesullivan/README) 176 | * [Art of README - Kira (@hackergrrl)](https://github.com/hackergrrl/art-of-readme) 177 | 178 | ::: 179 | -------------------------------------------------------------------------------- /documentation/write-user-documentation/create-package-tutorials.md: -------------------------------------------------------------------------------- 1 | # Create tutorials in your Python package documentation 2 | 3 | 6 | Your package should have tutorials that make it easy for a user 7 | to get started using your package. Ideally, those tutorials 8 | also can be run from start to finish providing a second set of 9 | checks (on top of your test suite) to your package's code base. 10 | 11 | On this page, we review two Sphinx extensions (`sphinx-gallery` and `nbsphinx`) 12 | that allow you to create reproducible tutorials that are run 13 | when your Sphinx documentation builds. 14 | 15 | ## Create Python package tutorials that run when you build your docs 16 | 17 | Adding well constructed tutorials to your package will make it easier for someone 18 | new to begin using your package. 19 | 20 | There are two Sphinx tools that make it easy to add tutorials to your package: 21 | 22 | * [Sphinx Gallery](https://sphinx-gallery.github.io/stable/index.html) and 23 | * [NbSphinx](https://nbsphinx.readthedocs.io/en/latest/) 24 | 25 | Both of these tools act as Sphinx extensions and: 26 | 27 | * Support creating a gallery type page in your Sphinx documentation where users can explore tutorials via thumbnails. 28 | * Run the code in your tutorials adding another level of "testing" for your package as used. 29 | * Render your tutorials with Python code and plot outputs 30 | 31 | ### [sphinx gallery:](https://sphinx-gallery.github.io/stable/index.html) 32 | 33 | If you prefer to write your tutorials using Python **.py** scripts, you 34 | may enjoy using Sphinx gallery. Sphinx gallery uses **.py** files with 35 | text and code sections that mimic the Jupyter Notebook format. When you build 36 | your documentation, the gallery extension: 37 | 38 | 1. Runs the code in each tutorial. Running your tutorial like this acts as a check to ensure your package's functions, classes, methods, and attributes (ie the API) are working as they should. 39 | 1. Creates a downloadable Jupyter Notebook **.ipynb** file and a **.py** script for your tutorial that a user can quickly download and run. 40 | 1. Creates a rendered **.html** page with the code elements and code outputs in a user-friendly tutorial gallery. 41 | 1. Creates a gallery landing page with visual thumbnails for each tutorial that you create. 42 | 43 | 44 | ```{figure} /images/sphinx-gallery-overview.png 45 | --- 46 | name: sphinx-gallery 47 | width: 80% 48 | alt: Image showing the gallery output provided by sphinx-gallery where each tutorial is in a grid and the tutorial thumbnails are created from a graphic in the tutorial. 49 | --- 50 | `sphinx-gallery` makes it easy to create a user-friendly tutorial gallery. 51 | Each tutorial has a download link where the user can download a **.py** file or a Jupyter Notebook. And it renders the tutorials in a user-friendly grid. 52 | ``` 53 | 54 | Below you can see what a tutorial looks like created with sphinx-gallery. 55 | 56 | ```{figure} /images/sphinx-gallery-tutorial.png 57 | --- 58 | name: spinx-gallery-tutorial 59 | width: 80% 60 | alt: Image showing ta single tutorial from Sphinx gallery. The tutorial shows a simple matplotlib created plot and associated code. 61 | --- 62 | `sphinx-gallery` tutorials by default include download links for both the 63 | python script (**.py** file) and a Jupyter notebook (**.ipynb** file) at the bottom. 64 | ``` 65 | 66 | ### Sphinx Gallery benefits 67 | * easy-to-download notebook and .py outputs for each tutorials. 68 | * .py files are easy to work with in the GitHub pull request environment. 69 | * Nice gridded gallery output. 70 | * Build execution time data per tutorial. [Example](https://sphinx-gallery.github.io/stable/auto_examples/sg_execution_times.html) 71 | 72 | #### Sphinx gallery challenges 73 | 74 | The downsides of using Sphinx gallery include: 75 | 76 | * the **.py** files can be finicky to configure, particularly if you have matplotlib plot outputs. 77 | 78 | For example: To allow for plots to render, you need to name each file with `plot_` 79 | at the beginning. 80 | 81 | * Many users these days are used to working in Jupyter Notebooks. .py may be slightly less user friendly to work with 82 | 83 | These nuances can make it challenging for potential contributors to add 84 | tutorials to your package. This can also present maintenance challenge. 85 | 86 | Add about the gallery setup: 87 | 88 | ```bash 89 | $ docs % make html 90 | 91 | Sphinx-Gallery successfully executed 2 out of 2 files 92 | ``` 93 | File directory structure: 94 | 95 | ```bash 96 | tutorials/ 97 | index.rst # landing page for your gallery 98 | plot_tutorial.py # a tutorial 99 | plot_tutorial-2.py # a tutorial that produces a plot output 100 | _build/ 101 | build_examples/ # This is where the downloadable tutorial files live 102 | plot_sample-1.ipynb 103 | plot_sample-1.py 104 | ... 105 | html/ 106 | built_examples/ # You can specify this dir name in gallery settings 107 | index.html 108 | plot_sample-1.html 109 | plot_sample.html 110 | sg_execution_times.html # in case you want to see build times for each tutorial 111 | 112 | ``` 113 | 114 | ### [nbsphinx - tutorials using Jupyter Notebooks](https://nbsphinx.readthedocs.io/en/latest/) 115 | 116 | If you prefer to use Jupyter Notebooks to create tutorials you can use nbsphinx. 117 | nbsphinx operates similarly to Sphinx gallery in that: 118 | 119 | * It runs your notebooks and produces outputs in the rendered tutorials 120 | 121 | * Pro/con By default it does not support downloading of **.py** and **.ipynb** files. However you can add a [link to the notebook at the top of the page with 122 | some additional conf.py settings (see: epilog settings)](https://nbsphinx.readthedocs.io/en/0.8.10/prolog-and-epilog.html) 123 | 124 | 125 | ```{figure} /images/python-package-documentation-nb_sphinx-gallery-output.png 126 | --- 127 | name: directive-fig 128 | width: 80% 129 | alt: Image showing the gallery output provided by nbsphinx using the sphinx-gallery front end interface. 130 | --- 131 | `nbsphinx` can be combined with Sphinx gallery to create a gallery of tutorials. 132 | However, rather than rendering the gallery as a grid, it lists all of the gallery 133 | elements in a single column. 134 | ``` 135 | 136 | ```bash 137 | tutorials/ 138 | index.md # Landing page for your gallery 139 | tutorial.ipynb # A tutorial in a jupyter notebook 140 | another_tutorial.ipynb 141 | # This shows you what the build directory looks like when you build with sphinx-build 142 | _build/ 143 | html/ 144 | # Notice that nbsphinx runs each notebook and produces an 145 | # html file with all of the outputs of your code 146 | # you can link to the notebook in your docs by modifying 147 | # the nbsphinx build - we will cover this in a separate tutorial series focused onPythonpackaging! 148 | tutorials/ 149 | index.html 150 | index.md 151 | plot_sample-2.html 152 | plot_sample-2.ipynb 153 | ... 154 | ``` 155 | -------------------------------------------------------------------------------- /documentation/write-user-documentation/get-started.md: -------------------------------------------------------------------------------- 1 | # Create User Facing Documentation for your Python Package 2 | 3 | 13 | 14 | ## Core components of user-facing Python package documentation 15 | Below we break documentation into two broad types. 16 | 17 | **User-facing documentation** refers to documentation that describes the way the 18 | tools within a package are broadly used in workflows. **API documentation** refers 19 | to documentation of functions, classes, methods, and attributes in your code and 20 | is written at a more granular level. This documentation is what a user sees when 21 | they type `help(function-name)`. 22 | 23 | Your user-facing documentation for your Python package should include several 24 | core components. 25 | 26 | * **Documentation Website:** This refers to easy-to-read documentation that helps someone use your package. This documentation should help users both install and use your package. 27 | * **Short Tutorials:** Your user-facing documentation should also include [**short tutorials** that showcase core features of your package](create-package-tutorials). 28 | * **Package Code / API documentation:** You package's functions, classes, methods, and attributes (the API) should also be documented. API documentation can be generated from [docstrings](https://pandas.pydata.org/docs/development/contributing_docstring.html) found in your 29 | code. Ideally, you have docstrings for all user-facing functions, classes, and methods in 30 | your Python package. [We discuss code documentation and docstrings in greater detail here.](document-your-code-api-docstrings) 31 | 32 | ### Write usable documentation 33 | 34 | User-facing documentation should be published on a 35 | easy-to-navigate website. The documentation should be written keeping in mind that users may not be developers or expert-level programmers. Rather, the language 36 | that you use in your documentation should not be 37 | highly technical. 38 | 39 | To make the language of your documentation more accessible 40 | to a broader audience: 41 | 42 | * Whenever possible, define technical terms and jargon. 43 | * Consider writing instructions for a high-school level reader. 44 | * Include step-by-step code examples, tutorials or vignettes that support getting started using your package. 45 | 46 | ## Four elements of a good open source documentation landing page 47 | 48 | To make it easy for users to find what they need quickly, 49 | consider adding quick links on your package's landing 50 | page to the following elements: 51 | 52 | * **Getting started:** This section should provide the user with a quick start for installing your package. A small example of how to use the package is good to have here as well. Or you can link to useful tutorials in the get started section. 53 | * **About:** Describe your project, stating its goals and its functionality. 54 | * **Community:** Instructions for how to help and/or get involved. This might include links to your issues (if that is where you let users ask questions) or the discussion part of your GitHub repo. This section might include a development guide for those who might contribute to your package. 55 | * **API Documentation:** This is the detailed project documentation. Here you store documentation for your package's API including all user-facing functions, classes, methods, and attributes as well as any additional high level discussion that will help people use your package. 56 | 57 | 58 | ```{figure} /images/geopandas-documentation-landing-page.png 59 | --- 60 | name: geopandas-landing 61 | width: 80% 62 | alt: Image showing the landing page for GeoPandas documentation which has 4 sections including Getting started, Documentation, About GeoPandas, Community. 63 | --- 64 | The documentation landing page of GeoPandas, a spatial Python library, has the 4 element specified above. Notice that the landing page is simple and directs users to each element using a Sphinx card. 65 | ``` 66 | 67 | NOTE: in many cases you can include your **README** file and your **CONTRIBUTING** files 68 | in your documentation given those files may have some of the components listed above. 69 | 70 | `````{tip} 71 | You can include files in Sphinx using the include directive. 72 | Below is an example of doing this using `myst` syntax. 73 | ```` 74 | ```{include} ../README.md 75 | ``` 76 | ```` 77 | ````` 78 | -------------------------------------------------------------------------------- /documentation/write-user-documentation/intro.md: -------------------------------------------------------------------------------- 1 | # Writing user-facing documentation for your Python package 2 | 3 | This section walks you through best practices for with writing 4 | documentation for your Python package. 5 | 6 | We talk about the elements that you should consider adding to your documentation, 7 | the different types of users who might read your documentation and how to 8 | create tutorials for your package. 9 | 10 | Here we also cover sphinx extensions that you can user to make documentation 11 | easier such as: 12 | 13 | * autodoc to automagically populate documentation for your code's functions, 14 | classes, methods and attributes (API documentation) and 15 | * sphinx gallery for tutorials. 16 | -------------------------------------------------------------------------------- /examples/extension-hatch/README: -------------------------------------------------------------------------------- 1 | An example Python package used to support Python packaging tutorials 2 | 3 | This project demonstrates mixing C and Python code by using the frontend hatch with the backend mesonpy 4 | -------------------------------------------------------------------------------- /examples/extension-hatch/examplePy/__init__.py: -------------------------------------------------------------------------------- 1 | from .temperature import celsius_to_fahrenheit, fahrenheit_to_celsius 2 | 3 | __all__ = ["celsius_to_fahrenheit", "fahrenheit_to_celsius"] 4 | __version__ = "0.1.0dev0" 5 | -------------------------------------------------------------------------------- /examples/extension-hatch/examplePy/meson.build: -------------------------------------------------------------------------------- 1 | py.extension_module( 2 | 'temperature', 3 | 'temperature.c', 4 | install: true, 5 | subdir: 'examplePy' 6 | ) 7 | 8 | python_sources = [ 9 | '__init__.py' 10 | ] 11 | 12 | py.install_sources( 13 | python_sources, 14 | subdir: 'examplePy' 15 | ) 16 | -------------------------------------------------------------------------------- /examples/extension-hatch/examplePy/temperature.c: -------------------------------------------------------------------------------- 1 | #define PY_SSIZE_T_CLEAN 2 | #include 3 | 4 | static PyObject * 5 | temperature_celsius_to_fahrenheit(PyObject *self, PyObject *args) 6 | { 7 | long celsius; 8 | long fahrenheit; 9 | PyObject *ret; 10 | 11 | if (!PyArg_ParseTuple(args, "l", &celsius)) 12 | return NULL; 13 | 14 | fahrenheit = (celsius * 9/5) + 32; 15 | 16 | ret = PyLong_FromLong(fahrenheit); 17 | Py_INCREF(ret); 18 | return ret; 19 | } 20 | 21 | static PyObject * 22 | temperature_fahrenheit_to_celsius(PyObject *self, PyObject *args) 23 | { 24 | long fahrenheit; 25 | long celsius; 26 | PyObject *ret; 27 | 28 | if (!PyArg_ParseTuple(args, "l", &fahrenheit)) 29 | return NULL; 30 | 31 | celsius = (fahrenheit - 32) * 9/5; 32 | 33 | ret = PyLong_FromLong(celsius); 34 | Py_INCREF(ret); 35 | return ret; 36 | } 37 | 38 | static PyMethodDef CoreMethods[] = { 39 | {"celsius_to_fahrenheit", temperature_celsius_to_fahrenheit, METH_VARARGS, "Convert temperature from Celsius to Fahrenheit"}, 40 | {"fahrenheit_to_celsius", temperature_fahrenheit_to_celsius, METH_VARARGS, "Convert temperature from Fahrenheit to Celsius"}, 41 | {NULL, NULL, 0, NULL} /* Sentinel */ 42 | }; 43 | 44 | static struct PyModuleDef temperaturemodule = { 45 | PyModuleDef_HEAD_INIT, 46 | "temperature", /* name of module */ 47 | NULL, /* module documentation, may be NULL */ 48 | -1, /* size of per-interpreter state of the module, 49 | or -1 if the module keeps state in global variables. */ 50 | CoreMethods 51 | }; 52 | 53 | PyMODINIT_FUNC 54 | PyInit_temperature(void) 55 | { 56 | PyObject *m; 57 | 58 | m = PyModule_Create(&temperaturemodule); 59 | if (m == NULL) 60 | return NULL; 61 | 62 | return m; 63 | } 64 | -------------------------------------------------------------------------------- /examples/extension-hatch/meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'examplePy', 3 | 'c', 4 | version: '0.1.dev0', 5 | license: 'MIT', 6 | meson_version: '>= 0.64.0', 7 | default_options: [ 8 | 'buildtype=debugoptimized', 9 | 'c_std=c99', 10 | 'cpp_std=c++14', 11 | ], 12 | ) 13 | 14 | cc = meson.get_compiler('c') 15 | 16 | py_mod = import('python') 17 | py = py_mod.find_installation(pure: false) 18 | py_dep = py.dependency() 19 | 20 | subdir('examplePy') 21 | -------------------------------------------------------------------------------- /examples/extension-hatch/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "mesonpy" 3 | requires = [ 4 | "meson-python>=0.13.0rc0", 5 | ] 6 | 7 | [project] 8 | name = "examplePy" 9 | version = "0.1" 10 | authors = [ 11 | {name = "Some Maintainer", email = "some-email@pyopensci.org"}, 12 | ] 13 | maintainers = [ 14 | {name = "All the contributors"}, 15 | ] 16 | description = "An example Python package used to support Python packaging tutorials" 17 | keywords = ["pyOpenSci", "python packaging"] 18 | readme = "README" 19 | classifiers = [ 20 | "Programming Language :: Python :: 3", 21 | "License :: OSI Approved :: BSD License", 22 | "Operating System :: OS Independent", 23 | ] 24 | -------------------------------------------------------------------------------- /examples/pure-hatch/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/PyCQA/isort 3 | rev: 5.11.4 4 | hooks: 5 | - id: isort 6 | files: \.py$ 7 | # Misc commit checks using built in pre-commit checks 8 | - repo: https://github.com/pre-commit/pre-commit-hooks 9 | rev: v4.4.0 10 | # ref: https://github.com/pre-commit/pre-commit-hooks#hooks-available 11 | hooks: 12 | # Autoformat: Makes sure files end in a newline and only a newline. 13 | - id: end-of-file-fixer 14 | # Lint: Check for files with names that would conflict on a 15 | # case-insensitive filesystem like MacOS HFS+ or Windows FAT. 16 | - id: check-case-conflict 17 | - id: trailing-whitespace 18 | # Linting: Python code (see the file .flake8) 19 | - repo: https://github.com/PyCQA/flake8 20 | rev: "6.0.0" 21 | hooks: 22 | - id: flake8 23 | # Black for auto code formatting 24 | - repo: https://github.com/psf/black 25 | rev: 22.12.0 26 | hooks: 27 | - id: black 28 | language_version: python3.8 29 | # Tell precommit.ci bot to update codoe format tools listed in the file 30 | # versions every quarter 31 | # The default it so update weekly which is too many new pr's for many 32 | # maintainers (remove these lines if you aren't using the bot!) 33 | ci: 34 | autoupdate_schedule: quarterly 35 | -------------------------------------------------------------------------------- /examples/pure-hatch/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "examplePy" 7 | authors = [ 8 | {name = "Some Maintainer", email = "some-email@pyopensci.org"}, 9 | ] 10 | maintainers = [ 11 | {name = "All the contributors"}, 12 | ] 13 | description = "An example Python package used to support Python packaging tutorials" 14 | keywords = ["pyOpenSci", "python packaging"] 15 | readme = "README.md" 16 | classifiers = [ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: BSD License", 19 | "Operating System :: OS Independent", 20 | ] 21 | dependencies = [ 22 | "dependency-package-name-1", 23 | "dependency-package-name-2", 24 | ] 25 | 26 | [project.optional-dependencies] 27 | tests = [ 28 | "pytest", 29 | "pytest-cov" 30 | ] 31 | lint = [ 32 | "black", 33 | "flake8" 34 | ] 35 | docs = [ 36 | "sphinx", 37 | "pydata-sphinx-theme" 38 | ] 39 | 40 | [tool.ruff] 41 | select = [ 42 | "E", # pycodestyle errors 43 | "W", # pycodestyle warnings 44 | "F", # pyflakes. "E" + "W" + "F" + "C90" (mccabe complexity) is equivalent to flake8 45 | "I", # isort 46 | ] 47 | 48 | [tool.ruff.isort] 49 | known-first-party = ["examplePy"] 50 | -------------------------------------------------------------------------------- /examples/pure-hatch/src/examplePy/temperature.py: -------------------------------------------------------------------------------- 1 | def celsius_to_fahrenheit(celsius): 2 | """ 3 | Convert temperature from Celsius to Fahrenheit. 4 | 5 | Parameters: 6 | celsius (float): Temperature in Celsius. 7 | 8 | Returns: 9 | float: Temperature in Fahrenheit. 10 | """ 11 | fahrenheit = (celsius * 9/5) + 32 12 | return fahrenheit 13 | 14 | 15 | def fahrenheit_to_celsius(fahrenheit): 16 | """ 17 | Convert temperature from Fahrenheit to Celsius. 18 | 19 | Parameters: 20 | fahrenheit (float): Temperature in Fahrenheit. 21 | 22 | Returns: 23 | float: Temperature in Celsius. 24 | """ 25 | celsius = (fahrenheit - 32) * 5/9 26 | return celsius 27 | -------------------------------------------------------------------------------- /examples/pure-hatch/src/examplePy/temporal-raw.py: -------------------------------------------------------------------------------- 1 | from examplePy.temperature import fahrenheit_to_celsius 2 | import pandas 3 | from typing import Sequence 4 | 5 | def calc_annual_mean(df: pandas.DataFrame): 6 | """Function to calculate the mean temperature for each year and the final mean""" 7 | # TODO: make this a bit more robust so we can write integration test examples?? 8 | # Calculate the mean temperature for each year 9 | yearly_means = df.groupby('Year').mean() 10 | 11 | # Calculate the final mean temperature across all years 12 | final_mean = yearly_means.mean() 13 | 14 | # Return a converted value 15 | return fahrenheit_to_celsius(yearly_means), fahrenheit_to_celsius(final_mean) 16 | -------------------------------------------------------------------------------- /examples/pure-hatch/src/examplePy/temporal.py: -------------------------------------------------------------------------------- 1 | from typing import Sequence 2 | 3 | import pandas 4 | 5 | from examplePy.temperature import fahrenheit_to_celsius 6 | 7 | def calc_annual_mean(df: pandas.DataFrame): 8 | """Function to calculate the mean temperature for each year and the final mean""" 9 | # TODO: make this a bit more robust so we can write integration test examples?? 10 | # Calculate the mean temperature for each year 11 | yearly_means = df.groupby('Year').mean() 12 | 13 | # Calculate the final mean temperature across all years 14 | final_mean = yearly_means.mean() 15 | 16 | # Return a converted value 17 | return fahrenheit_to_celsius(yearly_means), fahrenheit_to_celsius(final_mean) 18 | -------------------------------------------------------------------------------- /examples/pure-setuptools/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "examplePy" 7 | authors = [ 8 | {name = "Some Maintainer", email = "some-email@pyopensci.org"}, 9 | ] 10 | maintainers = [ 11 | {name = "All the contributors"}, 12 | ] 13 | description = "An example Python package used to support Python packaging tutorials" 14 | keywords = ["pyOpenSci", "python packaging"] 15 | readme = "README.md" 16 | classifiers = [ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: BSD License", 19 | "Operating System :: OS Independent", 20 | ] 21 | dependencies = [ 22 | "dependency-package-name-1", 23 | "dependency-package-name-2", 24 | ] 25 | -------------------------------------------------------------------------------- /images/code-cov-stravalib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/code-cov-stravalib.png -------------------------------------------------------------------------------- /images/conda-channels-geohackweek.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/conda-channels-geohackweek.jpeg -------------------------------------------------------------------------------- /images/conda-forge-staged-recipes-ci.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/conda-forge-staged-recipes-ci.png -------------------------------------------------------------------------------- /images/contributing/clone-repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/contributing/clone-repository.png -------------------------------------------------------------------------------- /images/contributing/commit-changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/contributing/commit-changes.png -------------------------------------------------------------------------------- /images/contributing/edit-button-pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/contributing/edit-button-pencil.png -------------------------------------------------------------------------------- /images/contributing/edit-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/contributing/edit-file.png -------------------------------------------------------------------------------- /images/contributing/new-pull-request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/contributing/new-pull-request.png -------------------------------------------------------------------------------- /images/contributing/preview-changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/contributing/preview-changes.png -------------------------------------------------------------------------------- /images/contributing/pull-requests-checks-fails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/contributing/pull-requests-checks-fails.png -------------------------------------------------------------------------------- /images/contributing/pull-requests-checks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/contributing/pull-requests-checks.png -------------------------------------------------------------------------------- /images/contributing/pull-requests-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/contributing/pull-requests-tab.png -------------------------------------------------------------------------------- /images/flower-puzzle-pyopensci.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/flower-puzzle-pyopensci.jpg -------------------------------------------------------------------------------- /images/geopandas-documentation-landing-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/geopandas-documentation-landing-page.png -------------------------------------------------------------------------------- /images/license-github-root-dir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/license-github-root-dir.png -------------------------------------------------------------------------------- /images/moving-pandas-python-package-github-community-standards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/moving-pandas-python-package-github-community-standards.png -------------------------------------------------------------------------------- /images/moving-pandas-python-package-github-main-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/moving-pandas-python-package-github-main-repo.png -------------------------------------------------------------------------------- /images/moving-pandas-python-package-snyk-health.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/moving-pandas-python-package-snyk-health.png -------------------------------------------------------------------------------- /images/packaging-lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/packaging-lifecycle.png -------------------------------------------------------------------------------- /images/pandera-python-package-readme-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/pandera-python-package-readme-github.png -------------------------------------------------------------------------------- /images/precommit-hook-python-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/precommit-hook-python-code.png -------------------------------------------------------------------------------- /images/publish-python-package-pypi-conda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/publish-python-package-pypi-conda.png -------------------------------------------------------------------------------- /images/pyopensci-puzzle-pieces-tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/pyopensci-puzzle-pieces-tests.png -------------------------------------------------------------------------------- /images/pyopensci-python-package-pypi-to-conda-forge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/pyopensci-python-package-pypi-to-conda-forge.png -------------------------------------------------------------------------------- /images/pypi-project-landing-page-no-meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/pypi-project-landing-page-no-meta.png -------------------------------------------------------------------------------- /images/python-build-package/pypi-metadata-classifiers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-build-package/pypi-metadata-classifiers.png -------------------------------------------------------------------------------- /images/python-build-package/pypi-metadata-keywords-license.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-build-package/pypi-metadata-keywords-license.png -------------------------------------------------------------------------------- /images/python-build-package/pypi-metadata-maintainers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-build-package/pypi-metadata-maintainers.png -------------------------------------------------------------------------------- /images/python-dependency-conflicts-xkcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-dependency-conflicts-xkcd.png -------------------------------------------------------------------------------- /images/python-flying-xkcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-flying-xkcd.png -------------------------------------------------------------------------------- /images/python-package-dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-package-dependencies.png -------------------------------------------------------------------------------- /images/python-package-dependency-types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-package-dependency-types.png -------------------------------------------------------------------------------- /images/python-package-development-process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-package-development-process.png -------------------------------------------------------------------------------- /images/python-package-documentation-nb_sphinx-gallery-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-package-documentation-nb_sphinx-gallery-output.png -------------------------------------------------------------------------------- /images/python-package-tools-2022-survey-pypa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-package-tools-2022-survey-pypa.png -------------------------------------------------------------------------------- /images/python-package-tools-decision-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-package-tools-decision-tree.png -------------------------------------------------------------------------------- /images/python-pypi-conda-channels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-pypi-conda-channels.png -------------------------------------------------------------------------------- /images/python-tests-puzzle-fit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-tests-puzzle-fit.png -------------------------------------------------------------------------------- /images/python-tests-puzzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/python-tests-puzzle.png -------------------------------------------------------------------------------- /images/sphinx-gallery-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/sphinx-gallery-overview.png -------------------------------------------------------------------------------- /images/sphinx-gallery-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/sphinx-gallery-tutorial.png -------------------------------------------------------------------------------- /images/sphinx-rendering-extent-to-json-earthpy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/sphinx-rendering-extent-to-json-earthpy.png -------------------------------------------------------------------------------- /images/tutorials/code-to-python-package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/code-to-python-package.png -------------------------------------------------------------------------------- /images/tutorials/environment-package-install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/environment-package-install.png -------------------------------------------------------------------------------- /images/tutorials/github-new-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/github-new-repo.png -------------------------------------------------------------------------------- /images/tutorials/package-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/package-components.png -------------------------------------------------------------------------------- /images/tutorials/packaging-101-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/packaging-101-outline.png -------------------------------------------------------------------------------- /images/tutorials/packaging-elements.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/packaging-elements.png -------------------------------------------------------------------------------- /images/tutorials/packaging-lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/packaging-lifecycle.png -------------------------------------------------------------------------------- /images/tutorials/publish-package-pypi-conda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/publish-package-pypi-conda.png -------------------------------------------------------------------------------- /images/tutorials/test-pypi-package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/test-pypi-package.png -------------------------------------------------------------------------------- /images/tutorials/testpypi-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/testpypi-search.png -------------------------------------------------------------------------------- /images/tutorials/toolbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/toolbox.png -------------------------------------------------------------------------------- /images/tutorials/view-license-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/images/tutorials/view-license-github.png -------------------------------------------------------------------------------- /locales/es/LC_MESSAGES/continuous-integration.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2025, pyOpenSci 3 | # This file is distributed under the same license as the pyOpenSci Python 4 | # Package Guide package. 5 | # FIRST AUTHOR , 2025. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: pyOpenSci Python Package Guide \n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2025-01-18 13:00-0500\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language: es\n" 16 | "Language-Team: es \n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=utf-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Generated-By: Babel 2.16.0\n" 22 | 23 | #: ../../continuous-integration/ci.md:2 24 | msgid "" 25 | "Continuous Integration and Continuous Deployment (CI/CD) For Python " 26 | "Packages" 27 | msgstr "" 28 | 29 | #: ../../continuous-integration/ci.md:4 30 | msgid "" 31 | "When you develop, work on, and contribute to software, there is more to " 32 | "consider than just writing code. Having tests and checks ensures that " 33 | "your code runs reliably and follows a consistent format is also " 34 | "important. You can use **Continuous Integration (CI)** and **Continuous " 35 | "Deployment (CD)** to run tests and checks on your code every time someone" 36 | " suggests a change online in a platform like GitHub or GitLab." 37 | msgstr "" 38 | 39 | #: ../../continuous-integration/ci.md:11 40 | msgid "" 41 | "**Continuous Integration (CI):** Automates the process of running tests, " 42 | "code checks, and other workflows each time code is updated." 43 | msgstr "" 44 | 45 | #: ../../continuous-integration/ci.md:13 46 | msgid "" 47 | "**Continuous Deployment (CD):** Extends CI by allowing you to automate " 48 | "publishing your package to PyPI, publishing your documentation, and more." 49 | msgstr "" 50 | 51 | #: ../../continuous-integration/ci.md:15 52 | msgid "" 53 | "CI and CD streamline software development by automating repetitive tasks " 54 | "and ensuring code quality and consistency. Having CI setup also makes it " 55 | "easier for new contributors to contribute to your code base without " 56 | "setting up all your test suites and other local checks." 57 | msgstr "" 58 | 59 | #: ../../continuous-integration/ci.md:20 60 | msgid "What is continuous integration?" 61 | msgstr "" 62 | 63 | #: ../../continuous-integration/ci.md:22 64 | msgid "" 65 | "When you’re ready to publish your code online, you can set up Continuous " 66 | "Integration (CI). CI is a platform that allows you to specify and run " 67 | "jobs or workflows you define. These workflows include:" 68 | msgstr "" 69 | 70 | #: ../../continuous-integration/ci.md:25 71 | msgid "Running your test suite" 72 | msgstr "" 73 | 74 | #: ../../continuous-integration/ci.md:26 75 | msgid "Running code checkers / linters / spellcheck" 76 | msgstr "" 77 | 78 | #: ../../continuous-integration/ci.md:27 79 | msgid "Building your documentation" 80 | msgstr "" 81 | 82 | #: ../../continuous-integration/ci.md:29 83 | msgid "" 84 | "CI allows you to automate running workflows across a suite of " 85 | "environments, including:" 86 | msgstr "" 87 | 88 | #: ../../continuous-integration/ci.md:31 89 | msgid "environments containing different Python versions and" 90 | msgstr "" 91 | 92 | #: ../../continuous-integration/ci.md:32 93 | msgid "different operating systems (Mac, Linux, Windows)." 94 | msgstr "" 95 | 96 | #: ../../continuous-integration/ci.md:34 97 | msgid "What is Continuous Deployment (CD)?" 98 | msgstr "" 99 | 100 | #: ../../continuous-integration/ci.md:36 101 | msgid "" 102 | "Continuous deployment (CD) extends the CI process by automating the " 103 | "deployment of code changes to production or staging environments. In the " 104 | "case of your open source tool, CD can be used to:" 105 | msgstr "" 106 | 107 | #: ../../continuous-integration/ci.md:38 108 | msgid "Automate publishing to PyPI" 109 | msgstr "" 110 | 111 | #: ../../continuous-integration/ci.md:39 112 | msgid "Automate publishing your documentation to GitHub Pages or Read the Docs." 113 | msgstr "" 114 | 115 | #: ../../continuous-integration/ci.md:41 116 | msgid "" 117 | "It is also used once your conda-forge recipe is set up to keep your " 118 | "package up to date on conda-forge." 119 | msgstr "" 120 | 121 | #: ../../continuous-integration/ci.md:43 122 | msgid "Why use CI" 123 | msgstr "" 124 | 125 | #: ../../continuous-integration/ci.md:45 126 | msgid "" 127 | "CI can be configured to run a workflow on every commit pushed to GitHub " 128 | "and every pull request opened. This ensures that any changes made to your" 129 | " package are tested across environments before merging into the main " 130 | "branch of your code." 131 | msgstr "" 132 | 133 | #: ../../continuous-integration/ci.md:47 134 | msgid "" 135 | "These checks are particularly useful if someone new is contributing to " 136 | "your code. Every contributor's change will be tested when pushed to your " 137 | "code repository." 138 | msgstr "" 139 | 140 | #: ../../continuous-integration/ci.md:49 141 | msgid "" 142 | "Together, CI and CD streamline the process of building, testing, and " 143 | "deploying code. They aim to improve software development and publication " 144 | "efficiency, quality, and reliability." 145 | msgstr "" 146 | 147 | #: ../../continuous-integration/ci.md:52 148 | msgid "" 149 | "All pyOpenSci packages must use some form of continuous integration. Even" 150 | " if you are not planning to go through peer review, we strongly recommend" 151 | " that you use continuous integration, too!" 152 | msgstr "" 153 | 154 | #: ../../continuous-integration/ci.md:55 155 | msgid "" 156 | "In the case of GitHub actions (which we will focus on here), CI workflows" 157 | " are running on online servers that support GitHub." 158 | msgstr "" 159 | 160 | #: ../../continuous-integration/ci.md:57 161 | msgid "CI / CD platforms" 162 | msgstr "" 163 | 164 | #: ../../continuous-integration/ci.md:59 165 | msgid "" 166 | "There are numerous platforms available for CI/CD. Here, we will focus on " 167 | "GitHub Actions (GHA), built into GitHub. GitHub is the most commonly used" 168 | " platform to store scientific open-source software." 169 | msgstr "" 170 | 171 | #: ../../continuous-integration/ci.md:62 172 | msgid "" 173 | "If you use [GitLab](https://about.gitlab.com/) CI/CD, many of the " 174 | "principles described here will apply. However, the workflow files may " 175 | "look different." 176 | msgstr "" 177 | 178 | #: ../../continuous-integration/ci.md:65 179 | msgid "If you aren't sure, use GitHub Actions" 180 | msgstr "" 181 | 182 | #: ../../continuous-integration/ci.md:67 183 | msgid "" 184 | "While you are welcome to use the continuous integration platform of your " 185 | "choice, we recommend GitHub Actions because it is free-to-use and " 186 | "integrated tightly into the GitHub user interface. There is also an " 187 | "entire store of GitHub action templates that you can easily use and adapt" 188 | " to your own needs." 189 | msgstr "" 190 | 191 | #: ../../continuous-integration/ci.md:72 192 | msgid "Other platforms that you may run into" 193 | msgstr "" 194 | 195 | #: ../../continuous-integration/ci.md:75 196 | msgid "" 197 | "[Appveyor:](https://www.appveyor.com/): Supports running tests on Windows" 198 | " operating systems and predated the release of GitHub Actions. Today, " 199 | "AppVeyor supports operating systems beyond Windows." 200 | msgstr "" 201 | 202 | #: ../../continuous-integration/ci.md:76 203 | msgid "" 204 | "[Travis CI:](https://www.travis-ci.com/) had been a common CI platform " 205 | "choice in our ecosystem. Usage dropped after Travis CI ended free support" 206 | " for open-source projects." 207 | msgstr "" 208 | 209 | #: ../../continuous-integration/ci.md:77 210 | msgid "" 211 | "[CircleCI:](https://circleci.com/) CircleCI can be useful for automated " 212 | "builds of websites and documentation since it offers a preview of the PR " 213 | "changes." 214 | msgstr "" 215 | 216 | #: ../../continuous-integration/ci.md:80 217 | msgid "Embrace automation" 218 | msgstr "" 219 | 220 | #: ../../continuous-integration/ci.md:82 221 | msgid "" 222 | "By embracing CI/CD, you can ensure that your code runs as you expect it " 223 | "to across the diverse landscapes of user environments. Further, you can " 224 | "automate certain checks (and, in some cases, code fixes), including " 225 | "linting and code style. You can even automate spell-checking your " 226 | "documentation and docstrings!" 227 | msgstr "" 228 | 229 | #: ../../continuous-integration/index.md:5 230 | msgid "What is CI?" 231 | msgstr "" 232 | 233 | #: ../../continuous-integration/index.md:5 234 | msgid "Continuous Integration" 235 | msgstr "" 236 | 237 | #: ../../continuous-integration/index.md:2 238 | msgid "" 239 | "Continuous Integration (CI) and Continuous Deployment (CD) for your " 240 | "Python package" 241 | msgstr "" 242 | -------------------------------------------------------------------------------- /package-structure-code/complex-python-package-builds.md: -------------------------------------------------------------------------------- 1 | # Complex Python package builds 2 | 3 | This guide is focused on packages that are either pure-python or that 4 | have a few simple extensions in another language such as C or C++. 5 | 6 | In the future, we want to provide resources for packaging workflows that require more complex builds. If you have questions about these types of package, please [add a question to our discourse](https://pyopensci.discourse.group/) or open an [issue about this guide specifically in the GitHub repo for this guide](https://github.com/pyOpenSci/python-package-guide/issues). There are many nuances to building and distributing Python packages that have compiled extensions requiring non-Python dependencies at build time. For an overview and thorough discussion of these nuances, please see [this site.](https://pypackaging-native.github.io/) 7 | 8 | ## Pure Python Packages vs. packages with extensions in other languages 9 | 10 | You can classify Python package complexity into three general categories. These 11 | categories can in turn help you select the correct package frontend and 12 | backend tools. 13 | 14 | 1. **Pure-python packages:** these are packages that only rely on Python to function. Building a pure Python package is simpler. As such, you can chose a tool below that has the features that you want and be done with your decision! 15 | 16 | 2. **Python packages with non-Python extensions:** These packages have additional components called extensions written in other languages (such as C or C++). If you have a package with non-Python extensions, then you need to select a build backend tool that allows additional build steps needed to compile your extension code. Further, if you wish to use a frontend tool to support your workflow, you will need to select a tool that supports additional build setups. We suggest that you chose build tool that supports custom build steps like Hatch. 17 | 18 | 3. **Python packages that have extensions written in different languages (e.g. Fortran and C++) or that have non Python dependencies that are difficult to install (e.g. GDAL):** These packages often have complex build steps (more complex than a package with just a few C extensions for instance). As such, these packages require tools such as [scikit-build](https://scikit-build.readthedocs.io/en/latest/) 19 | or [meson-python](https://mesonbuild.com/Python-module.html) to build. NOTE: you can use meson-python with PDM. 20 | 21 | ## Mixing frontend and backend projects 22 | 23 | It is sometimes necessary or desirable to use a build frontend with an alternative build-backend. 24 | This is because some frontends do not have a default backend (`build`), and this choice is placed on the maintainer. 25 | Other backends (`hatch`) have a preferred backend (`hatchling`) but allow the maintainer to migrate to another, while 26 | some backends (`poetry`) only work with a single backend (`poetry-core`). Refer to (#python-package-build-tools) for 27 | more information about frontend and backend compatibility. 28 | 29 | In this packaging guide we recommend using `hatch` along with its preferred backend `hatchling`. While this will be 30 | suitable for most packages, an alternate backend may be used with Hatch if needed when creating an extension module. 31 | A Python extension module is one that is made up, either in part or entirely, of compiled code. In this case the 32 | backend chosen (such as `meson-python`) must know how to compile the extension language and bind it to Python. 33 | `hatchling` does not know how to do this all on its own and must either make use of 34 | [plugins](https://hatch.pypa.io/1.9/plugins/about/) or be replaced by a backend that is already capable of building 35 | extension modules. 36 | 37 | In order to use a different backend you will need to edit your project's `pyproject.toml`. If you have a 38 | `pyproject.toml` generated by the `hatch` command, or from following the packaging tutorial, you may have 39 | to make a change like this 40 | 41 | ```diff 42 | [build-system] 43 | -requires = ["hatchling"] 44 | +requires = ["meson-python"] 45 | -build-backend = "hatchling.build" 46 | +build-backend = "mesonpy" 47 | ``` 48 | -------------------------------------------------------------------------------- /package-structure-code/intro.md: -------------------------------------------------------------------------------- 1 | # Python Package Structure 2 | 3 | This section provides guidance on your Python package's structure, code format, 4 | and style. It also reviews the various [packaging tools](python-package-build-tools) you can use to 5 | [build](python-package-distribution-files-sdist-wheel) and [publish](publish-python-package-pypi-conda) your Python package. 6 | 7 | If you want end-to-end tutorials, check out our tutorial series that starts by introducing [what a Python package is](what-is-a-package). 8 | 9 | If you are confused by Python packaging, you are not alone! The good news is 10 | that some great modern packaging tools ensure you follow 11 | best practices. Here, we review tool features and suggest tools you can use 12 | for your Python packaging workflow. 13 | 14 | :::{button-link} /tutorials/intro 15 | :color: success 16 | :class: sd-rounded-pill float-left 17 | 18 | Checkout our beginning-to-end create a Python package tutorials 19 | 20 | ::: 21 | 22 | 23 | :::{admonition} How this content is developed 24 | All of the content in this guide has been vetted by community members, including maintainers and developers of the core packaging tools. 25 | ::: 26 | 27 | 28 | :::::{grid} 1 1 2 2 29 | :class-container: text-center 30 | :gutter: 3 31 | 32 | ::::{grid-item} 33 | :::{card} ✨ 1. Package file structure ✨ 34 | :link: python-package-structure 35 | :link-type: doc 36 | 37 | src layout, flat layout and where should tests folders live? No matter what your level of packaging knowledge is, this page will help you decide upon a package structure that follows modern python best practices. 38 | ::: 39 | :::: 40 | 41 | ::::{grid-item} 42 | :::{card} ✨ 2. Learn about building your package ✨ 43 | :link: python-package-structure 44 | :link-type: doc 45 | 46 | To publish your Python package on PyPI, you will need to first build it. The act 47 | of "building" refers to the process of placing your package code and 48 | metadata into a format that can be published on PyPI. Learn more about building 49 | your Python package. 50 | ::: 51 | :::: 52 | 53 | 54 | ::::{grid-item} 55 | :::{card} ✨ 4. Add metadata ✨ 56 | :link: pyproject-toml-python-package-metadata 57 | :link-type: doc 58 | 59 | Learn how to add project metadata to your Python package to support both 60 | filtering on PyPI and also the metadata that a package installer needs to 61 | build and install your package. 62 | ::: 63 | :::: 64 | 65 | ::::{grid-item} 66 | :::{card} ✨ 3. What Python package tool should you use? ✨ 67 | :link: python-package-build-tools 68 | :link-type: doc 69 | 70 | Learn more about the suite of packaging tools out there. 71 | And learn which tool might be best for you. 72 | ::: 73 | :::: 74 | 75 | ::::{grid-item} 76 | :::{card} ✨ 4. Publish to PyPI and Conda ✨ 77 | :link: python-package-build-tools 78 | :link-type: doc 79 | 80 | If you have a pure Python package, it's a straight forward 81 | process to publish to both PyPI and then a Conda channel such as 82 | conda-forge. Learn more here. 83 | ::: 84 | :::: 85 | 86 | ::::{grid-item} 87 | :::{card} ✨ 5. Setup package versioning ✨ 88 | :link: python-package-versions 89 | :link-type: doc 90 | 91 | Semver (numeric versioning) and Calver (versioning using the date) are 2 92 | common ways to version a package. Which one should you pick? Learn more here. 93 | ::: 94 | :::: 95 | 96 | ::::{grid-item} 97 | :::{card} ✨ 6. Code style & linters ✨ 98 | :link: code-style-linting-format 99 | :link-type: doc 100 | 101 | Black, blue, flake8, Ruff - which tools can help you ensure your 102 | package follows best practices for code format? Learn more about the options 103 | and why this is important here. 104 | ::: 105 | :::: 106 | 107 | ::::: 108 | 109 | :::{figure-md} packaging-tools-decision-tree 110 | 111 | Figure showing a decision tree with the various packaging tool front-end and back-end options. 112 | 113 | Diagram showing the various front-end build tools that you can select from. 114 | See the packaging tools page to learn more about each tool. 115 | ::: 116 | 117 | :::{note} 118 | If you are considering submitting a package for peer review, have a look 119 | at the bare-minimum [editor checks](https://www.pyopensci.org/software-peer-review/how-to/editor-in-chief-guide.html#editor-checklist-template) 120 | that pyOpenSci performs before a review begins. These checks are useful 121 | to explore for both authors planning to submit a package to us for review 122 | and for anyone who is just getting started with creating a Python package. 123 | ::: 124 | 125 | ## What you will learn here 126 | 127 | In this section of our Python packaging guide, we: 128 | 129 | - Provide an overview of the options available to you when packaging your 130 | code. 131 | - Suggest tools and approaches that both meet your needs and also support 132 | existing standards. 133 | - Suggest tools and approaches that will allow you to expand upon a workflow 134 | that may begin as a pure Python code and evolve into code that requires 135 | addition layers of complexity in the packaging build. 136 | - Align our suggestions with the most current, accepted 137 | [PEPs (Python Enhancement Protocols)](https://peps.python.org/pep-0000/) 138 | and the [Scientific Python community SPECs](https://scientific-python.org/specs/). 139 | - In an effort to maintain consistency within our community, we also align 140 | with existing best practices being implemented by developers of core 141 | Scientific Python packages such as Numpy, SciPy and others. 142 | 143 | ## Guidelines for pyOpenSci's packaging recommendations 144 | 145 | The flexibility of the Python programming language lends itself to a diverse 146 | range of tool options for creating a Python package. Python is so flexible that 147 | it is one of the few languages that can be used to wrap around other languages. 148 | The ability of Python to wrap other languages is one the reasons you will often 149 | hear Python described as a ["glue" language](https://numpy.org/doc/stable/user/c-info.python-as-glue.html)" 150 | 151 | If you are building a pure Python package, then your packaging setup can be 152 | simple. However, some scientific packages have complex requirements as they may 153 | need to support extensions or tools written in other languages such as C or C++. 154 | 155 | To support the many different uses of Python, there are many ways to create a 156 | Python package. In this guide, we suggest packaging approaches and tools based on: 157 | 158 | 1. What we think will be best and easiest to adopt for those who are newer to 159 | packaging. 160 | 2. Tools that we think are well maintained and documented. 161 | 3. A shared goal of standardizing packaging approaches across this (scientific) 162 | Python ecosystem. 163 | 164 | Here, we also try to align our suggestions with the most current, accepted 165 | [Python community](https://packaging.python.org/en/latest/) and [scientific community](https://scientific-python.org/specs/). 166 | 167 | :::{admonition} Suggestions in this guide are not pyOpenSci review requirements 168 | :class: important 169 | 170 | The suggestions for package layout in this section are made with the 171 | intent of being helpful; they are not specific requirements for your 172 | package to be reviewed and accepted into our pyOpenSci open source ecosystem. 173 | 174 | Please check out our [package scope page](https://www.pyopensci.org/software-peer-review/about/package-scope.html) 175 | and [review requirements in our author guide](https://www.pyopensci.org/software-peer-review/how-to/author-guide.html#) 176 | if you are looking for pyOpenSci's Python package review requirements! 177 | ::: 178 | 179 | :::{toctree} 180 | :hidden: 181 | :caption: Create & Build Your Package 182 | 183 | Intro 184 | 185 | Python package structure 186 | pyproject.toml Package Metadata 187 | Build Your Package 188 | Declare dependencies 189 | Package Build Tools 190 | Complex Builds 191 | ::: 192 | 193 | :::{toctree} 194 | :hidden: 195 | :caption: Publish your package 196 | 197 | Publish with Conda / PyPI 198 | Package versions 199 | Code style 200 | 201 | ::: 202 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling", "hatch-vcs"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "python-package-guide" 7 | dynamic = [ 8 | "version" 9 | ] 10 | dependencies = [ 11 | "pydata-sphinx-theme==0.16.1", 12 | "myst-nb", 13 | "sphinx", 14 | "sphinx-autobuild", 15 | "sphinx-copybutton", 16 | "sphinx-design", 17 | "sphinx-favicon", 18 | # XML feed for analytics 19 | "sphinx-sitemap", 20 | # Support for social / adds meta tags 21 | "sphinxext-opengraph", 22 | "sphinx-inline-tabs", 23 | # for project cards 24 | "matplotlib", 25 | # for license page bibliography 26 | "sphinxcontrib-bibtex", 27 | ] 28 | 29 | [project.optional-dependencies] 30 | dev = [ 31 | # for general build workflows 32 | "nox", 33 | # for prose linting 34 | "vale", 35 | # for managing translation files 36 | "sphinx-intl", 37 | ] 38 | 39 | [tool.hatch.build.targets.wheel] 40 | bypass-selection = true 41 | 42 | [tool.hatch] 43 | version.source = "vcs" 44 | 45 | 46 | # https://github.com/codespell-project/codespell#usage 47 | [tool.codespell] 48 | ignore-words = "codespell-ignore.txt" 49 | skip = "./.git,./.nox,./_static,./_build,codespell-ignore.txt,*.svg" 50 | -------------------------------------------------------------------------------- /scripts/translation_stats.py: -------------------------------------------------------------------------------- 1 | from babel.messages import pofile 2 | 3 | from pathlib import Path 4 | from babel.messages import pofile 5 | 6 | # Paths needed for this script 7 | BASE_DIR = Path(__file__).resolve().parent.parent # Repository base directory 8 | LOCALES_DIR = BASE_DIR / "locales" # Locales directory 9 | STATIC_DIR = BASE_DIR / "_static" # Static directory 10 | 11 | def calculate_translation_percentage(po_path : Path, locale : str) -> dict: 12 | """ 13 | Calculate the translation percentage for a given .po file. 14 | 15 | Parameters 16 | ---------- 17 | po_path : Path 18 | Path to the .po file. 19 | locale : str 20 | Locale code (e.g., 'es', 'fr'). 21 | 22 | Returns 23 | ------- 24 | dict 25 | A dictionary containing the total number of strings, translated strings, 26 | fuzzy strings, untranslated strings, and the translation percentage. 27 | """ 28 | with open(po_path, "r", encoding="utf-8") as f: 29 | catalog = pofile.read_po(f, locale=locale) 30 | 31 | total = 0 32 | translated = 0 33 | fuzzy = 0 34 | 35 | for message in catalog: 36 | if message.id: 37 | total += 1 38 | # Check if the message is fuzzy 39 | # Fuzzy messages are not considered translated 40 | if message.fuzzy: 41 | fuzzy += 1 42 | break 43 | # Check if the message is translated 44 | if message.string: 45 | translated += 1 46 | 47 | percentage = (translated / total * 100) if total > 0 else 0 48 | 49 | return { 50 | "total": total, 51 | "translated": translated, 52 | "fuzzy": fuzzy, 53 | "untranslated": total - translated - fuzzy, 54 | "percentage": round(percentage, 2) 55 | } 56 | 57 | def main(): 58 | # Get all .po files in the locales directory 59 | po_files = list(LOCALES_DIR.rglob("*.po")) 60 | 61 | # Let's use a dictionary to store the results 62 | # 63 | # We will store the info as 64 | # { 65 | # "es": { 66 | # "file1": { 67 | # "total": 100, 68 | # "translated": 50, 69 | # "fuzzy": 0, 70 | # "untranslated": 50, 71 | # "percentage": 50.0 72 | # }, 73 | # ... 74 | # }, 75 | # "fr": { 76 | # "file1": { 77 | # "total": 100, 78 | # "translated": 50, 79 | # "fuzzy": 0, 80 | # "untranslated": 50, 81 | # "percentage": 50.0 82 | # }, 83 | # ... 84 | # } 85 | results = {} 86 | 87 | # Calculate translation percentages for each file 88 | for po_file in po_files: 89 | # Get the locale from the file path 90 | locale = po_file.parent.parent.name 91 | stats = calculate_translation_percentage(po_file, locale) 92 | print(f"({po_file.stem}): {stats['percentage']}% translated ({stats['translated']} of {stats['total']})") 93 | 94 | # Store the results in the dictionary 95 | if locale not in results: 96 | results[locale] = {} 97 | 98 | results[locale][po_file.stem] = stats 99 | 100 | # Dump the results to a JSON file 101 | with open(STATIC_DIR / "translation_stats.json", "w") as f: 102 | import json 103 | json.dump(results, f, indent=4) 104 | 105 | if __name__ == "__main__": 106 | main() 107 | -------------------------------------------------------------------------------- /tests/code-cov.md: -------------------------------------------------------------------------------- 1 | # Code coverage for your Python package test suite 2 | 3 | Code coverage measures how much of your package's code runs during testing. 4 | Achieving high coverage can help ensure the reliability of your codebase, but 5 | it’s not a guarantee of quality. Below, we outline key considerations for 6 | using code coverage effectively. 7 | 8 | ## Why aim for high code coverage? 9 | 10 | A good practice is to ensure that every line of your code runs at least once 11 | during your test suite. This helps you: 12 | 13 | - Identify untested parts of your codebase. 14 | - Catch bugs that might otherwise go unnoticed. 15 | - Build confidence in your software's stability. 16 | 17 | ## Limitations of code coverage 18 | 19 | While high code coverage is valuable, it has its limits: 20 | 21 | - **Difficult-to-test code:** Some parts of your code might be challenging to 22 | test, either due to complexity or limited resources. 23 | - **Missed edge cases:** Running all lines of code doesn’t guarantee that edge 24 | cases are handled correctly. 25 | 26 | Ultimately, you should focus on how your package will be used and ensure your 27 | tests cover those scenarios adequately. 28 | 29 | ## Tools for analyzing Python package code coverage 30 | 31 | Some common services for analyzing code coverage are [codecov.io](https://codecov.io/) and [coveralls.io](https://coveralls.io/). These projects are free for open source tools and will provide dashboards that tell you how much of your codebase is covered during your tests. We recommend setting up an account (on either CodeCov or Coveralls) and using it to keep track of your code coverage. 32 | 33 | :::{figure} ../images/code-cov-stravalib.png 34 | :height: 450px 35 | :alt: Screenshot of the code cov service - showing test coverage for the stravalib package. This image shows a list of package modules and the associated number of lines and % lines covered by tests. At the top of the image, you can see what branch is being evaluated and the path to the repository. 36 | 37 | The CodeCov platform is a useful tool if you wish to track code coverage visually. Using it, you can not only get the same summary information that you can get with the **pytest-cov** extension. You can also see what lines are covered by your tests and which are not. Code coverage is useful for evaluating unit tests and/or how much of your package code is "covered". It, however, will not evaluate things like integration tests and end-to-end workflows. 38 | 39 | ::: 40 | 41 | 42 | 43 | :::{admonition} Typing & MyPy coverage 44 | You can also create and upload typing reports to CodeCov. 45 | ::: 46 | 47 | ## Exporting Local Coverage Reports 48 | 49 | In addition to using services like CodeCov or Coveralls, you can generate local coverage reports directly using the **coverage.py** tool. This can be especially useful if you want to create reports in Markdown or HTML format for offline use or documentation. 50 | 51 | To generate a coverage report in **Markdown** format, run: 52 | 53 | ```bash 54 | $ python -m coverage report --format=markdown 55 | ``` 56 | This command will produce a Markdown-formatted coverage summary that you can easily include in project documentation or share with your team. 57 | 58 | To generate an HTML report that provides a detailed, interactive view of which lines are covered, use: 59 | 60 | ```bash 61 | python -m coverage html 62 | ``` 63 | 64 | The generated HTML report will be saved in a directory named htmlcov by default. Open the index.html file in your browser to explore your coverage results. 65 | 66 | These local reports are an excellent way to quickly review coverage without setting up an external service. 67 | -------------------------------------------------------------------------------- /tests/index.md: -------------------------------------------------------------------------------- 1 | (tests-intro)= 2 | # Tests and data for your Python package 3 | 4 | Tests are an important part of your Python package because they 5 | provide a set of checks that ensure that your package is 6 | functioning how you expect it to. 7 | 8 | In this section, you will learn more about the importance of writing 9 | tests for your Python package and how you can set up infrastructure 10 | to run your tests both locally and on GitHub. 11 | 12 | 13 | :::::{grid} 1 1 3 2 14 | :class-container: text-center 15 | :gutter: 3 16 | 17 | ::::{grid-item} 18 | :::{card} ✨ Why write tests ✨ 19 | :link: write-tests 20 | :link-type: doc 21 | :class-card: left-aligned 22 | 23 | Learn more about the art of writing tests for your Python package. 24 | Learn about why you should write tests and how they can help you and 25 | potential contributors to your project. 26 | ::: 27 | :::: 28 | 29 | ::::{grid-item} 30 | :::{card} ✨ Types of tests ✨ 31 | :link: test-types 32 | :link-type: doc 33 | :class-card: left-aligned 34 | 35 | There are three general types of tests that you can write for your Python 36 | package: unit tests, integration tests and end-to-end (or functional) tests. Learn about all three. 37 | ::: 38 | :::: 39 | 40 | ::::{grid-item} 41 | :::{card} ✨ Run tests locally ✨ 42 | :link: run-tests 43 | :link-type: doc 44 | :class-card: left-aligned 45 | 46 | If you expect your users to use your package across different versions 47 | of Python, then using an automation tool such as nox to run your tests is useful. Learn about the various tools that you can use to run your tests across python versions here. 48 | ::: 49 | :::: 50 | 51 | ::::{grid-item} 52 | :::{card} ✨ Run tests online (using CI) ✨ 53 | :link: tests-ci 54 | :link-type: doc 55 | :class-card: left-aligned 56 | 57 | Continuous integration platforms such as GitHub Actions can be 58 | useful for running your tests across both different Python versions 59 | and different operating systems. Learn about setting up tests to run in Continuous Integration here. 60 | ::: 61 | :::: 62 | 63 | ::::: 64 | 65 | 66 | :::{figure-md} fig-target 67 | 68 | 69 | 70 | Graphic showing the elements of the packaging process. 71 | ::: 72 | 73 | ```{toctree} 74 | :hidden: 75 | :maxdepth: 2 76 | :caption: Create & Run Tests 77 | 78 | Intro 79 | Write tests 80 | Test types 81 | Run tests locally 82 | Run tests online (using CI) 83 | Code coverage 84 | ``` 85 | -------------------------------------------------------------------------------- /tests/test-types.md: -------------------------------------------------------------------------------- 1 | # Test Types for Python packages 2 | 3 | ## Three types of tests: Unit, Integration & Functional Tests 4 | 5 | There are different types of tests that you want to consider when creating your 6 | test suite: 7 | 8 | 1. Unit tests 9 | 2. Integration 10 | 3. End-to-end (also known as Functional) tests 11 | 12 | Each type of test has a different purpose. Here, you will learn about all three types of tests. 13 | 14 | ```{todo} 15 | I think this page would be stronger if we did have some 16 | examples from our package here: https://github.com/pyOpenSci/pyosPackage 17 | 18 | ``` 19 | 20 | ## Unit Tests 21 | 22 | A unit test involves testing individual components or units of code in isolation to ensure that they work correctly. The goal of unit testing is to verify that each part of the software, typically at the function or method level, performs its intended task correctly. 23 | 24 | Unit tests can be compared to examining each piece of your puzzle to ensure parts of it are not broken. If all of the pieces of your puzzle don’t fit together, you will never complete it. Similarly, when working with code, tests ensure that each function, attribute, class, method works properly when isolated. 25 | 26 | **Unit test example:** Pretend that you have a function that converts a temperature value from Celsius to Fahrenheit. A test for that function might ensure that when provided with a value in Celsius, the function returns the correct value in degrees Fahrenheit. That function is a unit test. It checks a single unit (function) in your code. 27 | 28 | ```python 29 | # Example package function 30 | def celsius_to_fahrenheit(celsius): 31 | """ 32 | Convert temperature from Celsius to Fahrenheit. 33 | 34 | Parameters: 35 | celsius (float): Temperature in Celsius. 36 | 37 | Returns: 38 | float: Temperature in Fahrenheit. 39 | """ 40 | fahrenheit = (celsius * 9/5) + 32 41 | return fahrenheit 42 | ``` 43 | 44 | Example unit test for the above function. You'd run this test using the `pytest` command in your **tests/** directory. 45 | 46 | ```python 47 | import pytest 48 | from temperature_converter import celsius_to_fahrenheit 49 | 50 | def test_celsius_to_fahrenheit(): 51 | """ 52 | Test the celsius_to_fahrenheit function. 53 | """ 54 | # Test with freezing point of water 55 | assert pytest.approx(celsius_to_fahrenheit(0), abs=0.01) == 32.0 56 | 57 | # Test with boiling point of water 58 | assert pytest.approx(celsius_to_fahrenheit(100), abs=0.01) == 212.0 59 | 60 | # Test with a negative temperature 61 | assert pytest.approx(celsius_to_fahrenheit(-40), abs=0.01) == -40.0 62 | 63 | ``` 64 | 65 | ```{figure} ../images/pyopensci-puzzle-pieces-tests.png 66 | :height: 300px 67 | :alt: image of puzzle pieces that all fit together nicely. The puzzle pieces are colorful - purple, green and teal. 68 | 69 | Your unit tests should ensure each part of your code works as expected on its own. 70 | ``` 71 | 72 | ## Integration tests 73 | 74 | Integration tests involve testing how parts of your package work together or integrate. Integration tests can be compared to connecting a bunch of puzzle pieces together to form a whole picture. Integration tests focus on how different pieces of your code fit and work together. 75 | 76 | For example, if you had a series of steps that collected temperature data in a spreadsheet, converted it from degrees celsius to Fahrenheit and then provided an average temperature for a particular time period. An integration test would ensure that all parts of that workflow behaved as expected. 77 | 78 | ```python 79 | 80 | def fahr_to_celsius(fahrenheit): 81 | """ 82 | Convert temperature from Fahrenheit to Celsius. 83 | 84 | Parameters: 85 | fahrenheit (float): Temperature in Fahrenheit. 86 | 87 | Returns: 88 | float: Temperature in Celsius. 89 | """ 90 | celsius = (fahrenheit - 32) * 5/9 91 | return celsius 92 | 93 | # Function to calculate the mean temperature for each year and the final mean 94 | def calc_annual_mean(df): 95 | # TODO: make this a bit more robust so we can write integration test examples?? 96 | # Calculate the mean temperature for each year 97 | yearly_means = df.groupby('Year').mean() 98 | 99 | # Calculate the final mean temperature across all years 100 | final_mean = yearly_means.mean() 101 | 102 | # Return a converted value 103 | return fahr_to_celsius(yearly_means), fahr_to_celsius(final_mean) 104 | 105 | ``` 106 | 107 | ```{figure} ../images/python-tests-puzzle.png 108 | :height: 350px 109 | :alt: image of two puzzle pieces with some missing parts. The puzzle pieces are purple teal yellow and blue. The shapes of each piece don’t fit together. 110 | 111 | If puzzle pieces have missing ends, they can’t work together with other elements in the puzzle. The same is true with individual functions, methods and classes in your software. The code needs to work both individually and together to perform certain sets of tasks. 112 | 113 | ``` 114 | 115 | ```{figure} ../images/python-tests-puzzle-fit.png 116 | :height: 450px 117 | :alt: image of puzzle pieces that all fit together nicely. The puzzle pieces are colorful - purple, green and teal. 118 | 119 | Your integration tests should ensure that parts of your code that are expected to work 120 | together, do so as expected. 121 | 122 | ``` 123 | 124 | ## End-to-end (functional) tests 125 | 126 | End-to-end tests (also referred to as functional tests) in Python are like comprehensive checklists for your software. They simulate real user end-to-end workflows to make sure the code base supports real life applications and use-cases from start to finish. These tests help catch issues that might not show up in smaller tests and ensure your entire application or program behaves correctly. Think of them as a way to give your software a final check before it's put into action, making sure it's ready to deliver a smooth experience to its users. 127 | 128 | ```{figure} ../images/flower-puzzle-pyopensci.jpg 129 | :height: 450px 130 | :alt: Image of a completed puzzle showing a daisy 131 | 132 | End-to-end or functional tests represent an entire workflow that you 133 | expect your package to support. 134 | 135 | ``` 136 | 137 | End-to-end test also test how a program runs from start to finish. A tutorial that you add to your documentation that runs in CI in an isolated environment is another example of an end-to-end test. 138 | 139 | ```{note} 140 | For scientific packages, creating short tutorials that highlight core workflows that your package supports, that are run when your documentation is built could also serve as end-to-end tests. 141 | ``` 142 | 143 | ## Comparing unit, integration and end-to-end tests 144 | 145 | Unit tests, integration tests, and end-to-end tests have complementary advantages and disadvantages. The fine-grained nature of unit tests make them well-suited for isolating where errors are occurring. However, unit tests are not useful for verifying that different sections of code work together. 146 | 147 | Integration and end-to-end tests verify that the different portions of the program work together, but are less well-suited for isolating where errors are occurring. For example, when you refactor your code, it is possible that that your end-to-end tests will 148 | break. But if the refactor didn't introduce new behavior to your existing 149 | code, then you can rely on your unit tests to continue to pass, testing the 150 | original functionality of your code. 151 | 152 | It is important to note that you don't need to spend energy worrying about 153 | the specifics surrounding the different types of tests. When you begin to 154 | work on your test suite, consider what your package does and how you 155 | may need to test parts of your package. Bring familiar with the different types of tests can provides a framework to 156 | help you think about writing tests and how different types of tests can complement each other. 157 | -------------------------------------------------------------------------------- /tests/tests-ci.md: -------------------------------------------------------------------------------- 1 | # Run tests with Continuous Integration 2 | 3 | Running your [test suite locally](run-tests) is useful as you develop code and also test new features or changes to the code base. However, you also will want to setup Continuous Integration (CI) to run your tests online. CI allows you to run all of your tests in the cloud. While you may only be able to run tests locally on a specific operating system, using CI you can specify tests to run both on various versions of Python and across different operating systems. 4 | 5 | CI can also be triggered for pull requests and pushes to your repository. This means that every pull request that you, your maintainer team or a contributor submit, can be tested. In the end CI testing ensures your code continues to run as expected even as changes are made to the code base. 6 | 7 | ::::{todo} 8 | ```{note} 9 | Learn more about Continuous Integration and how it can be used, here. (add link) 10 | ``` 11 | :::: 12 | 13 | ## CI & pull requests 14 | 15 | CI is invaluable if you have outside people contributing to your software. 16 | You can setup CI to run on all pull requests submitted to your repository. 17 | CI can make your repository more friendly to new potential contributors. 18 | It allows users to contribute code, documentation fixes and more without 19 | having to create development environments, run tests and build documentation 20 | locally. 21 | 22 | ## Example GitHub Actions that runs tests 23 | 24 | Below is an example GitHub Actions that runs tests using nox 25 | across both Windows, Mac and Linux and on Python versions 26 | 3.9-3.11. 27 | 28 | To work properly, this file should be located in a root directory of your 29 | GitHub repository: 30 | 31 | ```bash 32 | pyospackage/ 33 | ├──.github/ 34 | └── workflows/ 35 | └── run-tests.yml # The name of this file can be whatever you wish 36 | ``` 37 | 38 | 39 | ```yaml 40 | name: Pytest unit/integration 41 | 42 | on: 43 | pull_request: 44 | push: 45 | branches: 46 | - main 47 | 48 | # Use bash by default in all jobs 49 | defaults: 50 | run: 51 | shell: bash 52 | 53 | jobs: 54 | build-test: 55 | name: Test Run (${{ matrix.python-version }}, ${{ matrix.os }}) 56 | runs-on: ${{ matrix.os }} 57 | strategy: 58 | fail-fast: false 59 | matrix: 60 | os: ["ubuntu-latest", "macos-latest", "windows-latest"] 61 | python-version: ["3.9", "3.10", "3.11"] 62 | 63 | steps: 64 | - uses: actions/checkout@v4 65 | - name: Set up Python ${{ matrix.python-version }} 66 | uses: actions/setup-python@v4 67 | with: 68 | python-version: ${{ matrix.python-version }} 69 | - name: Install dependencies 70 | run: | 71 | python -m pip install --upgrade pip 72 | python -m pip install nox 73 | - name: List installed packages 74 | run: pip list 75 | - name: Run tests with pytest & nox 76 | run: | 77 | nox -s tests-${{ matrix.python-version }} 78 | # You only need to upload code coverage once to codecov unless you have a 79 | # more complex build that you need coverage for. 80 | - name: Upload coverage to Codecov 81 | if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'}} 82 | uses: codecov/codecov-action@v3 83 | ``` 84 | -------------------------------------------------------------------------------- /tests/write-tests.md: -------------------------------------------------------------------------------- 1 | # Write tests for your Python package 2 | 3 | Writing code that tests your package code, also known as test suites, is important for you as a maintainer, your users, and package contributors. Test suites consist of sets of functions, methods, and classes 4 | that are written with the intention of making sure a specific part of your code 5 | works as you expected it to. 6 | 7 | ## Why write tests for your package? 8 | 9 | Tests act as a safety net for code changes. They help you spot and rectify bugs 10 | before they affect users. Tests also instill confidence that code alterations from 11 | contributors won't breaking existing functionality. 12 | 13 | Writing tests for your Python package is important because: 14 | 15 | - **Catch Mistakes:** Tests are a safety net. When you make changes or add new features to your package, tests can quickly tell you if you accidentally broke something that was working fine before. 16 | - **Save Time:** Imagine you have a magic button that can automatically check if your package is still working properly. Tests are like that magic button! They can run all those checks for you saving you time. 17 | - **Easier Collaboration:** If you're working with others, or have outside contributors, tests help everyone stay on the same page. Your tests explain how your package is supposed to work, making it easier for others to understand and contribute to your project. 18 | - **Fearless Refactoring:** Refactoring means making improvements to your code structure without changing its behavior. Tests empower you to make these changes as if you break something, test failures will let you know. 19 | - **Documentation:** Tests serve as technical examples of how to use your package. This can be helpful for a new technical contributor that wants to contribute code to your package. They can look at your tests to understand how parts of your code functionality fits together. 20 | - **Long-Term ease of maintenance:** As your package evolves, tests ensure that your code continues to behave as expected, even as you make changes over time. Thus you are helping your future self when writing tests. 21 | - **Easier pull request reviews:** By running your tests in a CI framework such as GitHub Actions, each time you or a contributor makes a change to your code-base, you can catch issues and things that may have changed in your code base. This ensures that your software behaves the way you expect it to. 22 | 23 | ### Tests for user edge cases 24 | 25 | Edge cases refer to unexpected or "outlier" ways that some users may use your package. Tests enable you to address various edge cases that could impair 26 | your package's functionality. For example, what occurs if a function expects a 27 | pandas `dataframe` but a user supplies a numpy `array`? Does your code gracefully 28 | handle this situation, providing clear feedback, or does it leave users 29 | frustrated by an unexplained failure? 30 | 31 | :::{note} 32 | 33 | For a good introduction to testing, see [this Software Carpentry lesson](https://swcarpentry.github.io/python-novice-inflammation/10-defensive.html) 34 | 35 | ::: 36 | 37 | ```{figure} ../images/python-tests-puzzle.png 38 | :height: 350px 39 | 40 | Imagine you're working on a puzzle where each puzzle piece represents a function, method, class or attribute in your Python package that you want other people to be able to use. Would you want to give someone a puzzle that has missing pieces or pieces that don't fit together? Providing people with the right puzzle pieces that work together can be compared to writing tests for your Python package. 41 | 42 | ``` 43 | 44 | ````{admonition} Test examples 45 | :class: note 46 | 47 | Let’s say you have a Python function that adds two numbers a and b together. 48 | 49 | ```python 50 | def add_numbers(a, b): 51 | return a + b 52 | ``` 53 | 54 | A test to ensure that function runs as you might expect when provided with different numbers might look like this: 55 | 56 | ```python 57 | def test_add_numbers(): 58 | result = add_numbers(2, 3) 59 | assert result == 5, f"Expected 5, but got {result}" 60 | 61 | result2 = add_numbers(-1, 4) 62 | assert result2 == 3, f"Expected 3, but got {result2}" 63 | 64 | result3 = add_numbers(0, 0) 65 | assert result3 == 0, f"Expected 0, but got {result3}" 66 | 67 | test_add_numbers() 68 | 69 | ``` 70 | ```` 71 | 72 | 🧩🐍 73 | 74 | ### How do I know what type of tests to write? 75 | 76 | :::{note} 77 | This section has been adapted from [a presentation by Nick Murphy](https://zenodo.org/record/8185113). 78 | ::: 79 | 80 | At this point, you may be wondering - what should you be testing in your package? Below are a few examples: 81 | 82 | - **Test some typical cases:** Test that the package functions as you expect it to when users use it. For instance, if your package is supposed to add two numbers, test that the outcome value of adding those two numbers is correct. 83 | 84 | - **Test special cases:** Sometimes there are special or outlier cases. For instance, if a function performs a specific calculation that may become problematic closer to the value = 0, test it with the input of both 0 and 85 | 86 | * **Test at and near the expected boundaries:** If a function requires a value that is greater than or equal to 1, make sure that the function still works with both the values 1 and less than one and 1.001 as well (something close to the constraint value).. 87 | 88 | * **Test that code fails correctly:** If a function requires a value greater than or equal to 1, then test at 0.999. Make sure that the function fails gracefully when given unexpected values and help and that the user can easily understand why if failed (provides a useful error message). 89 | -------------------------------------------------------------------------------- /tutorials/command-line-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | :og:description: Learn how to add a command-line interface (CLI) to your Python package using the argparse library. This lesson walks you through creating a CLI entry point so users can run your package directly from the terminal. 3 | :og:title: Command Line Reference Guide 4 | date: 1970-01-04 5 | --- 6 | 7 | # Command Line Reference Guide 8 | 9 | ```{important} 10 | **What these tables are:** These tables summarize the command line inputs (e.g., `pipx install hatch`, `hatch build`) necessary to complete all steps in the package creation process, from installing Hatch to publishing the package on PyPI and conda-forge. 11 | 12 | **What these tables are not:** These tables do not cover the manual/non-automated steps (e.g., create PyPI account, create PyPI API token) you have to complete throughout the package creation process. 13 | 14 | **Operating system note:** The current iteration of this guide has been tested on the Windows OS only. Many commands are Windows-specific. OS-specific commands are indicated with parentheses after the description of the command, e.g., [COMMAND_DESCRIPTION] (Windows). Corresponding commands for macOS and Linux will be added in the future. 15 | ``` 16 | 17 | ## Environment Setup 18 | 19 | :::{table} 20 | :widths: auto 21 | :align: center 22 | 23 | | Description | Syntax | 24 | |---|---| 25 | | Set PowerShell execution policy (Windows) | `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser` | 26 | | Install Scoop (Windows) | `Invoke-RestMethod -Uri https://get.scoop.sh \| Invoke-Expression` | 27 | | Add "main" bucket as download source (Windows) | `scoop bucket add main` | 28 | | Add "versions" bucket as download source (Windows) | `scoop bucket add versions` | 29 | | Install pipx (Windows) | `scoop install pipx` or `scoop install main/pipx` | 30 | | Update PATH variable with pipx directory | `pipx ensurepath` | 31 | | Install hatch | `pipx install hatch` or `pip install hatch` | 32 | | List hatch commands | `hatch -h` | 33 | | Open location of hatch config file | `hatch config explore` | 34 | | Print contents of hatch config file | `hatch config show` | 35 | | Install grayskull | `pipx install grayskull` or `pip install grayskull` | 36 | 37 | ::: 38 | 39 | ## Package Development 40 | 41 | :::{table} 42 | :widths: auto 43 | :align: center 44 | 45 | | Description | Syntax | 46 | |---|---| 47 | | Create package structure and baseline contents | `hatch new [PACKAGE_NAME]` | 48 | | Install package locally in editable mode | `python -m pip install -e .` | 49 | | Install development dependencies | `python -m pip install ".[DEPENDENCY_GROUP]"` | 50 | | List packages installed in current environment | `pip list` | 51 | | Install package from GitHub | `pip install git+https://github.com/user/repo.git@branch_or_tag` | 52 | | Create development environment | `hatch env create` | 53 | | Activate development environment | `hatch shell` | 54 | | Exit development environment | `exit` | 55 | 56 | ::: 57 | 58 | ## Package Publishing 59 | 60 | :::{table} 61 | :widths: auto 62 | :align: center 63 | 64 | | Description | Syntax | 65 | |---|---| 66 | | Build package sdist and wheel distributions | `hatch build` | 67 | | Publish package to Test PyPI | `hatch publish -r test` | 68 | | Install package from Test PyPI | `pip install -i https://test.pypi.org/simple/ [PACKAGE_NAME]` | 69 | | Publish package to PyPI | `hatch publish` | 70 | | Install package from PyPI | `pip install -i https://pypi.org/simple/ [PACKAGE_NAME]` | 71 | | Create conda-forge recipe | `grayskull pypi [PACKAGE_NAME]` | 72 | | Check that package installs properly | `pip check` | 73 | | Install package from conda-forge | `conda install -c conda-forge [PACKAGE_NAME]` | 74 | 75 | ::: 76 | 77 | ## Versions and Environments 78 | 79 | :::{table} 80 | :widths: auto 81 | :align: center 82 | 83 | | Description | Syntax | 84 | |---|---| 85 | | View environments | `hatch env show` | 86 | | Print path to active hatch environment | `hatch env find` | 87 | | Bump package version - major | `hatch version major` | 88 | | Bump package version - minor | `hatch version minor` | 89 | | Bump package version - patch | `hatch version patch` | 90 | | Run test scripts on multiple Python versions | `hatch run all:[SCRIPT_NAME]` | 91 | 92 | ::: 93 | -------------------------------------------------------------------------------- /tutorials/get-to-know-hatch.md: -------------------------------------------------------------------------------- 1 | --- 2 | :og:description: Get started with Hatch, a modern Python packaging tool. This lesson introduces Hatch’s features and shows how it simplifies environment management, project scaffolding, and building your package. 3 | :og:title: Get to Know Hatch 4 | date: 1970-01-05 5 | --- 6 | 7 | # Get to Know Hatch 8 | 9 | Our Python packaging tutorials use the tool 10 | [Hatch](https://hatch.pypa.io/latest/). While there are [many great packaging 11 | tools](/package-structure-code/python-package-build-tools) out there, we have 12 | selected Hatch because: 13 | 14 | 1. It is an end-to-end tool that supports most of the steps required to create 15 | a quality Python package. Beginners will have fewer tools to learn if they 16 | use Hatch. 17 | 2. It supports different build back-ends if you ever need to compile code in 18 | other languages. 19 | 3. As a community, pyOpenSci has decided that Hatch is a user-friendly tool that 20 | supports many different scientific Python use cases. 21 | 22 | In this tutorial, you will install and get to know Hatch a bit more before 23 | starting to use it. 24 | 25 | You need two things to successfully complete this tutorial: 26 | 27 | 1. You need Python installed. 28 | 2. You need Hatch installed. 29 | 30 | :::{important} 31 | If you don't already have Python installed on your computer, Hatch will do it 32 | for you when you install Hatch. 33 | ::: 34 | 35 | ## Install Hatch 36 | 37 | To begin, follow the operating-system-specific instructions below to install 38 | Hatch. 39 | 40 | ::::{tab-set} 41 | 42 | :::{tab-item} MAC 43 | 44 | Follow the instructions [here](https://hatch.pypa.io/latest/install/#installers). 45 | 46 | * Download the latest GUI installer for MAC [hatch-universal.pkg](https://github.com/pypa/hatch/releases/latest/download/hatch-universal.pkg). 47 | * Run the installer and follow the setup instructions. 48 | * If your terminal is open, then restart it. 49 | 50 | ::: 51 | 52 | :::{tab-item} Windows 53 | 54 | * In your browser, download the correct `.msi` file for your system: 55 | [hatch-x64.msi](https://github.com/pypa/hatch/releases/latest/download/hatch-x64.msi) 56 | * Run your downloaded installer file and follow the on-screen instructions. 57 | 58 | ::: 59 | 60 | :::{tab-item} Linux 61 | 62 | We suggest that you install Hatch using pipx on Linux. 63 | however, if you prefer another method, check out the [Hatch installation documentation](https://hatch.pypa.io/latest/install/) for other methods. 64 | 65 | ```bash 66 | # First install pipx 67 | > apt install pipx 68 | # Then install hatch using pipx 69 | > pipx install hatch 70 | ``` 71 | 72 | ::: 73 | :::: 74 | 75 | :::{tip} 76 | Hatch can also be installed directly using [pip](https://hatch.pypa.io/latest/install/#pip) or [conda](https://hatch.pypa.io/latest/install/#conda). We encourage you to 77 | follow the instructions above because we have found that the Hatch installers 78 | for Windows and Mac are the easiest and most efficient. 79 | 80 | Our Linux users have found success installing Hatch with pipx if they already 81 | use apt install. 82 | 83 | Both approaches (using a graphical installer on Windows/Mac and pipx) ensure 84 | that you have Hatch installed globally. A global install means that Hatch is 85 | available across all of your Python environments on your computer. 86 | ::: 87 | 88 | ### Check that hatch installed correctly 89 | 90 | Once you have completed the installation instructions above, you can open your 91 | terminal, and make sure that Hatch installed correctly using the command below: 92 | 93 | ```bash 94 | hatch --version 95 | # Hatch, version 1.9.4 96 | ``` 97 | 98 | *Note the version number output of `hatch --version` will likely be 99 | different from the output above in this tutorial.* 100 | 101 | ## Configure Hatch 102 | 103 | Once you have installed Hatch, you can customize its configuration. This 104 | includes setting the default name and setup for every package you create. While 105 | this step is not required, we suggest that you do it. 106 | 107 | Hatch stores your configuration in a [`config.toml` file](https://hatch.pypa.io/latest/config/project-templates/). 108 | 109 | While you can update the `config.toml` file through the command line, it might 110 | be easier to look at and update it in a text editor if you are using it for the 111 | first time. 112 | 113 | ### Step 1: Open and Edit Your `config.toml` File 114 | 115 | To open the config file in your file browser, run the following command in your 116 | shell: 117 | 118 | `hatch config explore` 119 | 120 | This will open up a directory window that allows you to double-click on the file 121 | and open it in your favorite text editor. 122 | 123 | You can also retrieve the location of the Hatch config file by running the 124 | following command in your shell: 125 | 126 | ```bash 127 | hatch config find 128 | # hatch config --help will show you all the options for config. 129 | ``` 130 | 131 | ### Step 2 - update your email and name 132 | 133 | Once the file is open, update the [template] table of the `config.toml` file 134 | with your name and email. This information will be used in any `pyproject.toml` 135 | metadata files that you create using Hatch. 136 | 137 | ```toml 138 | [template] 139 | name = "firstName LastName" 140 | email = "your-email@your-domain.org" 141 | ``` 142 | 143 | ### Step 3 144 | 145 | Next, set tests to false in the `[template.plugins.default]` table. 146 | 147 | While tests are important, setting the tests configuration in Hatch 148 | to `true` will create a more complex `pyproject.toml` file. You won't 149 | need to use this feature in this beginner friendly tutorial series 150 | but we will introduce it in later tutorials. 151 | 152 | Your `config.toml` file should look something like the one below. 153 | 154 | ```toml 155 | mode = "local" 156 | project = "" 157 | shell = "" 158 | 159 | [dirs] 160 | project = [] 161 | python = "isolated" 162 | data = "/Users/your/path/Application Support/hatch" 163 | cache = "/Users/your/path/Library/Caches/hatch" 164 | 165 | [dirs.env] 166 | 167 | [projects] 168 | 169 | [publish.index] 170 | repo = "main" 171 | 172 | [template] 173 | name = "Leah Wasser" 174 | email = "leah@pyopensci.org" 175 | 176 | [template.licenses] 177 | headers = true 178 | default = [ 179 | "MIT", 180 | ] 181 | 182 | [template.plugins.default] 183 | tests = false 184 | ci = false 185 | src-layout = true 186 | 187 | [terminal.styles] 188 | ``` 189 | 190 | Also notice that the default license option is MIT. While we will discuss 191 | license in more detail in a later lesson, the MIT license is the 192 | recommended permissive license from 193 | [choosealicense.com](https://www.choosealicense.com) and as such we will 194 | use it for this tutorial series. 195 | 196 | You are of course welcome to select another license. 197 | 198 | :::{todo} 199 | I think we'd need the SPDX license options here if they want to chose bsd-3 for instance 200 | ::: 201 | 202 | ### Step 4: Close the config file and run `hatch config show` 203 | 204 | Once you have completed the steps above run the following command in your shell. 205 | 206 | `hatch config show` 207 | 208 | `hatch config show` will print out the contents of your `config.toml` file in 209 | your shell. Look at the values and ensure that your name, email is set. Also 210 | make sure that `tests=false`. 211 | 212 | ## Hatch features 213 | 214 | Hatch offers a suite of features that will make creating, publishing 215 | and maintaining your Python package easier. 216 | 217 | :::{admonition} Comparison to other tools 218 | :class: tip 219 | [We compared Hatch to several of the other popular packaging tools in the ecosystem including flit, pdm and poetry. Learn more here](package-features) 220 | ::: 221 | 222 | [More on Hatch here](hatch) 223 | 224 | A few features that Hatch offers 225 | 226 | 1. It will convert metadata stored in a `setup.py` or `setup.cfg` file to a pyproject.toml file for you (see [Migrating setup.py to pyproject.toml using Hatch](setup-py-to-pyproject-toml.md 227 | )) 228 | 2. It will help you by storing configuration information for publishing to PyPI after you've entered it once. 229 | 230 | Use `hatch -h` to see all of the available commands. 231 | 232 | ## What's next 233 | 234 | In the next lesson you'll learn how to package and make your code installable using Hatch. 235 | -------------------------------------------------------------------------------- /tutorials/setup-py-to-pyproject-toml.md: -------------------------------------------------------------------------------- 1 | --- 2 | :og:description: If you’re creating a pure Python project, pyproject.toml is preferred over setup.py for packaging and configuration. Learn how to migrate from the older setup.py format to the modern pyproject.toml file. This lesson walks you through updating your package metadata and build settings to align with current Python packaging standards. 3 | :og:title: Using Hatch to Migrate setup.py to a pyproject.toml 4 | date: 1970-01-09 5 | --- 6 | 7 | # Using Hatch to Migrate setup.py to a pyproject.toml 8 | 9 | Hatch can be particularly useful to generate your project's `pyproject.toml` if your project already has a `setup.py`. 10 | 11 | :::{admonition} Note 12 | :class: tip 13 | 14 | This step is not necessary and is only useful if your project already has a `setup.py` file defined. 15 | * If your project does not already define a `setup.py` see [Make your Python code installable](installable-code.md) 16 | ::: 17 | 18 | :::{admonition} Learning Objectives 19 | :class: tip 20 | 21 | In this lesson you will learn: 22 | 23 | 1. The process of using Hatch to transition to using `pyproject.toml` for projects that already have a `setup.py` defined. 24 | ::: 25 | 26 | ## What is Hatch? 27 | 28 | Hatch is a Python package manager designed to streamline the process of creating, managing, and distributing Python packages. It provides a convenient CLI (Command-Line Interface) for tasks such as creating new projects, managing dependencies, building distributions, and publishing packages to repositories like PyPI. 29 | 30 | :::{admonition} Get to know Hatch 31 | :class: tip 32 | 33 | See [Get to know Hatch](get-to-know-hatch.md) for more information. 34 | 35 | ::: 36 | 37 | ## Prerequisites 38 | 39 | Before we begin, ensure that you have Hatch installed on your system. You can install it via pip: 40 | 41 | ```bash 42 | pipx install hatch 43 | ``` 44 | 45 | ## Sample Directory Tree 46 | 47 | Let's take a look at a sample directory tree structure before and after using `hatch init`: 48 | 49 | ### Before `hatch init` 50 | 51 | ``` 52 | project/ 53 | │ 54 | ├── src/ 55 | │ └── my_package/ 56 | │ ├── __init__.py 57 | │ └── module.py 58 | │ 59 | ├── tests/ 60 | │ └── test_module.py 61 | │ 62 | └── setup.py 63 | ``` 64 | 65 | ### After `hatch init` 66 | 67 | ``` 68 | project/ 69 | │ 70 | ├── pyproject.toml 71 | │ 72 | ├── src/ 73 | │ └── my_package/ 74 | │ ├── __init__.py 75 | │ └── module.py 76 | │ 77 | ├── tests/ 78 | │ └── test_module.py 79 | │ 80 | └── setup.py 81 | ``` 82 | 83 | As you can see, the main change after running `hatch init` is the addition of the `pyproject.toml` file in the project directory. 84 | 85 | ## Step-by-Step Guide 86 | 87 | Now, let's walk through the steps to use Hatch to create a `pyproject.toml` file for your project. 88 | 89 | 1. **Navigate to Your Project Directory**: Open your terminal or command prompt and navigate to the directory where your Python project is located. 90 | 91 | 2. **Initialize Hatch**: Run the following command to initialize Hatch in your project directory: 92 | 93 | ```bash 94 | hatch new --init 95 | ``` 96 | 97 | 3. **Review and Customize**: After running the previous command, Hatch will automatically generate a `pyproject.toml` file based on your existing project configuration. Take some time to review the contents of the generated `pyproject.toml` file. You may want to customize certain settings or dependencies based on your project's requirements (see [pyproject.toml tutorial](pyproject-toml.md) for more information about the `pyproject.toml`). 98 | 99 | 4. **Verify**: Verify that the `pyproject.toml` file accurately reflects your project configuration and dependencies. You can manually edit the file if needed, but be cautious and ensure that the syntax is correct. 100 | 101 | 5. **Delete setup.py**: Since we're migrating to using `pyproject.toml` exclusively, the `setup.py` file becomes unnecessary. You can safely delete it from your project directory. 102 | 103 | 6. **Test Build**: Before proceeding further, it's essential to ensure that your project builds successfully using only the `pyproject.toml` file. Run the following command to build your project: 104 | 105 | ```bash 106 | hatch build 107 | ``` 108 | 109 | This command will build your project based on the specifications in the `pyproject.toml` file. Make sure to check for any errors or warnings during the build process. 110 | 111 | 7. **Test Existing Functionality**: After successfully building your project with `pyproject.toml`, it's crucial to ensure that your project's existing functionality remains intact. Run any pre-existing tests to verify that everything still works as expected. 112 | -------------------------------------------------------------------------------- /vale-styles/config/vocabularies/sample/accept.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/vale-styles/config/vocabularies/sample/accept.txt -------------------------------------------------------------------------------- /vale-styles/config/vocabularies/sample/reject.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyOpenSci/python-package-guide/4a1a5869e6aaeaea534be4cc30b9e610670b9197/vale-styles/config/vocabularies/sample/reject.txt -------------------------------------------------------------------------------- /vale-styles/package-guide-test/PyPI.yml: -------------------------------------------------------------------------------- 1 | extends: substitution 2 | message: Consider using '%s' instead of '%s' 3 | level: warning 4 | ignorecase: false 5 | action: 6 | name: replace 7 | # swap maps tokens in form of bad: good 8 | swap: 9 | # lower case defined as regex to prevent false positives in URLs or other identifiers 10 | - (?:\spypi[\.,]?\s): PyPI 11 | - (?:\stestpypi[\.,;:]?\s): TestPyPI 12 | - (?:\stest-pypi[\.,;:]?\s): TestPyPI 13 | # other tests are defined with strings 14 | - pyPi: PyPI 15 | - pyPI: PyPI 16 | - PYPI: PyPI 17 | - PyPi: PyPI 18 | - Pypi: PyPI 19 | - testPyPI: TestPyPI 20 | - testPYPI: TestPyPI 21 | - TestPypi: TestPyPI 22 | - TestPYPI: TestPyPI 23 | -------------------------------------------------------------------------------- /vale-styles/write-good/E-Prime.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Try to avoid using '%s'." 3 | ignorecase: true 4 | level: suggestion 5 | tokens: 6 | - am 7 | - are 8 | - aren't 9 | - be 10 | - been 11 | - being 12 | - he's 13 | - here's 14 | - here's 15 | - how's 16 | - i'm 17 | - is 18 | - isn't 19 | - she's 20 | - that's 21 | - there's 22 | - they're 23 | - was 24 | - wasn't 25 | - we're 26 | - were 27 | - weren't 28 | - what's 29 | - where's 30 | - who's 31 | - you're 32 | -------------------------------------------------------------------------------- /vale-styles/write-good/Illusions.yml: -------------------------------------------------------------------------------- 1 | extends: repetition 2 | message: "'%s' is repeated!" 3 | level: warning 4 | alpha: true 5 | action: 6 | name: edit 7 | params: 8 | - truncate 9 | - " " 10 | tokens: 11 | - '[^\s]+' 12 | -------------------------------------------------------------------------------- /vale-styles/write-good/Passive.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' may be passive voice. Use active voice if you can." 3 | ignorecase: true 4 | level: warning 5 | raw: 6 | - \b(am|are|were|being|is|been|was|be)\b\s* 7 | tokens: 8 | - '[\w]+ed' 9 | - awoken 10 | - beat 11 | - become 12 | - been 13 | - begun 14 | - bent 15 | - beset 16 | - bet 17 | - bid 18 | - bidden 19 | - bitten 20 | - bled 21 | - blown 22 | - born 23 | - bought 24 | - bound 25 | - bred 26 | - broadcast 27 | - broken 28 | - brought 29 | - built 30 | - burnt 31 | - burst 32 | - cast 33 | - caught 34 | - chosen 35 | - clung 36 | - come 37 | - cost 38 | - crept 39 | - cut 40 | - dealt 41 | - dived 42 | - done 43 | - drawn 44 | - dreamt 45 | - driven 46 | - drunk 47 | - dug 48 | - eaten 49 | - fallen 50 | - fed 51 | - felt 52 | - fit 53 | - fled 54 | - flown 55 | - flung 56 | - forbidden 57 | - foregone 58 | - forgiven 59 | - forgotten 60 | - forsaken 61 | - fought 62 | - found 63 | - frozen 64 | - given 65 | - gone 66 | - gotten 67 | - ground 68 | - grown 69 | - heard 70 | - held 71 | - hidden 72 | - hit 73 | - hung 74 | - hurt 75 | - kept 76 | - knelt 77 | - knit 78 | - known 79 | - laid 80 | - lain 81 | - leapt 82 | - learnt 83 | - led 84 | - left 85 | - lent 86 | - let 87 | - lighted 88 | - lost 89 | - made 90 | - meant 91 | - met 92 | - misspelt 93 | - mistaken 94 | - mown 95 | - overcome 96 | - overdone 97 | - overtaken 98 | - overthrown 99 | - paid 100 | - pled 101 | - proven 102 | - put 103 | - quit 104 | - read 105 | - rid 106 | - ridden 107 | - risen 108 | - run 109 | - rung 110 | - said 111 | - sat 112 | - sawn 113 | - seen 114 | - sent 115 | - set 116 | - sewn 117 | - shaken 118 | - shaven 119 | - shed 120 | - shod 121 | - shone 122 | - shorn 123 | - shot 124 | - shown 125 | - shrunk 126 | - shut 127 | - slain 128 | - slept 129 | - slid 130 | - slit 131 | - slung 132 | - smitten 133 | - sold 134 | - sought 135 | - sown 136 | - sped 137 | - spent 138 | - spilt 139 | - spit 140 | - split 141 | - spoken 142 | - spread 143 | - sprung 144 | - spun 145 | - stolen 146 | - stood 147 | - stridden 148 | - striven 149 | - struck 150 | - strung 151 | - stuck 152 | - stung 153 | - stunk 154 | - sung 155 | - sunk 156 | - swept 157 | - swollen 158 | - sworn 159 | - swum 160 | - swung 161 | - taken 162 | - taught 163 | - thought 164 | - thrived 165 | - thrown 166 | - thrust 167 | - told 168 | - torn 169 | - trodden 170 | - understood 171 | - upheld 172 | - upset 173 | - wed 174 | - wept 175 | - withheld 176 | - withstood 177 | - woken 178 | - won 179 | - worn 180 | - wound 181 | - woven 182 | - written 183 | - wrung 184 | -------------------------------------------------------------------------------- /vale-styles/write-good/README.md: -------------------------------------------------------------------------------- 1 | Based on [write-good](https://github.com/btford/write-good). 2 | 3 | > Naive linter for English prose for developers who can't write good and wanna learn to do other stuff good too. 4 | 5 | ``` 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2014 Brian Ford 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | ``` 28 | -------------------------------------------------------------------------------- /vale-styles/write-good/So.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't start a sentence with '%s'." 3 | level: error 4 | raw: 5 | - '(?:[;-]\s)so[\s,]|\bSo[\s,]' 6 | -------------------------------------------------------------------------------- /vale-styles/write-good/ThereIs.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't start a sentence with '%s'." 3 | ignorecase: false 4 | level: error 5 | raw: 6 | - '(?:[;-]\s)There\s(is|are)|\bThere\s(is|are)\b' 7 | -------------------------------------------------------------------------------- /vale-styles/write-good/TooWordy.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' is too wordy." 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - a number of 7 | - abundance 8 | - accede to 9 | - accelerate 10 | - accentuate 11 | - accompany 12 | - accomplish 13 | - accorded 14 | - accrue 15 | - acquiesce 16 | - acquire 17 | - additional 18 | - adjacent to 19 | - adjustment 20 | - admissible 21 | - advantageous 22 | - adversely impact 23 | - advise 24 | - aforementioned 25 | - aggregate 26 | - aircraft 27 | - all of 28 | - all things considered 29 | - alleviate 30 | - allocate 31 | - along the lines of 32 | - already existing 33 | - alternatively 34 | - amazing 35 | - ameliorate 36 | - anticipate 37 | - apparent 38 | - appreciable 39 | - as a matter of fact 40 | - as a means of 41 | - as far as I'm concerned 42 | - as of yet 43 | - as to 44 | - as yet 45 | - ascertain 46 | - assistance 47 | - at the present time 48 | - at this time 49 | - attain 50 | - attributable to 51 | - authorize 52 | - because of the fact that 53 | - belated 54 | - benefit from 55 | - bestow 56 | - by means of 57 | - by virtue of 58 | - by virtue of the fact that 59 | - cease 60 | - close proximity 61 | - commence 62 | - comply with 63 | - concerning 64 | - consequently 65 | - consolidate 66 | - constitutes 67 | - demonstrate 68 | - depart 69 | - designate 70 | - discontinue 71 | - due to the fact that 72 | - each and every 73 | - economical 74 | - eliminate 75 | - elucidate 76 | - employ 77 | - endeavor 78 | - enumerate 79 | - equitable 80 | - equivalent 81 | - evaluate 82 | - evidenced 83 | - exclusively 84 | - expedite 85 | - expend 86 | - expiration 87 | - facilitate 88 | - factual evidence 89 | - feasible 90 | - finalize 91 | - first and foremost 92 | - for all intents and purposes 93 | - for the most part 94 | - for the purpose of 95 | - forfeit 96 | - formulate 97 | - have a tendency to 98 | - honest truth 99 | - however 100 | - if and when 101 | - impacted 102 | - implement 103 | - in a manner of speaking 104 | - in a timely manner 105 | - in a very real sense 106 | - in accordance with 107 | - in addition 108 | - in all likelihood 109 | - in an effort to 110 | - in between 111 | - in excess of 112 | - in lieu of 113 | - in light of the fact that 114 | - in many cases 115 | - in my opinion 116 | - in order to 117 | - in regard to 118 | - in some instances 119 | - in terms of 120 | - in the case of 121 | - in the event that 122 | - in the final analysis 123 | - in the nature of 124 | - in the near future 125 | - in the process of 126 | - inception 127 | - incumbent upon 128 | - indicate 129 | - indication 130 | - initiate 131 | - irregardless 132 | - is applicable to 133 | - is authorized to 134 | - is responsible for 135 | - it is 136 | - it is essential 137 | - it seems that 138 | - it was 139 | - magnitude 140 | - maximum 141 | - methodology 142 | - minimize 143 | - minimum 144 | - modify 145 | - monitor 146 | - multiple 147 | - necessitate 148 | - nevertheless 149 | - not certain 150 | - not many 151 | - not often 152 | - not unless 153 | - not unlike 154 | - notwithstanding 155 | - null and void 156 | - numerous 157 | - objective 158 | - obligate 159 | - obtain 160 | - on the contrary 161 | - on the other hand 162 | - one particular 163 | - optimum 164 | - overall 165 | - owing to the fact that 166 | - participate 167 | - particulars 168 | - pass away 169 | - pertaining to 170 | - point in time 171 | - portion 172 | - possess 173 | - preclude 174 | - previously 175 | - prior to 176 | - prioritize 177 | - procure 178 | - proficiency 179 | - provided that 180 | - purchase 181 | - put simply 182 | - readily apparent 183 | - refer back 184 | - regarding 185 | - relocate 186 | - remainder 187 | - remuneration 188 | - requirement 189 | - reside 190 | - residence 191 | - retain 192 | - satisfy 193 | - shall 194 | - should you wish 195 | - similar to 196 | - solicit 197 | - span across 198 | - strategize 199 | - subsequent 200 | - substantial 201 | - successfully complete 202 | - sufficient 203 | - terminate 204 | - the month of 205 | - the point I am trying to make 206 | - therefore 207 | - time period 208 | - took advantage of 209 | - transmit 210 | - transpire 211 | - type of 212 | - until such time as 213 | - utilization 214 | - utilize 215 | - validate 216 | - various different 217 | - what I mean to say is 218 | - whether or not 219 | - with respect to 220 | - with the exception of 221 | - witnessed 222 | -------------------------------------------------------------------------------- /vale-styles/write-good/Weasel.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' is a weasel word!" 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - absolutely 7 | - accidentally 8 | - additionally 9 | - allegedly 10 | - alternatively 11 | - angrily 12 | - anxiously 13 | - approximately 14 | - awkwardly 15 | - badly 16 | - barely 17 | - beautifully 18 | - blindly 19 | - boldly 20 | - bravely 21 | - brightly 22 | - briskly 23 | - bristly 24 | - bubbly 25 | - busily 26 | - calmly 27 | - carefully 28 | - carelessly 29 | - cautiously 30 | - cheerfully 31 | - clearly 32 | - closely 33 | - coldly 34 | - completely 35 | - consequently 36 | - correctly 37 | - courageously 38 | - crinkly 39 | - cruelly 40 | - crumbly 41 | - cuddly 42 | - currently 43 | - daily 44 | - daringly 45 | - deadly 46 | - definitely 47 | - deliberately 48 | - doubtfully 49 | - dumbly 50 | - eagerly 51 | - early 52 | - easily 53 | - elegantly 54 | - enormously 55 | - enthusiastically 56 | - equally 57 | - especially 58 | - eventually 59 | - exactly 60 | - exceedingly 61 | - exclusively 62 | - extremely 63 | - fairly 64 | - faithfully 65 | - fatally 66 | - fiercely 67 | - finally 68 | - fondly 69 | - few 70 | - foolishly 71 | - fortunately 72 | - frankly 73 | - frantically 74 | - generously 75 | - gently 76 | - giggly 77 | - gladly 78 | - gracefully 79 | - greedily 80 | - happily 81 | - hardly 82 | - hastily 83 | - healthily 84 | - heartily 85 | - helpfully 86 | - honestly 87 | - hourly 88 | - hungrily 89 | - hurriedly 90 | - immediately 91 | - impatiently 92 | - inadequately 93 | - ingeniously 94 | - innocently 95 | - inquisitively 96 | - interestingly 97 | - irritably 98 | - jiggly 99 | - joyously 100 | - justly 101 | - kindly 102 | - largely 103 | - lately 104 | - lazily 105 | - likely 106 | - literally 107 | - lonely 108 | - loosely 109 | - loudly 110 | - loudly 111 | - luckily 112 | - madly 113 | - many 114 | - mentally 115 | - mildly 116 | - monthly 117 | - mortally 118 | - mostly 119 | - mysteriously 120 | - neatly 121 | - nervously 122 | - nightly 123 | - noisily 124 | - normally 125 | - obediently 126 | - occasionally 127 | - only 128 | - openly 129 | - painfully 130 | - particularly 131 | - patiently 132 | - perfectly 133 | - politely 134 | - poorly 135 | - powerfully 136 | - presumably 137 | - previously 138 | - promptly 139 | - punctually 140 | - quarterly 141 | - quickly 142 | - quietly 143 | - rapidly 144 | - rarely 145 | - really 146 | - recently 147 | - recklessly 148 | - regularly 149 | - remarkably 150 | - relatively 151 | - reluctantly 152 | - repeatedly 153 | - rightfully 154 | - roughly 155 | - rudely 156 | - sadly 157 | - safely 158 | - selfishly 159 | - sensibly 160 | - seriously 161 | - sharply 162 | - shortly 163 | - shyly 164 | - significantly 165 | - silently 166 | - simply 167 | - sleepily 168 | - slowly 169 | - smartly 170 | - smelly 171 | - smoothly 172 | - softly 173 | - solemnly 174 | - sparkly 175 | - speedily 176 | - stealthily 177 | - sternly 178 | - stupidly 179 | - substantially 180 | - successfully 181 | - suddenly 182 | - surprisingly 183 | - suspiciously 184 | - swiftly 185 | - tenderly 186 | - tensely 187 | - thoughtfully 188 | - tightly 189 | - timely 190 | - truthfully 191 | - unexpectedly 192 | - unfortunately 193 | - usually 194 | - very 195 | - victoriously 196 | - violently 197 | - vivaciously 198 | - warmly 199 | - waverly 200 | - weakly 201 | - wearily 202 | - weekly 203 | - wildly 204 | - wisely 205 | - worldly 206 | - wrinkly 207 | - yearly 208 | -------------------------------------------------------------------------------- /vale-styles/write-good/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "feed": "https://github.com/errata-ai/write-good/releases.atom", 3 | "vale_version": ">=1.0.0" 4 | } 5 | --------------------------------------------------------------------------------