├── .github ├── ISSUE_TEMPLATE │ └── bug_report.yml ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── deploy-docs.yml │ ├── pypi.yml │ └── tests.yml ├── .gitignore ├── .isort.cfg ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── docs ├── Makefile └── source │ ├── _static │ └── logo.png │ ├── conf.py │ ├── how2package4ioos.md │ ├── index.rst │ └── ioos_pkg_skeleton.rst ├── ioos_pkg_skeleton ├── __init__.py └── ioos_pkg_skeleton.py ├── notebooks ├── IOOS-Python-Package-Skeleton.ipynb └── tutorial.ipynb ├── pyproject.toml ├── requirements-dev.txt ├── requirements.txt └── tests └── test_ioos_pkg_skeleton.py /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report that something is not working as expected 3 | labels: ["Type: Bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking the time to report a bug. 9 | - type: textarea 10 | id: description 11 | attributes: 12 | label: What went wrong? 13 | description: This should explain what went wrong and what you expected to happen. 14 | placeholder: Describe the bug. You can attach example code and error messages in the separate fields below. 15 | validations: 16 | required: true 17 | - type: dropdown 18 | id: os 19 | attributes: 20 | label: Operating System 21 | description: What platform are you running on? 22 | options: 23 | - Windows 24 | - MacOS 25 | - Linux 26 | - Other 27 | validations: 28 | required: true 29 | - type: input 30 | id: version 31 | attributes: 32 | label: Version 33 | description: What version of the package are you using? 34 | validations: 35 | required: true 36 | - type: input 37 | id: python 38 | attributes: 39 | label: Python Version 40 | description: What version of Python are you using? 41 | validations: 42 | required: true 43 | - type: textarea 44 | id: code 45 | attributes: 46 | label: Code to Reproduce 47 | description: | 48 | Please include sample code to reproduce the problem. A ["Minimal, Complete and Verifiable Example"](http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) will make it much easier for maintainers to help you. 49 | render: python 50 | placeholder: "# Your code here" 51 | validations: 52 | required: true 53 | - type: textarea 54 | id: error 55 | attributes: 56 | label: Errors, Traceback, and Logs 57 | description: | 58 | If applicable, share with us the Error and full Traceback provided by your interpreter. 59 | render: pytb 60 | placeholder: | 61 | # Your Traceback here 62 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/keeping-your-actions-up-to-date-with-dependabot 2 | 3 | version: 2 4 | updates: 5 | 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | interval: "daily" 10 | labels: 11 | - "Bot" 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | #### Description Of Changes in the Pull Request 9 | 10 | 17 | 18 | #### Checklist 19 | 20 | - [ ] I read the [CONTRIBUTING.md](https://github.com/ioos/ioos-python-package-skeleton/blob/main/CONTRIBUTING.md) guide 21 | - [ ] Closes #xxxx 22 | - [ ] Added Tests 23 | - [ ] Added Documented of the changes/features 24 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | release: 9 | types: 10 | - published 11 | 12 | jobs: 13 | build-docs: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: checkout 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Setup Micromamba 23 | uses: mamba-org/setup-micromamba@v2 24 | with: 25 | environment-name: TEST 26 | init-shell: bash 27 | create-args: >- 28 | python=3 pip 29 | --file requirements.txt 30 | --file requirements-dev.txt 31 | --channel conda-forge 32 | 33 | - name: Install package 34 | shell: bash -l {0} 35 | run: | 36 | python -m pip install -e . --no-deps --force-reinstall 37 | 38 | - name: Build documentation 39 | shell: bash -l {0} 40 | run: | 41 | set -e 42 | jupyter nbconvert --to notebook --execute notebooks/tutorial.ipynb --output=tutorial-output.ipynb 43 | mv notebooks/*output.ipynb docs/source/ 44 | pushd docs 45 | make clean html linkcheck 46 | popd 47 | 48 | - name: Deploy 49 | if: success() && github.event_name == 'release' 50 | uses: peaceiris/actions-gh-pages@v4 51 | with: 52 | github_token: ${{ secrets.GITHUB_TOKEN }} 53 | publish_dir: docs/build/html 54 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | release: 9 | types: 10 | - published 11 | 12 | jobs: 13 | packages: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: "3.x" 22 | 23 | - name: Get tags 24 | run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* 25 | shell: bash 26 | 27 | - name: Install build tools 28 | run: | 29 | python -m pip install --upgrade pip build 30 | 31 | shell: bash 32 | 33 | - name: Build binary wheel 34 | run: python -m build --sdist --wheel . --outdir dist 35 | 36 | - name: CheckFiles 37 | run: | 38 | ls dist 39 | shell: bash 40 | 41 | - name: Test wheels 42 | run: | 43 | cd dist && python -m pip install *.whl 44 | python -m pip install --upgrade twine 45 | python -m twine check * 46 | shell: bash 47 | 48 | - name: Publish a Python distribution to PyPI 49 | if: success() && github.event_name == 'release' 50 | uses: pypa/gh-action-pypi-publish@release/v1 51 | with: 52 | user: __token__ 53 | password: ${{ secrets.PYPI_PASSWORD }} 54 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [main] 7 | 8 | jobs: 9 | run: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | python-version: ["3.8", "3.9", "3.10", "3.11"] 14 | os: [windows-latest, ubuntu-latest, macos-latest] 15 | fail-fast: false 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Setup Micromamba ${{ matrix.python-version }} 23 | uses: mamba-org/setup-micromamba@v2 24 | with: 25 | environment-name: TEST 26 | init-shell: bash 27 | create-args: >- 28 | python=${{ matrix.python-version }} pip 29 | --file requirements.txt 30 | --file requirements-dev.txt 31 | --channel conda-forge 32 | 33 | - name: Install package 34 | shell: bash -l {0} 35 | run: | 36 | python -m pip install -e . --no-deps --force-reinstall 37 | 38 | - name: Tests 39 | shell: bash -l {0} 40 | run: | 41 | python -m pytest -n 2 -rxs --cov=ioos_pkg_skeleton tests 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *-output.ipynb 2 | *.egg-info 3 | .coverage 4 | .ipynb_checkpoints/ 5 | .mypy_cache/ 6 | .pytest_cache/ 7 | .vscode/ 8 | __pycache__/ 9 | build/ 10 | dist/ 11 | ioos_pkg_skeleton/_version.py 12 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | known_third_party = numpy,pytest,requests,setuptools 3 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v4.4.0 5 | hooks: 6 | - id: trailing-whitespace 7 | exclude: tests/data 8 | - id: check-ast 9 | - id: debug-statements 10 | - id: end-of-file-fixer 11 | - id: check-docstring-first 12 | - id: check-added-large-files 13 | - id: requirements-txt-fixer 14 | - id: file-contents-sorter 15 | files: requirements-dev.txt 16 | 17 | - repo: https://github.com/econchick/interrogate 18 | rev: 1.5.0 19 | hooks: 20 | - id: interrogate 21 | exclude: ^(docs|setup.py|tests) 22 | args: [--config=pyproject.toml] 23 | 24 | - repo: https://github.com/keewis/blackdoc 25 | rev: v0.3.8 26 | hooks: 27 | - id: blackdoc 28 | 29 | - repo: https://github.com/charliermarsh/ruff-pre-commit 30 | rev: v0.0.282 31 | hooks: 32 | - id: ruff 33 | 34 | - repo: https://github.com/psf/black 35 | rev: 23.7.0 36 | hooks: 37 | - id: black 38 | language_version: python3 39 | 40 | - repo: https://github.com/codespell-project/codespell 41 | rev: v2.2.5 42 | hooks: 43 | - id: codespell 44 | args: 45 | - --quiet-level=2 46 | 47 | - repo: https://github.com/asottile/add-trailing-comma 48 | rev: v3.0.1 49 | hooks: 50 | - id: add-trailing-comma 51 | 52 | - repo: https://github.com/tox-dev/pyproject-fmt 53 | rev: "0.13.0" 54 | hooks: 55 | - id: pyproject-fmt 56 | 57 | - repo: https://github.com/aio-libs/sort-all 58 | rev: "v1.2.0" 59 | hooks: 60 | - id: sort-all 61 | types: [file, python] 62 | 63 | 64 | ci: 65 | autofix_commit_msg: | 66 | [pre-commit.ci] auto fixes from pre-commit.com hooks 67 | 68 | for more information, see https://pre-commit.ci 69 | autofix_prs: false 70 | autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate' 71 | autoupdate_schedule: monthly 72 | skip: [] 73 | submodules: false 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributors Guide 2 | 3 | Are you interested in helping out? 4 | Have a few minutes to tackle an issue? 5 | In this guide we will get you setup into contributing to our project! 6 | 7 | ## Setting up your development environment 8 | 9 | We recommend using the [conda](https://conda.io/docs/) package manager for your environments. 10 | Our recommended setup for contributing is: 11 | 12 | 1. Install [miniconda](https://docs.conda.io/en/latest/miniconda.html) on your system. 13 | You may have to restart your prompt for the remaining steps to work. 14 | 15 | 2. Now, with a local clone, of your fork\* you can create a development environment with: 16 | 17 | ```shell 18 | conda create --name MYENV python=3 --file requirements.txt --file requirements-dev.txt 19 | ``` 20 | 21 | 3. The changes should be made via GitHub pull requests\* against ``main``. 22 | 23 | 24 | ## More Questions? 25 | 26 | If you're stuck somewhere or are interested in being a part of the community in 27 | other ways, feel free to contact us! 28 | 29 | ## Further Reading 30 | 31 | There are a ton of great resources out there on contributing to open source and on the 32 | importance of writing tested and maintainable software. 33 | 34 | * [How to Contribute to Open Source Guide](https://opensource.guide/how-to-contribute/) 35 | * [Zen of Scientific Software Maintenance](https://jrleeman.github.io/ScientificSoftwareMaintenance/) 36 | 37 | **Working on your first Pull Request?** You can learn how from this video series 38 | [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github), 39 | Aaron Meurer's [tutorial on the git workflow](https://www.asmeurer.com/git-workflow/), or the 40 | guide [“How to Contribute to Open Source"](https://opensource.guide/how-to-contribute/). 41 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2017 AUTHOR NAME 2 | 3 | 4 | Redistribution and use in source and binary forms, 5 | with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 28 | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.txt 2 | include README.md 3 | include pyproject.toml 4 | 5 | # CHANGE PKG NAME HERE 6 | graft ioos_pkg_skeleton 7 | 8 | prune docs 9 | prune tests 10 | prune notebooks 11 | prune *.egg-info 12 | 13 | global-exclude *.nc 14 | 15 | exclude *.yml 16 | exclude *.enc 17 | exclude .gitignore 18 | exclude .isort.cfg 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ioos_pkg_skeleton 2 | 3 | [![Build Status](https://travis-ci.com/ioos/ioos-python-package-skeleton.svg?branch=master)](https://travis-ci.com/ioos/ioos-python-package-skeleton) 4 | 5 | Quick description 6 | 7 | ### Documentation and code 8 | 9 | URLs for the docs and code. 10 | 11 | ### Installation 12 | 13 | For `conda` users you can 14 | 15 | ```shell 16 | conda install --channel conda-forge ioos_pkg_skeleton 17 | ``` 18 | 19 | or, if you are a `pip` users 20 | 21 | ```shell 22 | pip install ioos_pkg_skeleton 23 | ``` 24 | 25 | ### Example 26 | 27 | ```python 28 | from ioos_pkg_skeleton import ioos_pkg_skeleton 29 | 30 | 31 | ioos_pkg_skeleton.meaning_of_life_url() 32 | ``` 33 | 34 | 35 | ## Get in touch 36 | 37 | Report bugs, suggest features or view the source code on [GitHub](https://github.com/ioos/ioos_pkg_skeleton/issues). 38 | 39 | 40 | ## License and copyright 41 | 42 | ioos_pkg_skeleton is licensed under BSD 3-Clause "New" or "Revised" License (BSD-3-Clause). 43 | 44 | Development occurs on GitHub at . 45 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = ioos_pkg_skeleton 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/source/_static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioos/ioos-python-package-skeleton/11b20312e41f470903bc2b40908a220fa22e30ec/docs/source/_static/logo.png -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # ioos_pkg_skeleton documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Oct 9 21:28:42 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | # import os 20 | # import sys 21 | # sys.path.insert(0, os.path.abspath('.')) 22 | 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | "sphinx.ext.autodoc", 35 | "sphinx.ext.viewcode", 36 | "sphinx.ext.napoleon", 37 | "nbsphinx", 38 | "myst_parser", 39 | ] 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ["_templates"] 43 | 44 | # The suffix(es) of source filenames. 45 | # You can specify multiple suffix as a list of string: 46 | # 47 | source_suffix = [".rst", ".md"] 48 | 49 | # The master toctree document. 50 | master_doc = "index" 51 | 52 | # General information about the project. 53 | project = "ioos_pkg_skeleton" 54 | copyright = "2017, Filipe Fernandes" 55 | author = "Filipe Fernandes" 56 | 57 | # The version info for the project you're documenting, acts as replacement for 58 | # |version| and |release|, also used in various other places throughout the 59 | # built documents. 60 | # 61 | from ioos_pkg_skeleton import __version__ as VERSION # noqa 62 | 63 | version = VERSION 64 | # The full version, including alpha/beta/rc tags. 65 | release = VERSION 66 | 67 | # The language for content autogenerated by Sphinx. Refer to documentation 68 | # for a list of supported languages. 69 | # 70 | # This is also used if you do content translation via gettext catalogs. 71 | # Usually you set "language" from the command line for these cases. 72 | language = "en" 73 | 74 | # List of patterns, relative to source directory, that match files and 75 | # directories to ignore when looking for source files. 76 | # This patterns also effect to html_static_path and html_extra_path 77 | exclude_patterns = [] 78 | 79 | # The name of the Pygments (syntax highlighting) style to use. 80 | pygments_style = "sphinx" 81 | 82 | # If true, `todo` and `todoList` produce output, else they produce nothing. 83 | todo_include_todos = False 84 | 85 | 86 | # -- Options for HTML output ---------------------------------------------- 87 | 88 | # The theme to use for HTML and HTML Help pages. See the documentation for 89 | # a list of builtin themes. 90 | # 91 | html_theme = "alabaster" 92 | 93 | # Theme options are theme-specific and customize the look and feel of a theme 94 | # further. For a list of options available for each theme, see the 95 | # documentation. 96 | # 97 | html_theme_options = { 98 | "logo": "logo.png", 99 | "logo_name": "ioos_pkg_skeleton", 100 | "github_user": "ioos", 101 | "github_repo": "ioos_pkg_skeleton", 102 | "github_banner": True, 103 | "travis_button": True, 104 | "fixed_sidebar": True, 105 | } 106 | 107 | 108 | # Add any paths that contain custom static files (such as style sheets) here, 109 | # relative to this directory. They are copied after the builtin static files, 110 | # so a file named "default.css" will overwrite the builtin "default.css". 111 | html_static_path = ["_static"] 112 | 113 | # Custom sidebar templates, must be a dictionary that maps document names 114 | # to template names. 115 | # 116 | # This is required for the alabaster theme 117 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 118 | html_sidebars = { 119 | "**": [ 120 | "about.html", 121 | "navigation.html", 122 | "relations.html", # needs 'show_related': True theme option to display 123 | "searchbox.html", 124 | "donate.html", 125 | ], 126 | } 127 | 128 | 129 | # -- Options for HTMLHelp output ------------------------------------------ 130 | 131 | # Output file base name for HTML help builder. 132 | htmlhelp_basename = "ioos_pkg_skeletondoc" 133 | 134 | 135 | # -- Options for LaTeX output --------------------------------------------- 136 | 137 | latex_elements = { 138 | # The paper size ('letterpaper' or 'a4paper'). 139 | # 140 | # 'papersize': 'letterpaper', 141 | # The font size ('10pt', '11pt' or '12pt'). 142 | # 143 | # 'pointsize': '10pt', 144 | # Additional stuff for the LaTeX preamble. 145 | # 146 | # 'preamble': '', 147 | # Latex figure (float) alignment 148 | # 149 | # 'figure_align': 'htbp', 150 | } 151 | 152 | # Grouping the document tree into LaTeX files. List of tuples 153 | # (source start file, target name, title, 154 | # author, documentclass [howto, manual, or own class]). 155 | latex_documents = [ 156 | ( 157 | master_doc, 158 | "ioos_pkg_skeleton.tex", 159 | "ioos_pkg_skeleton Documentation", 160 | "Filipe Fernandes", 161 | "manual", 162 | ), 163 | ] 164 | 165 | 166 | # -- Options for manual page output --------------------------------------- 167 | 168 | # One entry per manual page. List of tuples 169 | # (source start file, name, description, authors, manual section). 170 | man_pages = [ 171 | ( 172 | master_doc, 173 | "ioos_pkg_skeleton", 174 | "ioos_pkg_skeleton Documentation", 175 | [author], 176 | 1, 177 | ), 178 | ] 179 | 180 | 181 | # -- Options for Texinfo output ------------------------------------------- 182 | 183 | # Grouping the document tree into Texinfo files. List of tuples 184 | # (source start file, target name, title, author, 185 | # dir menu entry, description, category) 186 | texinfo_documents = [ 187 | ( 188 | master_doc, 189 | "ioos_pkg_skeleton", 190 | "ioos_pkg_skeleton Documentation", 191 | author, 192 | "ioos_pkg_skeleton", 193 | "One line description of project.", 194 | "Miscellaneous", 195 | ), 196 | ] 197 | -------------------------------------------------------------------------------- /docs/source/how2package4ioos.md: -------------------------------------------------------------------------------- 1 | # IOOS packaging guidelines 2 | 3 | ## Why not a cookie-cutter? 4 | 5 | IOOS packages are developed by multiple partners and enforcing a 6 | cookie-cutter would cause more harm than good. 7 | That is why we prefer a set of guidelines where only the parts that are of interested to the user can be copied instead. 8 | 9 | This document describes these guidelines and how to implement them in an existing project or a new one. 10 | 11 | If you know enough about Python packaging just visit 12 | [https://github.com/ioos/ioos-python-package-skeleton](https://github.com/ioos/ioos-python-package-skeleton) and copy the bits that are relevant to your project. 13 | 14 | ## Project structure 15 | 16 | Almost all python packages are structure as following: 17 | 18 | ``` 19 | |-docs 20 | | |-source 21 | | | |-_static 22 | | |-build 23 | |-tests 24 | |-ioos_pkg_skeleton 25 | |-notebooks 26 | |-README.md 27 | |-LICENSE.txt 28 | ``` 29 | 30 | 31 | Sometimes the `tests` folder goes inside the actual module. 32 | We recommend that only if shipping the tests is important, e.g compiled modules. 33 | If your module is pure Python it is fine to leave them outside of the package. 34 | 35 | Always start writing tests and document from day 1! 36 | We also recommend to write notebooks with examples that can be build as part of the docs. 37 | 38 | Note that you should always have a README in your projects. 39 | Markdown is a good format because it renders automatically on GitHub and is support on PyPI. 40 | 41 | While IOOS does not recommend any particular license we prefer projects with OSI approved license, 42 | the most common one is BSD-3-Clause. 43 | 44 | 45 | ## PEP 517/518 46 | 47 | [PEP 517](https://peps.python.org/pep-0517/) species a build-system that is 48 | independent of the format for source trees (your code). 49 | The idea is to allow for other tools to become Python builds systems, 50 | like `flit` and `poetry`, 51 | via a minimum interface installation with `pip`. 52 | PEP 517 really shines when combined with [PEP 518](https://peps.python.org/pep-0518), 53 | which specifies a minimum build system requirements via the `pyproject.toml` file. 54 | 55 | ```toml 56 | [build-system] 57 | requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4", "cython", "numpy"] 58 | build-backend = "setuptools.build_meta" 59 | ``` 60 | 61 | When this file is present `pip` knows that it should install everything on requires before building the packages, 62 | and knows that it should be built with `setuptools`. 63 | 64 | The main advantages of using these PEPs together are: 65 | 66 | - standardized non-executable config file; 67 | - non-executable setup.py (safer installs without crazy workarounds); 68 | - support many backends with one spec: 69 | - poetry, setuptools, pipenv(?), flit, conda, etc; 70 | - all should support pip installs. 71 | - ensure that setup dependencies will be available at build time. 72 | 73 | 74 | For IOOS packages we recommend to keep a bare bones `setup.py`, 75 | for backwards compatibility, and to move all the package metadata to a `setup.cfg`, 76 | while keeping the `pyproject.toml` only for the build information. 77 | 78 | ## `setup.py` 79 | 80 | Most `setup.py` files can now be simplified to just the version handling and build call: 81 | 82 | ```python 83 | from setuptools import setup 84 | 85 | setup( 86 | use_scm_version={ 87 | "write_to": "ioos_pkg_skeleton/_version.py", 88 | "write_to_template": '__version__ = "{version}"', 89 | "tag_regex": r"^(?Pv)?(?P[^\+]+)(?P.*)?$", 90 | } 91 | ) 92 | 93 | ``` 94 | 95 | We recommend the use of `setuptools-scm`. 96 | Just add `setuptools-scm` to your development/build dependencies and the lines above in your `setup.py` file. 97 | The version will be automatically generated via tags and changes in your version control system. 98 | 99 | PS: `setuptools-scm` can use a file or `pkg_resources` to get the version number. 100 | We do not recommend `pkg_resources`because, depending on the number of packages installed, 101 | that can lead to a significant overhead at import time. 102 | 103 | ## `setup.cfg` 104 | 105 | While could use the `pyproject.toml` for most of your project configuration we recommend 106 | to split that between the `setup.cfg` and `pyproject.toml` for readability. 107 | The former will have the package metadata and tools configuration while the latter will specify the build system. 108 | 109 | ```cfg 110 | [metadata] 111 | name = ioos_pkg_skeleton 112 | description = My Awesome module 113 | author = AUTHOR NAME 114 | author_email = AUTHOR@EMAIL.COM 115 | url = https://github.com/ioos/ioos-python-package-skeleton 116 | long_description = file: README.md 117 | long_description_content_type = text/markdown 118 | license = BSD-3-Clause 119 | license_file = LICENSE.txt 120 | classifiers = 121 | Development Status :: 5 - Production/Stable 122 | Intended Audience :: Science/Research 123 | Operating System :: OS Independent 124 | License :: OSI Approved :: BSD License 125 | Programming Language :: Python 126 | Programming Language :: Python :: 3 127 | Programming Language :: Python :: 3.6 128 | Programming Language :: Python :: 3.7 129 | Programming Language :: Python :: 3.8 130 | Topic :: Scientific/Engineering 131 | 132 | [options] 133 | zip_safe = False 134 | install_requires = 135 | numpy 136 | requests 137 | python_requires = >=3.6 138 | packages = find: 139 | 140 | [sdist] 141 | formats = gztar 142 | 143 | [check-manifest] 144 | ignore = 145 | *.yml 146 | *.yaml 147 | .coveragerc 148 | docs 149 | docs/* 150 | *.enc 151 | notebooks 152 | notebooks/* 153 | tests 154 | tests/* 155 | 156 | [flake8] 157 | max-line-length = 105 158 | select = C,E,F,W,B,B950 159 | ignore = E203, E501, W503 160 | exclude = ioos_pkg_skeleton/_version.py 161 | ``` 162 | 163 | The metadata and options fields are almost the same information that used to go in the `setup.py`. 164 | In addition to the metadata we can use this file to write the configuration for 165 | `flake8`, `check-manifest` and other tools. 166 | We strongly recommend adding a `check-manifest` test to avoid shipping broken tarballs. 167 | See more on tarball checks in the #travis-yaml section. 168 | 169 | Note: `flake8` can be handled exclusively via #pre-commit-hooks. 170 | 171 | ## `MANIFEST.in` 172 | 173 | Most of the problems we find with published tarballs is the lack of a required file at build time. 174 | That is we why recommend `check-manifest` to help you write your `MANIFEST.in` file. 175 | Here is an example that covers most cases: 176 | 177 | 178 | ``` 179 | include *.txt 180 | include LICENSE # Please consider the Windows users and use .txt 181 | include README.md 182 | 183 | recursive-include ioos_pkg_skeleton *.py 184 | ``` 185 | 186 | ## Do we still need a `requirements.txt` file? 187 | 188 | Sadly yes, PEP 517/518 do not allow for non-python dependencies in the spec. 189 | Even though the first two are already in the `setup.cfg` we cannot specify `libnetcdf` 190 | and `libgdal` without an external file. 191 | 192 | In order to make the package both `pip` and `conda` friendly we recommend to add the 193 | external dependencies as comments and write a parser to read them in your CI, 194 | or just duplicated them in the install section of your testing CI. 195 | 196 | For example, 197 | 198 | ``` 199 | numpy 200 | requests 201 | #conda: libnetcdf 202 | #conda: libgdal 203 | ``` 204 | 205 | One should also have a `requirements-dev.txt` with all the dependencies that are used to 206 | build the package, build documents, and perform tests. For example: 207 | 208 | ``` 209 | # code style/consistency 210 | black 211 | flake8 212 | flake8-builtins 213 | flake8-comprehensions 214 | flake8-mutable 215 | flake8-print 216 | isort 217 | pylint 218 | pytest-flake8 219 | # checks and tests 220 | check-manifest 221 | pytest 222 | pytest-cov 223 | pytest-xdist 224 | pre-commit 225 | # documentation 226 | doctr 227 | nbsphinx 228 | sphinx 229 | # build 230 | setuptools_scm 231 | twine 232 | wheel 233 | ``` 234 | 235 | ## Continuous Integration 236 | 237 | The easiest one to configure is GitHub Actions, 238 | 239 | ```yaml 240 | name: Tests 241 | 242 | on: 243 | pull_request: 244 | push: 245 | branches: [main] 246 | 247 | jobs: 248 | run: 249 | runs-on: ${{ matrix.os }} 250 | strategy: 251 | matrix: 252 | python-version: ["3.8", "3.9", "3.10", "3.11"] 253 | os: [windows-latest, ubuntu-latest, macos-latest] 254 | fail-fast: false 255 | 256 | steps: 257 | - uses: actions/checkout@v3 258 | with: 259 | fetch-depth: 0 260 | 261 | - name: Setup Micromamba ${{ matrix.python-version }} 262 | uses: mamba-org/setup-micromamba@v1 263 | with: 264 | environment-name: TEST 265 | init-shell: bash 266 | create-args: >- 267 | python=${{ matrix.python-version }} pip 268 | --file requirements.txt 269 | --file requirements-dev.txt 270 | --channel conda-forge 271 | 272 | - name: Install package 273 | shell: bash -l {0} 274 | run: | 275 | python -m pip install -e . --no-deps --force-reinstall 276 | 277 | - name: Tests 278 | shell: bash -l {0} 279 | run: | 280 | python -m pytest -n 2 -rxs --cov=ioos_pkg_skeleton tests 281 | ``` 282 | 283 | This configuration sets a test matrix with multiple python versions and OSes. 284 | The conda environment for the tests is created using the same requirement files as one would of with `pip` and the install section performs a simple `pip` installation to ensure everything works as expected on a user machine. 285 | 286 | The test section will run all the items in the matrix if the conditions are met. 287 | Note that the documentation section will also build for latest version, development, and the tagged version. 288 | 289 | PS: one can create a local development environment using the same commands as the CI. 290 | If you already have conda installed something like, 291 | 292 | ``` 293 | conda create --name TEST python=3 --file requirements.txt --file requirements-dev.txt 294 | ``` 295 | 296 | 297 | ## Configuring `pre-commit` locally 298 | 299 | With `pre-commit` we can run multiple checks every time we issue a new commit. 300 | These checks can also be run on https://pre-commit.ci/. 301 | This is useful when the contributors do not have `pre-commit` installed on their machine. 302 | 303 | The configuration below can be dropped in the project root. 304 | The checks selection are not comprehensive and not all of them are good for all the projects. 305 | We will leave as an exercise to the reader to determine which ones are best for your project. 306 | 307 | We do recommend `black` and `isort` for big projects with multiple contributors though because them help PR reviews by removing the code style from the equation. 308 | 309 | 310 | ```yaml 311 | repos: 312 | - repo: https://github.com/pre-commit/pre-commit-hooks 313 | rev: v3.1.0 314 | hooks: 315 | - id: trailing-whitespace 316 | exclude: tests/data 317 | - id: check-ast 318 | - id: debug-statements 319 | - id: end-of-file-fixer 320 | - id: check-docstring-first 321 | - id: check-added-large-files 322 | - id: requirements-txt-fixer 323 | - id: file-contents-sorter 324 | files: requirements-dev.txt 325 | 326 | - repo: https://gitlab.com/pycqa/flake8 327 | rev: 3.7.9 328 | hooks: 329 | - id: flake8 330 | exclude: docs/source/conf.py 331 | args: [--max-line-length=105, --ignore=E203,E501,W503, --select=select=C,E,F,W,B,B950] 332 | 333 | - repo: https://github.com/pre-commit/mirrors-isort 334 | rev: v4.3.21 335 | hooks: 336 | - id: isort 337 | additional_dependencies: [toml] 338 | args: [--project=ioos_pkg_skeleton, --multi-line=3, --lines-after-imports=2, --lines-between-types=1, --trailing-comma, --force-grid-wrap=0, --use-parentheses, --line-width=88] 339 | 340 | - repo: https://github.com/asottile/seed-isort-config 341 | rev: v2.1.1 342 | hooks: 343 | - id: seed-isort-config 344 | 345 | - repo: https://github.com/psf/black 346 | rev: stable 347 | hooks: 348 | - id: black 349 | language_version: python3 350 | 351 | - repo: https://github.com/pre-commit/mirrors-mypy 352 | rev: v0.770 353 | hooks: 354 | - id: mypy 355 | exclude: docs/source/conf.py 356 | args: [--ignore-missing-imports] 357 | 358 | ``` 359 | 360 | In order to run them in every commit one must install them with: 361 | 362 | ``` 363 | pre-commit install 364 | ``` 365 | 366 | Two other handy commands are running to all files (if you are configuring it for an existing project): 367 | 368 | ``` 369 | pre-commit run --all-files 370 | ``` 371 | 372 | and ignoring it in a commit if you don't want it to run: 373 | 374 | ``` 375 | git commit ioos_pkg_skeleton/some-dot-pwhy.py --no-verify 376 | ``` 377 | 378 | ## PyPI auto publishing with GitHub Actions 379 | 380 | For the `PYPI_PASSWORD` you can get the TOKEN from the PyPI website and add to GitHub's secrets. 381 | The rest of this GiTHub is mostly a boilerplate package building and installation testing. 382 | 383 | ```yaml 384 | name: Publish to PyPI 385 | 386 | on: 387 | pull_request: 388 | push: 389 | branches: [main] 390 | release: 391 | types: [published] 392 | 393 | jobs: 394 | packages: 395 | runs-on: ubuntu-latest 396 | steps: 397 | - uses: actions/checkout@v3 398 | 399 | - name: Set up Python 400 | uses: actions/setup-python@v3 401 | with: 402 | python-version: "3.x" 403 | 404 | - name: Get tags 405 | run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* 406 | shell: bash 407 | 408 | - name: Install build tools 409 | run: | 410 | python -m pip install --upgrade pip wheel setuptools setuptools_scm build twine 411 | 412 | shell: bash 413 | 414 | - name: Build binary wheel 415 | run: python -m build --sdist --wheel . --outdir dist 416 | 417 | - name: CheckFiles 418 | run: | 419 | ls dist 420 | shell: bash 421 | 422 | - name: Test wheels 423 | run: | 424 | cd dist && python -m pip install ioos_pkg_skeleton*.whl 425 | python -m twine check * 426 | shell: bash 427 | 428 | - name: Publish a Python distribution to PyPI 429 | if: success() && github.event_name == 'release' 430 | uses: pypa/gh-action-pypi-publish@release/v1 431 | with: 432 | user: __token__ 433 | password: ${{ secrets.PYPI_PASSWORD }} 434 | ``` 435 | 436 | ## gh-pages documentation auto publishing with GitHub Actions 437 | 438 | ```yaml 439 | name: Documentation 440 | 441 | on: 442 | pull_request: 443 | push: 444 | branches: 445 | - main 446 | release: 447 | types: 448 | - published 449 | 450 | jobs: 451 | build-docs: 452 | runs-on: ubuntu-latest 453 | 454 | steps: 455 | - name: checkout 456 | uses: actions/checkout@v3 457 | with: 458 | fetch-depth: 0 459 | 460 | - name: Setup Micromamba 461 | uses: mamba-org/setup-micromamba@v1 462 | with: 463 | environment-name: TEST 464 | init-shell: bash 465 | create-args: >- 466 | python=3 pip 467 | --file requirements.txt 468 | --file requirements-dev.txt 469 | --channel conda-forge 470 | 471 | - name: Install package 472 | shell: bash -l {0} 473 | run: | 474 | python -m pip install -e . --no-deps --force-reinstall 475 | 476 | - name: Build documentation 477 | shell: bash -l {0} 478 | run: | 479 | set -e 480 | jupyter nbconvert --to notebook --execute notebooks/tutorial.ipynb --output=tutorial-output.ipynb 481 | mv notebooks/*output.ipynb docs/source/ 482 | pushd docs 483 | make clean html linkcheck 484 | popd 485 | 486 | - name: Deploy 487 | if: success() && github.event_name == 'release' 488 | uses: peaceiris/actions-gh-pages@v3 489 | with: 490 | github_token: ${{ secrets.GITHUB_TOKEN }} 491 | publish_dir: docs/build/html 492 | ``` 493 | 494 | ## Summary 495 | 496 | For IOOS project we divided the guidelines here in "Must Have" and "Nice to Have." 497 | 498 | The Must Have list is: 499 | 500 | ``` 501 | - README 502 | - install instructions 503 | - License 504 | - docs 505 | - unittest tests 506 | - CIs 507 | ``` 508 | 509 | and the Nice to Have: 510 | 511 | ``` 512 | - automatic version number from tags (`setuptools-scm`) 513 | - auto-publish docs and tarball 514 | - tarball automated checks (`check-manifest`) 515 | - standard style: `black`, lints (`flake8`), `isort` 516 | - integration tests 517 | - Windows Testing 518 | - A package on both PyPI and conda-forge 519 | ``` 520 | 521 | ## Extras 522 | 523 | ``` 524 | - CONTRIBUTING.rst 525 | - .github/ 526 | ``` 527 | 528 | Please check out https://www.pyopensci.org/ 529 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Package Guidelines 2 | ================== 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | how2package4ioos.md 8 | 9 | ioos_pkg_skeleton 10 | ================= 11 | 12 | What is ioos_pkg_skeleton? 13 | -------------------------- 14 | 15 | The best module ever written! 16 | 17 | 18 | .. toctree:: 19 | :maxdepth: 3 20 | :caption: Contents: 21 | 22 | tutorial-output.ipynb 23 | ioos_pkg_skeleton 24 | 25 | Indices and tables 26 | ================== 27 | 28 | * :ref:`genindex` 29 | * :ref:`modindex` 30 | * :ref:`search` 31 | -------------------------------------------------------------------------------- /docs/source/ioos_pkg_skeleton.rst: -------------------------------------------------------------------------------- 1 | :mod:`ioos_pkg_skeleton API` 2 | ---------------------------- 3 | 4 | .. automodule:: ioos_pkg_skeleton.ioos_pkg_skeleton 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /ioos_pkg_skeleton/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ioos_pkg_skeleton is not a real package, just a set of best practices examples. 3 | """ 4 | 5 | from ioos_pkg_skeleton.ioos_pkg_skeleton import ( 6 | meaning_of_life, 7 | meaning_of_life_url, 8 | ) 9 | 10 | __all__ = [ 11 | "meaning_of_life", 12 | "meaning_of_life_url", 13 | ] 14 | 15 | try: 16 | from ._version import __version__ 17 | except ImportError: 18 | __version__ = "unknown" 19 | -------------------------------------------------------------------------------- /ioos_pkg_skeleton/ioos_pkg_skeleton.py: -------------------------------------------------------------------------------- 1 | """ 2 | ioos_pkg_skeleton 3 | 4 | My awesome ioos_pkg_skeleton 5 | """ 6 | 7 | import numpy as np 8 | import requests 9 | 10 | 11 | def meaning_of_life(n: int) -> np.ndarray: 12 | """Return the meaning of life n times.""" 13 | matrix = (n, n) 14 | return np.ones(matrix) * 42 15 | 16 | 17 | def meaning_of_life_url() -> str: 18 | """ 19 | Fetch the meaning of life from https://en.wikipedia.org/wiki/Main_Page. 20 | """ 21 | url = "https://en.wikipedia.org/api/rest_v1/page/summary/Monty_Python's_The_Meaning_of_Life" # noqa 22 | r = requests.get(url) 23 | r.raise_for_status() 24 | j = r.json() 25 | return j["extract"] 26 | -------------------------------------------------------------------------------- /notebooks/IOOS-Python-Package-Skeleton.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Python ~~packaging~~ (best practices and community building)\n", 8 | "\n", 9 | "## Jun 5, 2020\n", 10 | "\n", 11 | "Based on https://docs.google.com/presentation/d/11-fwwDKKMDc1dt-MrY1IhNUkhqax7AhwsxbynAvRAGU/edit#slide=id.g64bd9da3ea_0_8" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## Motivation\n", 19 | "\n", 20 | "\n", 21 | "![](https://imgs.xkcd.com/comics/tar.png)" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "## How bad is the packaging situation?\n", 29 | "\n", 30 | "1654 | 1987\n", 31 | ":-------------------------------------------------------------:|:-------------------------:\n", 32 | "![](https://imgs.xkcd.com/comics/universal_install_script.png) | ![](https://imgs.xkcd.com/comics/python_environment.png)" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "## One-stop shop for packaging needs\n", 40 | "\n", 41 | "\n", 42 | "https://github.com/ioos/ioos-python-package-skeleton" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "## Are we doing this right?\n", 50 | "\n", 51 | "(or “Why not a cookie-cutter?”)\n", 52 | "\n", 53 | "![](https://imgs.xkcd.com/comics/the_general_problem.png)" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "# Project structure\n", 61 | "\n", 62 | "\n", 63 | "```\n", 64 | "|-docs\n", 65 | "| |-source\n", 66 | "| | |-_static\n", 67 | "| |-build\n", 68 | "|-tests\n", 69 | "|-ioos_pkg_skeleton\n", 70 | "```\n", 71 | "\n", 72 | "Why are the tests outside of the module?" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "## Ciao setup.py\n", 80 | "\n", 81 | "```python\n", 82 | "from setuptools import setup\n", 83 | "\n", 84 | "setup(\n", 85 | " use_scm_version={\n", 86 | " \"write_to\": \"ioos_pkg_skeleton/_version.py\",\n", 87 | " \"write_to_template\": '__version__ = \"{version}\"',\n", 88 | " \"tag_regex\": r\"^(?Pv)?(?P[^\\+]+)(?P.*)?$\",\n", 89 | " }\n", 90 | ")\n", 91 | "\n", 92 | "```" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "## Aloha setup.cfg\n", 100 | "\n", 101 | "```cfg\n", 102 | "[metadata]\n", 103 | "name = ioos_pkg_skeleton\n", 104 | "description = My Awesome module\n", 105 | "author = AUTHOR NAME\n", 106 | "author_email = AUTHOR@EMAIL.COM\n", 107 | "url = https://github.com/ioos/ioos-python-package-skeleton\n", 108 | "long_description = file: README.md\n", 109 | "long_description_content_type = text/markdown\n", 110 | "license = BSD-3-Clause\n", 111 | "license_file = LICENSE.txt\n", 112 | "classifiers =\n", 113 | " Development Status :: 5 - Production/Stable\n", 114 | " Intended Audience :: Science/Research\n", 115 | " Operating System :: OS Independent\n", 116 | " License :: OSI Approved :: BSD License\n", 117 | " Programming Language :: Python\n", 118 | " Programming Language :: Python :: 3\n", 119 | " Programming Language :: Python :: 3.6\n", 120 | " Programming Language :: Python :: 3.7\n", 121 | " Programming Language :: Python :: 3.8\n", 122 | " Topic :: Scientific/Engineering\n", 123 | "\n", 124 | "[options]\n", 125 | "zip_safe = False\n", 126 | "install_requires =\n", 127 | " numpy\n", 128 | " requests\n", 129 | "python_requires = >=3.6\n", 130 | "packages = find:\n", 131 | "\n", 132 | "[sdist]\n", 133 | "formats = gztar\n", 134 | "\n", 135 | "[check-manifest]\n", 136 | "ignore =\n", 137 | " *.yml\n", 138 | " *.yaml\n", 139 | " .coveragerc\n", 140 | " docs\n", 141 | " docs/*\n", 142 | " *.enc\n", 143 | " notebooks\n", 144 | " notebooks/*\n", 145 | " tests\n", 146 | " tests/*\n", 147 | "\n", 148 | "[flake8]\n", 149 | "max-line-length = 105\n", 150 | "select = C,E,F,W,B,B950\n", 151 | "ignore = E203, E501, W503\n", 152 | "exclude = ioos_pkg_skeleton/_version.py\n", 153 | "```" 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": {}, 159 | "source": [ 160 | "## Do we still need a `requirements.txt`?\n", 161 | "\n", 162 | "```\n", 163 | "OWSLib>=0.8.3\n", 164 | "Jinja2>=2.7.3\n", 165 | "functools32==3.2.3-2; python_version < '3.2'\n", 166 | "#conda: libnetcdf\n", 167 | "#conda: libgdal\n", 168 | "```" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "## Code Pause\n", 176 | "\n", 177 | "\n", 178 | "![](https://imgs.xkcd.com/comics/wisdom_of_the_ancients.png)" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": {}, 184 | "source": [ 185 | "## PEP 517/518\n", 186 | "\n", 187 | "\n", 188 | "- standardized non-executable config file;\n", 189 | "- many backends, one spec:\n", 190 | " - poetry, setuptools, pipenv(?), flit, conda, etc;\n", 191 | " - all should support pip installs.\n", 192 | "\n", 193 | "\n", 194 | "Refs.:\n", 195 | "\n", 196 | "https://www.python.org/dev/peps/pep-0517\n", 197 | "\n", 198 | "https://www.python.org/dev/peps/pep-0518\n", 199 | "\n", 200 | "https://medium.com/@grassfedcode/pep-517-and-518-in-plain-english-47208ca8b7a6" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "## MANIFEST.in\n", 208 | "\n", 209 | "```\n", 210 | "include *.txt\n", 211 | "include LICENSE # Please consider the Windows users and use .txt\n", 212 | "include README.md\n", 213 | "\n", 214 | "recursive-include ioos_pkg_skeleton *.py\n", 215 | "```" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "## README.md\n", 223 | "\n", 224 | "\n", 225 | "You should always have a README in your projects!" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "## LICENSE\n", 233 | "\n", 234 | "\n", 235 | "```\n", 236 | "Copyright 2017 AUTHOR NAME\n", 237 | "\n", 238 | "\n", 239 | "Redistribution and use in source and binary forms,\n", 240 | "with or without modification,\n", 241 | "are permitted provided that the following conditions are met:\n", 242 | "\n", 243 | "1. Redistributions of source code must retain the above copyright notice,\n", 244 | " this list of conditions and the following disclaimer.\n", 245 | "\n", 246 | "2. Redistributions in binary form must reproduce the above copyright notice,\n", 247 | " this list of conditions and the following disclaimer in the documentation\n", 248 | " and/or other materials provided with the distribution.\n", 249 | "\n", 250 | "3. Neither the name of the copyright holder nor the names of its contributors\n", 251 | " may be used to endorse or promote products derived from this software\n", 252 | " without specific prior written permission.\n", 253 | "\n", 254 | "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n", 255 | "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n", 256 | "THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n", 257 | "ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n", 258 | "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n", 259 | "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n", 260 | "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n", 261 | "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n", 262 | "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY\n", 263 | "WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n", 264 | "```" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "## .travis.yml: matrix\n", 272 | "\n", 273 | "```yaml\n", 274 | "language: minimal\n", 275 | "\n", 276 | "sudo: false\n", 277 | "\n", 278 | "env:\n", 279 | " global:\n", 280 | " - secure: \"TOKEN\"\n", 281 | "\n", 282 | "matrix:\n", 283 | " fast_finish: true\n", 284 | " include:\n", 285 | " - name: \"python-3.6\"\n", 286 | " env: PY=3.6\n", 287 | " - name: \"python-3.8\"\n", 288 | " env: PY=3.8\n", 289 | " - name: \"coding_standards\"\n", 290 | " env: PY=3\n", 291 | " - name: \"tarball\"\n", 292 | " env: PY=3\n", 293 | " - name: \"docs\"\n", 294 | " env: PY=3\n", 295 | "```" 296 | ] 297 | }, 298 | { 299 | "cell_type": "markdown", 300 | "metadata": {}, 301 | "source": [ 302 | "## .travis.yml: how to install\n", 303 | "\n", 304 | "```yaml\n", 305 | "before_install:\n", 306 | " # Install miniconda and create TEST env.\n", 307 | " - |\n", 308 | " wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh\n", 309 | " bash miniconda.sh -b -p $HOME/miniconda\n", 310 | " export PATH=\"$HOME/miniconda/bin:$PATH\"\n", 311 | " conda config --set always_yes yes --set changeps1 no --set show_channel_urls true\n", 312 | " conda update --quiet conda\n", 313 | " conda config --add channels conda-forge --force\n", 314 | " conda config --set channel_priority strict\n", 315 | " conda create --name TEST python=$PY --file requirements.txt --file requirements-dev.txt\n", 316 | " source activate TEST\n", 317 | " conda info --all\n", 318 | "\n", 319 | "install:\n", 320 | " - pip install -e . --no-deps --force-reinstall\n", 321 | "\n", 322 | "```" 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": {}, 328 | "source": [ 329 | "## .travis.yml: the actual tests\n", 330 | "\n", 331 | "```yaml\n", 332 | "script:\n", 333 | " - if [[ $TRAVIS_JOB_NAME == python-* ]]; then\n", 334 | " cp -r tests/ /tmp ;\n", 335 | " pushd /tmp && pytest -n 2 -rxs --cov=ioos_pkg_skeleton tests && popd ;\n", 336 | " fi\n", 337 | "\n", 338 | "```" 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "## .travis.yml: always test de tarball\n", 346 | "\n", 347 | "```yaml\n", 348 | " - if [[ $TRAVIS_JOB_NAME == 'tarball' ]]; then\n", 349 | " pip wheel . -w dist --no-deps ;\n", 350 | " check-manifest --verbose ;\n", 351 | " twine check dist/* ;\n", 352 | " fi\n", 353 | "```" 354 | ] 355 | }, 356 | { 357 | "cell_type": "markdown", 358 | "metadata": {}, 359 | "source": [ 360 | "## .travis.yml: maybe test coding standards (pre-commit hooks are preferred)\n", 361 | "\n", 362 | "```yaml\n", 363 | "script:\n", 364 | " - if [[ $TRAVIS_JOB_NAME == 'coding_standards' ]]; then\n", 365 | " pytest --flake8 -m flake8 ;\n", 366 | " fi\n", 367 | "```" 368 | ] 369 | }, 370 | { 371 | "cell_type": "markdown", 372 | "metadata": {}, 373 | "source": [ 374 | "## .travis.yml: build the docs as part of the tests\n", 375 | "\n", 376 | "```yaml\n", 377 | " - |\n", 378 | " if [[ $TRAVIS_JOB_NAME == 'docs' ]]; then\n", 379 | " set -e\n", 380 | " cp notebooks/tutorial.ipynb docs/source/\n", 381 | " pushd docs\n", 382 | " make clean html linkcheck\n", 383 | " popd\n", 384 | " if [[ -z \"$TRAVIS_TAG\" ]]; then\n", 385 | " python -m doctr deploy --build-tags --key-path github_deploy_key.enc --built-docs docs/_build/html dev\n", 386 | " else\n", 387 | " python -m doctr deploy --build-tags --key-path github_deploy_key.enc --built-docs docs/_build/html \"version-$TRAVIS_TAG\"\n", 388 | " python -m doctr deploy --build-tags --key-path github_deploy_key.enc --built-docs docs/_build/html .\n", 389 | " fi\n", 390 | " fi\n", 391 | "\n", 392 | "```" 393 | ] 394 | }, 395 | { 396 | "cell_type": "markdown", 397 | "metadata": {}, 398 | "source": [ 399 | "## requirements-dev.txt\n", 400 | "\n", 401 | "```\n", 402 | "black\n", 403 | "check-manifest\n", 404 | "doctr\n", 405 | "flake8\n", 406 | "flake8-builtins\n", 407 | "flake8-comprehensions\n", 408 | "flake8-mutable\n", 409 | "flake8-print\n", 410 | "isort\n", 411 | "nbsphinx\n", 412 | "pre-commit\n", 413 | "pylint\n", 414 | "pytest\n", 415 | "pytest-cov\n", 416 | "pytest-flake8\n", 417 | "pytest-xdist\n", 418 | "setuptools_scm\n", 419 | "sphinx\n", 420 | "twine\n", 421 | "wheel\n", 422 | "```" 423 | ] 424 | }, 425 | { 426 | "cell_type": "markdown", 427 | "metadata": {}, 428 | "source": [ 429 | "## Extras pre-commit hooks: .pre-commit-config.yaml\n", 430 | "\n", 431 | "\n", 432 | "```yaml\n", 433 | "repos:\n", 434 | "- repo: https://github.com/pre-commit/pre-commit-hooks\n", 435 | " rev: v3.1.0\n", 436 | " hooks:\n", 437 | " - id: trailing-whitespace\n", 438 | " exclude: tests/data\n", 439 | " - id: check-ast\n", 440 | " - id: debug-statements\n", 441 | " - id: end-of-file-fixer\n", 442 | " - id: check-docstring-first\n", 443 | " - id: check-added-large-files\n", 444 | " - id: requirements-txt-fixer\n", 445 | " - id: file-contents-sorter\n", 446 | " files: requirements-dev.txt\n", 447 | "\n", 448 | "- repo: https://gitlab.com/pycqa/flake8\n", 449 | " rev: 3.7.9\n", 450 | " hooks:\n", 451 | " - id: flake8\n", 452 | " exclude: docs/source/conf.py\n", 453 | " args: [--max-line-length=105, --ignore=E203,E501,W503, --select=select=C,E,F,W,B,B950]\n", 454 | "\n", 455 | "- repo: https://github.com/pre-commit/mirrors-isort\n", 456 | " rev: v4.3.21\n", 457 | " hooks:\n", 458 | " - id: isort\n", 459 | " additional_dependencies: [toml]\n", 460 | " args: [--project=ioos_pkg_skeleton, --multi-line=3, --lines-after-imports=2, --lines-between-types=1, --trailing-comma, --force-grid-wrap=0, --use-parentheses, --line-width=88]\n", 461 | "\n", 462 | "- repo: https://github.com/asottile/seed-isort-config\n", 463 | " rev: v2.1.1\n", 464 | " hooks:\n", 465 | " - id: seed-isort-config\n", 466 | "\n", 467 | "- repo: https://github.com/psf/black\n", 468 | " rev: stable\n", 469 | " hooks:\n", 470 | " - id: black\n", 471 | " language_version: python3\n", 472 | "\n", 473 | "- repo: https://github.com/pre-commit/mirrors-mypy\n", 474 | " rev: v0.770\n", 475 | " hooks:\n", 476 | " - id: mypy\n", 477 | " exclude: docs/source/conf.py\n", 478 | " args: [--ignore-missing-imports]\n", 479 | "\n", 480 | "```" 481 | ] 482 | }, 483 | { 484 | "cell_type": "markdown", 485 | "metadata": {}, 486 | "source": [ 487 | "## Extras pre-commit hooks: .isort.cfg\n", 488 | "\n", 489 | "\n", 490 | "```cfg\n", 491 | "[settings]\n", 492 | "known_third_party = numpy,pytest,requests,setuptools\n", 493 | "```" 494 | ] 495 | }, 496 | { 497 | "cell_type": "markdown", 498 | "metadata": {}, 499 | "source": [ 500 | "## Extras pre-commit hooks: .giuthub/workflows/pre-commit.yml\n", 501 | "\n", 502 | "\n", 503 | "```yaml\n", 504 | "name: pre-commit\n", 505 | "\n", 506 | "on:\n", 507 | " pull_request:\n", 508 | " push:\n", 509 | " branches: [master]\n", 510 | "\n", 511 | "jobs:\n", 512 | " pre-commit:\n", 513 | " runs-on: ubuntu-latest\n", 514 | " steps:\n", 515 | " - uses: actions/checkout@v1\n", 516 | " - uses: actions/setup-python@v1\n", 517 | " - name: set PY\n", 518 | " run: echo \"::set-env name=PY::$(python --version --version | sha256sum | cut -d' ' -f1)\"\n", 519 | " - uses: actions/cache@v1\n", 520 | " with:\n", 521 | " path: ~/.cache/pre-commit\n", 522 | " key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}\n", 523 | " - uses: pre-commit/action@v1.0.0\n", 524 | "\n", 525 | "```" 526 | ] 527 | }, 528 | { 529 | "cell_type": "markdown", 530 | "metadata": {}, 531 | "source": [ 532 | "## Summary: must have\n", 533 | "\n", 534 | "```\n", 535 | "- README\n", 536 | " - install instructions\n", 537 | "- License\n", 538 | "- docs\n", 539 | "- unittest tests\n", 540 | "- CIs\n", 541 | "```" 542 | ] 543 | }, 544 | { 545 | "cell_type": "markdown", 546 | "metadata": {}, 547 | "source": [ 548 | "## Summary: Nice to have\n", 549 | "\n", 550 | "```\n", 551 | "- automatic version number from tags\n", 552 | "- auto-publish docs and tarball\n", 553 | "- tarball automated checks\n", 554 | "- standard style: black, lints, isort\n", 555 | "- integration tests\n", 556 | "- Windows CI\n", 557 | "- Your pkg in conda-forge\n", 558 | "```" 559 | ] 560 | }, 561 | { 562 | "cell_type": "markdown", 563 | "metadata": {}, 564 | "source": [ 565 | "## Summary: also nice to have\n", 566 | "\n", 567 | "```\n", 568 | "- CONTRIBUTING.rst\n", 569 | "- .github/\n", 570 | "```" 571 | ] 572 | }, 573 | { 574 | "cell_type": "markdown", 575 | "metadata": {}, 576 | "source": [ 577 | "Please check out https://www.pyopensci.org/" 578 | ] 579 | } 580 | ], 581 | "metadata": { 582 | "kernelspec": { 583 | "display_name": "Python 3 (ipykernel)", 584 | "language": "python", 585 | "name": "python3" 586 | }, 587 | "language_info": { 588 | "codemirror_mode": { 589 | "name": "ipython", 590 | "version": 3 591 | }, 592 | "file_extension": ".py", 593 | "mimetype": "text/x-python", 594 | "name": "python", 595 | "nbconvert_exporter": "python", 596 | "pygments_lexer": "ipython3", 597 | "version": "3.11.0" 598 | } 599 | }, 600 | "nbformat": 4, 601 | "nbformat_minor": 4 602 | } 603 | -------------------------------------------------------------------------------- /notebooks/tutorial.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# ioos_pkg_skeleton tutorial" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import ioos_pkg_skeleton\n", 17 | "\n", 18 | "\n", 19 | "ioos_pkg_skeleton.meaning_of_life(3)" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "ioos_pkg_skeleton.meaning_of_life_url()" 29 | ] 30 | } 31 | ], 32 | "metadata": { 33 | "kernelspec": { 34 | "display_name": "Python 3 (ipykernel)", 35 | "language": "python", 36 | "name": "python3" 37 | }, 38 | "language_info": { 39 | "codemirror_mode": { 40 | "name": "ipython", 41 | "version": 3 42 | }, 43 | "file_extension": ".py", 44 | "mimetype": "text/x-python", 45 | "name": "python", 46 | "nbconvert_exporter": "python", 47 | "pygments_lexer": "ipython3", 48 | "version": "3.11.0" 49 | } 50 | }, 51 | "nbformat": 4, 52 | "nbformat_minor": 4 53 | } 54 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "setuptools.build_meta" 3 | requires = [ 4 | "setuptools>=42", 5 | "setuptools_scm[toml]>=3.4", 6 | "wheel", 7 | ] 8 | 9 | [project] 10 | name = "ioos-pkg-skeleton" 11 | description = "MY AWESOME MODULE" 12 | readme = "README.md" 13 | license = {file = "LICENSE.txt"} 14 | authors = [ 15 | {name = "AUTHOR NAME", email = "AUTHOR@EMAIL.COM"}, 16 | ] 17 | requires-python = ">=3.8" 18 | classifiers = [ 19 | "Programming Language :: Python :: 3 :: Only", 20 | "Programming Language :: Python :: 3.8", 21 | "Programming Language :: Python :: 3.9", 22 | "Programming Language :: Python :: 3.10", 23 | "Programming Language :: Python :: 3.11", 24 | ] 25 | dynamic = [ 26 | "version", 27 | ] 28 | dependencies = [ 29 | "numpy", 30 | "requests", 31 | ] 32 | [project.urls] 33 | documentation = "https://ioos.github.io/ioos-python-package-skeleton" 34 | homepage = "https://github.com/ioos/ioos-python-package-skeleton" 35 | repository = "https://github.com/ioos/ioos-python-package-skeleton" 36 | 37 | [tool.setuptools] 38 | packages = ["ioos_pkg_skeleton"] 39 | zip-safe = false 40 | include-package-data = true 41 | 42 | [tool.setuptools_scm] 43 | write_to = "ioos_pkg_skeleton/_version.py" 44 | write_to_template = "__version__ = '{version}'" 45 | 46 | [tool.ruff] 47 | select = [ 48 | "F", # flakes 49 | "I", # import sorting 50 | "U", # upgrade 51 | ] 52 | target-version = "py311" 53 | line-length = 79 54 | 55 | [tool.ruff.per-file-ignores] 56 | "docs/conf.py" = ["E402"] 57 | 58 | [tool.interrogate] 59 | ignore-init-method = true 60 | ignore-init-module = false 61 | ignore-magic = false 62 | ignore-semiprivate = false 63 | ignore-private = false 64 | ignore-module = false 65 | fail-under = 95 66 | exclude = ["setup.py", "docs", "tests"] 67 | verbose = 1 68 | quiet = false 69 | color = true 70 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | black 2 | check-manifest 3 | interrogate 4 | jupyter 5 | myst-parser 6 | nbsphinx 7 | pre-commit 8 | pytest 9 | pytest-cov 10 | pytest-xdist 11 | setuptools_scm 12 | sphinx 13 | twine 14 | wheel 15 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | requests 3 | -------------------------------------------------------------------------------- /tests/test_ioos_pkg_skeleton.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | 4 | from ioos_pkg_skeleton import meaning_of_life, meaning_of_life_url 5 | 6 | 7 | @pytest.mark.web 8 | def test_meaning_of_life_url(): 9 | ret = meaning_of_life_url() 10 | 11 | assert isinstance(ret, str) 12 | assert "Monty Python" in ret 13 | assert "Meaning of Life" in ret 14 | 15 | 16 | def test_meaning_of_life(): 17 | n = 2 18 | ret = meaning_of_life(n) 19 | assert isinstance(ret, np.ndarray) 20 | assert np.unique(ret) == 42 21 | --------------------------------------------------------------------------------