diff --git a/code-style-structure/intro.md b/code-style-structure/intro.md deleted file mode 100644 index 7f540b30..00000000 --- a/code-style-structure/intro.md +++ /dev/null @@ -1,4 +0,0 @@ -# Code style and structure - - -Under development \ No newline at end of file diff --git a/images/python-package-tools-2022-survey-pypa.png b/images/python-package-tools-2022-survey-pypa.png new file mode 100644 index 00000000..c3d7d4f8 Binary files /dev/null and b/images/python-package-tools-2022-survey-pypa.png differ diff --git a/index.md b/index.md index 5c531ed5..32b8e8c2 100644 --- a/index.md +++ b/index.md @@ -123,24 +123,13 @@ Contributing & license files :caption: Package structure & code style Intro -package-structure-code/code-structure-style -package-structure-code/release + +Python package structure +Package Build Tools +Package Versions package-structure-code/overview package-structure-code/collaboration -``` - -```{toctree} -:hidden: -:caption: Code Style & Structure - -code-style-structure/index -``` - -```{toctree} -:hidden: -:caption: Test your code -testing-infrastructure/test-code -testing-infrastructure/continuous-integration +Code Style & Format ``` diff --git a/package-structure-code/code-structure-style.md b/package-structure-code/code-structure-style.md new file mode 100644 index 00000000..555bcef1 --- /dev/null +++ b/package-structure-code/code-structure-style.md @@ -0,0 +1,250 @@ +# Python Package Code Style & Linting + +Consistent code format and style is useful to both your package +and across the scientific python ecosystem because using similar +formats makes code easier to read. + +For instance, if you saw a sentence like this one without any +spaces it would take your brain longer to process it. + +``` +forinstanceifyousawasentencelikethisonewithoutany... +``` + +pyOpenSci requires you to follow standard Python PEP 8 format +rules. But we also suggest that you use a code formatter too. +If more packages start using formatter, we will slowly gain consistency +across the scientific ecosystem. Code across packages will then +be consistent and as such easier to scan, understand and contribute to. + +### pyOpenSci requirements for code style and format + +pyOpenSci doesn't require you to use a specific code format tool. However we do look for +consistency and readbility in code style. pyOpenSci also requires that you +follow the [python PEP8 style guide](https://peps.python.org/pep-0008/) for decisions +related to styling your open source Python packages code. We also require that your code adheres +to the PEP8 style guidelines. + +We do strongly recommend that you implement a code styler tool suite in your +package as it will save you and your maintainer team time. It will also ensure +that format and style is consistent accross all of your code. + + +Below you will find a list of commonly used Python code formatters. +Currently, [black](https://github.com/psf/black) is the most popular code styler for Python. + +## Code format and style +We suggest that you use a code formatter such as [black](https://black.readthedocs.io/en/stable/) or +[blue](https://blue.readthedocs.io/en/latest/) to ensure your +code is consistently formatted throughout your package. Using an existing, +widely used tool will avoid lengthy discussions with contributors and other +maintainers about personalized code format preferences during reviews. + +## Code linters and formatters +A code linter is a tool that will review your code and identify errors or +issues. A linter typically does not actually modify the code for you. It will +just tell you what the error is and on what line it was discovered. + +### flake8 is a preferred code linting tool for Python packages + +To adhere to Python `pep8` format standards, we suggest that you use +[flake8](https://flake8.pycqa.org/en/latest/). + +Below you can see the output of running +`flake8 filename.py` at the command line for a python file within a package +called `stravalib`. The line length standard for PEP8 is 79 characters. flake8 +will: +* flags every line in your code that extends beyond 79 characters +* spacing issues that conflict with pep8 such as missing spaces after commas + +It also flags unused imports in your modules. + +Notice below that you run flak8 in the command line and it returns a list of +each error it finds in a file. All of this output is at the command line. The +python file is not modified. + +```bash +(stravalib-dev) username@computer stravalib % flake8 stravalib/model.py +stravalib/model.py:8:1: F401 'os' imported but unused +stravalib/model.py:29:80: E501 line too long (90 > 79 characters) +stravalib/model.py:34:80: E501 line too long (95 > 79 characters) +stravalib/model.py:442:80: E501 line too long (82 > 79 characters) +stravalib/model.py:443:39: E231 missing whitespace after ',' +stravalib/model.py:493:20: E225 missing whitespace around operator +stravalib/model.py:496:80: E501 line too long (82 > 79 characters) +``` + +### Black and Blue +While `flake8` is a linter that identifies issues with your code, [black](https://black.readthedocs.io/en/stable/) and also +[blue](https://blue.readthedocs.io/en/latest/) (which wraps around black) are code +formatters. Both black and blue will manually (and unapologetically) +fix spacing issues and ensure format is consistent throughout your package. + +These formatters leave you more time to work on code function rather than +format. Black and blue do +generally adhere to pep8 style guidelines with some exceptions. + +* Black defaults to a line width of 88 (79 + 10%) rather than the 79 character `pep8` specification (but this setting can be manually overwritten in your black setup) +* Black and blue will not adjust line length in your comments or docstrings. +* Neither tool will review and fix import order (you need isort to do that). + +Blue adresses a few format decisions in black that some maintainers do not like. +[You can compare the differences here](https://blue.readthedocs.io/en/latest/#so-what-s-different) and decide which tool suites your preferences! + +```{tip} +If you are interested in seeing how black will format your code, you can +use the [black playground](https://black.vercel.app/?version=stable&state=_Td6WFoAAATm1rRGAgAhARYAAAB0L-Wj4ARsAnNdAD2IimZxl1N_WlkPinBFoXIfdFTaTVkGVeHShArYj9yPlDvwBA7LhGo8BvRQqDilPtgsfdKl-ha7EFp0Ma6lY_06IceKiVsJ3BpoICJM9wU1VJLD7l3qd5xTmo78LqThf9uibGWcWCD16LBOn0JK8rhhx_Gf2ClySDJtvm7zQJ1Z-Ipmv9D7I_zhjztfi2UTVsJp7917XToHBm2EoNZqyE8homtGskFIiif5EZthHQvvOj8S2gJx8_t_UpWp1ScpIsD_Xq83LX-B956I_EBIeNoGwZZPFC5zAIoMeiaC1jU-sdOHVucLJM_x-jkzMvK8Utdfvp9MMvKyTfb_BZoe0-FAc2ZVlXEpwYgJVAGdCXv3lQT4bpTXyBwDrDVrUeJDivSSwOvT8tlnuMrXoD1Sk2NZB5SHyNmZsfyAEqLALbUnhkX8hbt5U2yNQRDf1LQhuUIOii6k6H9wnDNRnBiQHUfzKfW1CLiThnuVFjlCxQhJ60u67n3EK38XxHkQdOocJXpBNO51E4-f9z2hj0EDTu_ScuqOiC9cI8qJ4grSZIOnnQLv9WPvmCzx5zib3JacesIxMVvZNQiljq_gL7udm1yeXQjENOrBWbfBEkv1P4izWeAysoJgZUhtZFwKFdoCGt2TXe3xQ-wVZFS5KoMPhGFDZGPKzpK15caQOnWobOHLKaL8eFA-qI44qZrMQ7sSLn04bYeenNR2Vxz7hvK0lJhkgKrpVfUnZrtF-e-ubeeUCThWus4jZbKlFBe2Kroz90Elij_UZBMFCcFo0CfIx5mGlrINrTJLhERszRMMDd39XsBDzpZIYV4TcG7HoMS_IF8aMAAAxI-5uTWXbUQAAY8F7QgAAP01Vc6xxGf7AgAAAAAEWVo=) +``` + + +### Isort + +Python imports refer to the python packages that a module in your package require to run. Following conventional standards, +all Python imports should be called at the top of your code (.py)files. + +In addition to these imports all being at the top of each code +file (or each module) [PEP 8 has specific standards for the order of these imports](https://peps.python.org/pep-0008/#imports). These standards are listed below: + + +> Imports should be grouped in the following order: +> +> * Standard library imports. +> * Related third party imports. +> * Local application/library specific imports. + +While flake8 will identify unused imports in your code, it won't +fix or identify issues with the order of those imports. + +`isort` will identify where imports in your code are out of +order. It will then modify your module, automatically reordering +all imports. This leaves you with one less +thing to think about when cleaning up your code. Further, if +setup as a precommit hook, isort and a continuous integration +check, using isort will ensure that new contributors, who may +be less familiar with pep 8 standards, follow +the pep 8 import order standards as well. + +### Example application of isort + +Code imports before `isort` is run: + +Below, the exc module is a part of starvalib which is a +third party package. `abc` and `logging` are core `Python` packages +distributed with `Python`. Also notice that there are extra +spaces in the imports listed below. + +```python +from stravalib import exc +import abc +import logging + +from collections.abc import Sequence + + +from stravalib import unithelper as uh +``` +Run: +`isort stravalib/model.py` + +Python module after `isort` has been run + +```python +import abc +import logging +from collections.abc import Sequence + +from stravalib import exc +from stravalib import unithelper as uh +``` + + +## How to setup a code formatter to support your + +### Linters, code formatters and your favorite coding tools +Linters can be run as a command-line tool as shown above. They also can +be run within your favorite coding tool (e.g. VScode, pycharm, etc). Tools like +`flake8` and `black` can often be integrated with your favorite code editors to +run automatically when you save a file. + +### Linters, code formatters and pre-commit hooks +Some maintainers setup a `pre-commit hook` in their repository. + +This section will make the most sense if you are familiar with git. +You can skip it if you are not yet familiar with git commit! + +A pre-commit hook cna be best described as a trigger that happens making a +tool run when you type and run +`git commit -m "message here` at the command line. + +For instance, if you setup the black code formatter as a pre-commit hook, +when you go to commit changes to a code file and hit enter, black will run +on your the code files in your commit. It will update any files to match +black format standards. You can then retype the commit and push files to +GitHub that have been formatted by black. + +Using pre-commit hooks is helpful to ensure code consistency for edits to your +code files. + +```{note} +If you already have a Python package and you want to apply a code formatter, you +may need to go through the initial effort of formatting all of your code files. + +Running a tool like black is normally quick. However implementing fixes found by +flake8 can take a bit of time. Make sure that you allocate a bit of time to +format your code. Also make sure that other maintainers are not actively +updating your code base as an initial commit with black format changes will +likely change a significant amount of your code. This is a recipe for merge +conflicts! +``` + +## Setting up a `git` pre-commit hook + +Git pre-commit hook is a useful tool that checks your code automatically when you run a `git commit` and, +if it fails, the `git commit` is canceled. This is often used to make sure +that the changes to your code match a particular style, or that there are no +code linting errors. + +For example, if you want that `git commit` checks if your code matches the PEP8 specification, +you can configure a git flake8 pre-commit hook: + +```yaml +# file: .pre-commit-config.yaml +repos: + - repo: https://gitlab.com/pycqa/flake8 + rev: '3.7.9' + hooks: + - id: flake8 +``` + +```{note} +See [the flake8 hooks explanation](https://flake8.pycqa.org/en/latest/user/using-hooks.html) for more details. +``` + +This file specifies a hook that will be triggered automatically before each `git commit`, +in this case, it specifies a `flake8` using version `3.7.9`. + +Before you can see any change, first you should install `pre-commit` library. +One way to do it is using `pip` or `conda`: + +```sh +pip install pre-commit + +# or + +conda install -c conda-forge pre-commit +``` + +Now, you can install your pre-commit hook using `pre-commit install`, and it will create the hook based on +the file `.pre-commit-config.yaml`. + +Before each `git commit` command, in this case, git will run `flake8` and, if it fails, the `commit` will be canceled. + +## Summary +pyOpenSci suggests setting up a linter and a code styler for +your package, regardless of whether you use pre-commit hooks, CI +or other infrastructure to manage code format. Setting up these +tools will give you automatic feedback about your code's +structure as you (or a contributor) write it. And using tools +like black or blue that format code for you, reduce effort that +you need to make surrounding decisions around code format and +style. \ No newline at end of file diff --git a/package-structure-code/collaboration.md b/package-structure-code/collaboration.md new file mode 100644 index 00000000..c2cee89f --- /dev/null +++ b/package-structure-code/collaboration.md @@ -0,0 +1,55 @@ +# Collaboration Guide + +🚧 UNDER CONSTRUCTION - THIS CONTENT SHOULD BE FURTHER DEVELOPED BY THE END OF 2022! KEEP CHECKING BACK TO UPDATES AS THEY ARE IN PROGRESS🚧 + + +Note: pyOpenSci is still building out this section of our guidebook. Many of the examples come from rOpenSci. If you have suggestions for changes, check out the [GitHub repo](https://github.com/pyOpenSci/contributing-guide). + +## Make your repo contribution and collaboration friendly + +### Code of conduct + +We require that you use a code of conduct such as the [Contributor Covenant](https://contributor-covenant.org/) in developing your package. You should document your code of conduct in a `CODE_OF_CONDUCT` file in the package root directory, and link to this file from the `README` file. + +### Contributing guide + +October 2022 NOTE: we are in the process of updating all of this and will likely deprecate +this cookie cutter. Proceed at your own risk :) + +We have a [template for contributing guidelines](https://github.com/pyOpenSci/cookiecutter-pyopensci/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/CONTRIBUTING.rst) in our cookiecutter repository. Even if you're not using cookiecutter to build your project (see our [packaging guide](../authoring/overview#project-template) for info), you can download our CONTRIBUTING.rst as a starting point. + +You can tweak it a bit depending on your workflow and package. For example, make sure contributors have instructions in your CONTRIBUTING file for running local tests if not trivial. CONTRIBUTING can also contain some details about how you acknowledge contributions (see [this section](#attributions)) and the roadmap of your package (cf [this example for R](https://github.com/ecohealthalliance/fasterize/blob/master/CONTRIBUTING.md)). + +### Issue labelling + +You can use labels such as "help wanted", "good first issue", or "beginner" to help potential collaborators, including newbies, find your repo. For more info, see this [GitHub article](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/encouraging-helpful-contributions-to-your-project-with-labels). + +## Working with collaborators + +### Onboarding collaborators + +There's no general pyOpenSci rule as to how you should onboard collaborators. You should increase their rights to the repo as you gain trust, and you should definitely acknowledge contributions (see [this section](#attributions)). + +You can ask a new collaborator to make PRs (see following section for assessing a PR locally, i.e. beyond CI checks) to dev/main and assess them before merging, and after a while let them push to main, although you might want to keep a system of PR reviews... even for yourself once you have team mates! + +A possible model for onboarding collaborators is provided by Jim Hester in [his `lintr` repo](https://github.com/jimhester/lintr/issues/318). + +If your problem is _recruiting_ collaborators, you can post an open call like Jim Hester's [on Twitter](https://twitter.com/jimhester_/status/997109466674819074), ([GitHub](https://github.com/jimhester/lintr/issues/318)). As an pyOpenSci package author, you can also ask for help from pyOpenSci. + +### Working with collaborators (including yourself) + +You could implement the "[gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)" philosophy as explained by Amanda Dobbyn in [this rOpenSci blog post](https://ropensci.org/blog/2018/04/20/monkeydo/). + +One particular aspect of working with collaborators is reviewing pull requests. Even if not adopting gitflow it might make sense for repo collaborators to make PRs and have them reviewed, and in general PRs by external developers will need to be assessed. Sometimes you'll be fine just reading the changes and trusting [Continuous integration](../authoring/overview#continuous-integration). Sometimes you'll need to dive deeper, e.g. by downloading a copy of the branch that the PR was created from. + +(attributions)= +### Be generous with attributions + +If someone contributes to your repository consider adding them in CONTRIBUTORS, as contributor for small contributions, author for bigger contributions. Also consider adding their name near the feature/bug fix line in HISTORY We recommend your being generous with such acknowledgements. + +If your package was reviewed by pyOpenSci and you feel that your reviewers have made a substantial contribution to the development of your package, you may list them in CONTRIBUTORS as a reviewer. Only include reviewers after asking for their consent. + +```{note} +Please do not list pyOpenSci editors and reviewers as contributors to your project. +Your participation in and contribution to pyOpenSci is thanks enough! +``` diff --git a/package-structure-code/intro.md b/package-structure-code/intro.md new file mode 100644 index 00000000..1481ac31 --- /dev/null +++ b/package-structure-code/intro.md @@ -0,0 +1,59 @@ +# Python package structure information + +If you plan to submit a package for review to pyOpenSci and are looking for +some guidance on package structure, code formats and style, then this section is for you. + +## Guidelines for pyOpenSci's packaging recommendations + + + +There are some differing opinions on what Python package structure should +look like and what tools to use across the Python ecosystem. + +In this guide, we have made decisions around suggested standards and required +standards, based upon the commonly used approaches in the scientific Python +community. Our goal is to help standardize packaging across this ecosystem. + +In some cases the suggestions here may diverge from those in the non-scientific parts of the Python ecosystem. + +```{note} +The suggestions for package layout in this section are made with the +intent of being helpful; they are not specific requirements for your +package to be reviewed and accepted into our ecosystem. +``` + +In all cases, we try to align our suggestions with the most current, accepted +[PEP's (Python Enhancement Protocols)](https://peps.python.org/pep-0000/) and the [scientific-python community specs](https://scientific-python.org/specs/). + +```{note} +Have a look at the +bare-minimum [editor checks](https://www.pyopensci.org/peer-review-guide/software-peer-review-guide/editor-in-chief-guide.html#editor-checklist-template) that pyOpenSci +performs before a review begins. These checks are useful to explore +for both authors planning to submit a package to us for review and for +anyone who is just getting started with creating a Python package. + +In general these are basic items that should be in any open software repository. +``` + + + + \ No newline at end of file diff --git a/package-structure-code/publish-python-package.md b/package-structure-code/publish-python-package.md new file mode 100644 index 00000000..637d2940 --- /dev/null +++ b/package-structure-code/publish-python-package.md @@ -0,0 +1,63 @@ +# Publish Your Python Package in a Community Channel + + + + +## maybe also have a page in creating an installable package + + +## Original Release + +After the review process has finished, you're ready to release your package to PyPI so others can find and use your awesome package! Releasing to PyPI is easy. Once your package is ready, register it on PyPI by running: + +``` +python setup.py register +``` + +## Releasing Updated Versions + +When you update your package, you release a new version to PyPI. Fortunately, this is easy! First, we'll talk about the metadata you'll need to update for each version. Then we'll cover how to release your updated version to PyPI [manually via the command line](manual-release) or [automatically via Travis CI](travis-release). + + + +(history)= +### History/News/Changelog file + +A HISTORY (or NEWS or CHANGELOG) file describing changes associated with each version makes it easier for users to see what's changing in the package and how it might impact their workflow. You must add one for your package, and make it easy to read. + +* It is mandatory to use a `HISTORY` file in the root of your package. It can also be called NEWS or CHANGELOG We recommend using `[NAME].md` or `[NAME].rst` to make the file more browsing-friendly on GitHub (GitHub renders certain file types, including Markdown and reStructured text). + +* Update the history file before every PyPI release, with a section with the package name, version and date of release. + +``` +foobar 0.2.0 (2016-04-01) +========================= +``` + +* Under that header, put in sections as needed, including: `NEW FEATURES`, `MINOR IMPROVEMENTS`, `BUG FIXES`, `DEPRECATED AND DEFUNCT`, `DOCUMENTATION FIXES` and any special heading grouping a large number of changes. Under each header, list items as needed. For each item give a description of the new feature, improvement, bug fix, or deprecated function/feature. Link to any related GitHub issue like `(#12)`. The `(#12)` will resolve on GitHub in Releases to a link to that issue in the repo. + +* After you have added a `git tag` and pushed up to GitHub, add the news items for that tagged version to the Release notes of a release in your GitHub repo with a title like `pkgname v0.1.0`. See [GitHub docs about creating a release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository). + +(manual-release)= +### Releasing Versions: Manual + +To manually upload a new package version to PyPI, follow these steps: + +1. Update your HISTORY file as described [above](#history) +2. Open `setup.py` and change the version, e.g., version='1.0.3' +3. If you added new, non-Python files to the project that need to be distributed as well, e.g., configuration files, add them to `MANIFEST.in`. This does not need to be done for Python code files ending in .py. +4. Open a terminal and go into the parent of the project's root dir +5. `python setup.py sdist` +6. Check the resulting files, especially the egg file: are all the files contained? +7. If everything is ok, upload the new version to PyPI: `python setup.py sdist upload` + +That's it! + +(travis-release)= +### Releasing via Travis CI +Instead of manually uploading new package versions, Travis can be configured to automatically upload new versions. If you use this, each time you tag a new release and push it to GitHub, Travis will release it to PyPI (assuming it passes testing). + +* The pyOpenSci cookiecutter comes with this option mostly set up. For details on how to finish the set up, see [this guide](https://cookiecutter-pyopensci.readthedocs.io/en/latest/travis_pypi_setup.html). + +* Also, be sure to check out this [PyPI release checklist](https://cookiecutter-pyopensci.readthedocs.io/en/latest/pypi_release_checklist.html). + diff --git a/package-structure-code/publish-your-python-package-pypi-conda.md b/package-structure-code/publish-your-python-package-pypi-conda.md new file mode 100644 index 00000000..984fda6e --- /dev/null +++ b/package-structure-code/publish-your-python-package-pypi-conda.md @@ -0,0 +1,10 @@ +## Package publication + +pyOpenSci requires that your package can be installed from a public community +repository such as `PyPI` or a `conda` channel such as `bioconda` or `conda-forge`. + +### What is PyPi + +### What is Conda + +### How are they different? \ No newline at end of file diff --git a/package-structure-code/python-dependency-versions.md b/package-structure-code/python-dependency-versions.md new file mode 100644 index 00000000..2c5fcc47 --- /dev/null +++ b/package-structure-code/python-dependency-versions.md @@ -0,0 +1,8 @@ +# Dep versions + +https://scientific-python.org/specs/spec-0000/ + +https://endoflife.date/python +https://numpy.org/neps/nep-0029-deprecation_policy.html + +36 month drop python version diff --git a/package-structure-code/python-package-build-tools.md b/package-structure-code/python-package-build-tools.md new file mode 100644 index 00000000..910d1731 --- /dev/null +++ b/package-structure-code/python-package-build-tools.md @@ -0,0 +1,310 @@ +# Python Package Build Tools + +Below, we discuss some of the tools that are commonly used +to build Python packages. This page is intended to help +maintainers select a build tool to use. + + + + +## Python package distribution files + +Before we dive into specific build tools, it's important +to review the pieces of a "built" Python package. + +### Build +To understand the two distributions below, it is important to understand two different types of files: + +**Source files:** source files are the unbuilt files needed to build your package. An example of a build step would be compiling uncompiled code. +**Binary files:** binary files are the files needed to install your package. These files are pre-build. As such any code that need to be compiled has been compiled / built in a binary distribution. + +There are two types of distribution files that you will create to support +publishing your Python package (on PyPI): + +1. SDIST and +1. Wheel + + + +```{note} +If your package is a pure python package with no additional +build / compilation steps then the SDIST and Wheel files will have the same content. +``` + + + +### What is a SDist file? + +SDist, short for **S**ource **D**istribution file is a packaged file in `.tar.gz` +format (often called a "tarball") that has all of the files needed to build your +package. In the SDist format, your package's files are not included in a built +format. Thus, anyone using this file, will have to build the files first before +they can be installed. + +```{tip} +When you make a release on GitHub, it creates a SDist file (`.tar.gz`) that +anyone can download and use. +``` + + + +### Wheel (.whl files): + +A wheel or `.whl` file, is a zipped file that has +the extension `.whl`. The wheel does not contain any of your packages +configuration files such as **setup.cfg** or **pyproject.toml**. This distribution +is a pre-build, ready-to-install format. + +Because it is prebuilt, the wheel file will be faster to install for pure Python +projects and can lead to consistent installs across machines. + +```{tip} +Wheels are also useful in the case that a package +needs a **setup.py** file to support a more complex built. +In this case, because the files in the wheel bundle +are pre built, the user installing doesn't have to +work about malicious code injections when it is installed. +``` + +[Read more about the wheel format here](https://pythonwheels.com/) + +## Tools to build python packages + +There are a suite of build tools that you can use to create your Python package's **SDist** and *Wheel* distributions. +To better understand your options, it's important to first understand the difference between a +build tool front-end and build backend. + +## Build front-end vs. build backend tools for Python packaging + +### Build back-ends +Every tool has a back-end +build tool that actually builds the package and creates associated (SDist and wheel) distribution +files. Some of the tools below such as Flit and pdm only +support straight forward builds that do not have a compilation or other additional build step. These +types of tools are ideal if you have a pure python +project. + +Other tools such as Hatch/ hatchling and setuptools +support more complex builds with custom steps. + +### Build front-ends +Each tool discussed below has a front-end interface that you can use to +perform different types of Python packaging tasks. +For instance, +you can use **Flit** to both build your package and +to publish your package +to PyPI (or test PyPI). + +Using a tool like **Flit** makes your workflow commands consistent and simple. For example, rather than using `twine` to publish your package to PyPi, you use `flit publish` (see below). + +Example to build your package with Flit: + +`flit build` + +Example to publish to PyPI: +`flit publish --repository testpypi` + +In the Pytyhon package build space **setuptools** is +the "OG" -the original tool that everyone used to use. +With a tool like `setuptools` you have the flexibility +to publish python pure python packages and packages with custom build steps. However, you will also need to use other tools. For example, you will use `twine` to publish to PyPI. + +## An ecosystem of Python build tools + +Below we introduce several of the most commonly used +Python packaging build tools. Each tool has various +features that might make you chose to use it +or not use it. There is no right or wrong tool to use +as far as pyOpenSci is concerned. We are just trying to +help you find the tool that works best for +your workflow. + + +:::{figure-md} fig-target + +Graph showing the results of the 2022 PyPA survey of Python packaging tools. On the x axis is percent response and on the y axis are the tools. + +The Python developers survey results (n=>8,000 PyPI users) show setuptools and poetry as the most commonly used Python packaging tools. The core tools that we've seen being used in the scientific community are included here. [You can view the full survey results by clicking here.](https://drive.google.com/file/d/1U5d5SiXLVkzDpS0i1dJIA4Hu5Qg704T9/view) +::: + +The tools that we review below are those that were the most commonly used in the above survey. They include: + +* setuptools +* flit +* hatch +* pdm + + +```{note} +Note that we are intentionally not including Poetry in this list because + +1. Poetry pins dependencies using `^`. This `^` symbol means that there is an "upper bound" to the dependency. Thus poetry will bump a dependency version to a new major version. Thus if your package using a dependency that is at version 1.2.3, poetry will never bump the dependency to 2.0 even if there is a new major version of the package. It will bump up to 1.9.x. . [This approach has been found to be problematic by many of our core scientific packages.](https://iscinumpy.dev/post/bound-version-constraints/) +## 1. TODO: look at how it creates a pyproject toml file too... + +As such, we don't suggest that use of poetry for your +development workflow even though it is an excellent and +well documented tool. It's dependency management +decisions have caused breaking changes in several large +Python packages. +``` + +## How to chose a build development tool + +When deciding what tools you wish to use, there are a few basic criteria that +you can use to help guide your decision: + +### Build tools for pure Python packages +If your package is a pure Python package and it doesn't have any additional build steps (such as compiling code, etc) you can use any of the tools discussed on +this page including: + +* Flit +* Hatch +* setuptools +* pdm + + ### Build tools for Python packages with complex build steps +If your package is not pure python, or it has complex build steps (or build +steps that you need to customize), then you should consider using: + +* Setuptools +* Hatch + +Both of these tools allow you to customize your workflow with build steps needed +to compile code. + +**Setuptools** is the "OG" tool that has been used for Python packaging for many years. +**Hatch** is the newer, more modern tool that supports customization of any part of your packaging steps. + +## Tools that also support version bumping +Of the tools listed on this page, two of them support +direct package versioning: + +* setuptools has setuptools_scm +* hatch has hatch_vcs + +Each tool listed above allows you to setup a release +workflow using your version control platform of choice +(we are using GitHub as an example here given it is +currently the most popular platform). + +## Python packaging tools summary +A summary of what each of the tools offers can be found in the table below: +| Feature | Setuptools | Flit | Hatch | PDM | +| ---------------------------------------------------------------------- | --------------------- | --------- | --------- | --- | +| Build backend | setuptools.build_meta | flit_core | hatchling | pdm-pep517 | +| Supports projects that aren't pure python | yes | no | yes | no | +| Supports custom build steps (before creating wheel) | yes | no | yes | no | +| Has built in dependency management | no | no | no (future feature) | yes | +| Can be used to publish directly to pypi | no (use twine) | yes | yes | yes | +| Has version control based (eg git based) tooling to bump version | setuptools_scm | no | hatch-vcs | ? | +| Supports automated GitHub only releases (no local command line needed) | yes | no | yes | | +(GitHu +## Setuptools + +build-backend: setuptools.build_meta +build_frontend: setuptools +build_versioning: setuptools_scm + +[Setuptools](https://setuptools.pypa.io/en/latest/) is one of the most mature Python packaging tools with [development dating back to 2009 and earlier](https://setuptools.pypa.io/en/latest/history.html#). Given this history, it is said to be the most heavily used Python packaging tool. + +### Potential benefits of setuptools +Some of the benefits include: +* Fully customizable build workflow +* Examples from many packages already using +* Built in versioning using **setuptools_scm** +* Supports modern packaging using **pyproject.toml** for metadata + +### Potential drawbacks of setuptools + +Setuptools has a few drawbacks: + +* Because **setuptools** has to maintain backwards compatibility across a range of packages, it is +not as flexible in its adoption of modern Python packaging +standards. This backwards compatibility also makes for a more complex code-base. +* To push to PyPI you will need to use another tool, **twine**. +* TODO: Pradyun - bad defaults?? + +## Flit + +* Build Backend: **flit_core.buildapi** +* Versioning: No, Flit uses the version from your package's ` __version__`. To update this you'd want to use another tool such as: bump2version +* [Why use flit?](https://flit.pypa.io/en/stable/rationale.html) + +[Flit is a modern, no-nonsense, streamlined packaging tool](https://flit.pypa.io/en/stable/) that supports modern Python packaging standards. + +If you have a pure Python project and don't need any +additional build steps for your package, Flit could be +a tool for you. + +**Flit** supports **proproject.toml** files for metadata and dependency management. + +**Flit** also has a front end command line that will help +you publish your package to both testPyPI and PyPI. + + + +## Hatch + +* Build Backend: **hatchling** +* Version control based versioning: Yes - **hatch-vcs** +* Push to PyPI: + +[**Hatch**](https://hatch.pypa.io/latest/) can be compared to **Flit** in that it simplifies your +package's build workflow using consistent commands. However, it adds: + +* A fully customizable build back-end +* Full flexibility for each step of the build process. +* Matrix environment creation to support testing across Python versions +* [Nox / MAKEFILE like functionality](https://hatch.pypa.io/latest/environment/#selection) +where you can create workflows in the pyproject.toml configuration to do things +like serve docs locally and cleaning your package build directory. + +Hatch does about everything that you might need to support +package development. It is ideal for packages that have compiled code and thus +require custom build steps. + +While hatch does have some "opinions" about how parts +of the packaging build run, you can customize any aspect +of it's build making if fully flexible. + +## PDM + +[PDM is a Python packaging and dependency management tool.](https://pdm.fming.dev/latest/). +It is designed to support pure python projects. + +Benefits of using PDM: + +* Dependency management + +HELP! Can someone help me understand why you'd pick pdm please? my guess if is you want dependency management diff --git a/package-structure-code/python-package-structure.md b/package-structure-code/python-package-structure.md new file mode 100644 index 00000000..d1af26fa --- /dev/null +++ b/package-structure-code/python-package-structure.md @@ -0,0 +1,303 @@ +# Python Package Structure for Scientific Python Projects +There are two different general layouts that you will commonly see +within the Python packaging ecosystem: +[src and flat layouts (click here to learn more).](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/) +We believe that both layouts have advantages. +The Python packaging authority advocates for the [**src/** layout](https://py-pkgs.org/04-package-structure), +in their tutorials. + +```{important} +pyOpenSci however will never require a specific package structure for its +peer review process. The overview on this page presents recommendations. +``` + +Below you will learn about the pros and cons of both layouts. + +Currently most scientific packages use the **flat-layout** given: + +* It's the most commonly found layout with the scientific Python ecosystem +* Many Python tools depend upon tools in other language and / or complex builds with compilation steps. Many developers thus appreciate features of the flat layout such as tests being included with the package when they are installed) to support troubleshooting across installations. + + In the end, the advantages of using the **src/** layout for scientific packages + that already use this approach do not + outweigh the maintenance cost that a large cultural change in + package structure would bring to existing maintainers the scientific + Python ecosystem. + + +```{tip} + +## Core scientific Python packages that use the flat layout + +* [numpy](https://github.com/numpy/numpy) +* [scipy](https://github.com/scipy/scipy) +* [pandas](https://github.com/pandas-dev/pandas) +* [xarray](https://github.com/pydata/xarray) +* [Jupyter-core](https://github.com/jupyter/jupyter_core) +* [Jupyter notebook](https://github.com/jupyter/notebook) +* [scikit-learn](https://github.com/scikit-learn/scikit-learn) + +It would be a significant maintenance cost and burden to move all of these +packages to a different layout. The potential benefits of the source layout +for these tools is not worth the maintenance investment. To avoid dividing the scientific Python community, +pyOpenSci supports maintainers using a flat layout for scientific packages. +``` + +## What does the flat layout structure look like? + +The flat layout's primary characteristics are: + +* The source code for your package lives in a directory with your package's name in the root of your directory +* Often the `tests/` directory also lives within that same `package-name` directory. + +Below you can see the recommended structure of a scientific Python package +using the flat layout. + +```bash +myPackage/ +β”œβ”€β”€ CHANGELOG.md ┐ +β”œβ”€β”€ CODE_OF_CONDUCT.md β”‚ +β”œβ”€β”€ CONTRIBUTING.md β”‚ +β”œβ”€β”€ docs/ β”‚ Package documentation +β”‚ └── ... β”‚ +β”œβ”€β”€ LICENSE β”‚ +β”œβ”€β”€ README.md β”˜ +β”œβ”€β”€ pyproject.toml ] Package metadata and build configuration +| myPackage/ ┐ +β”‚ β”œβ”€β”€ __init__.py β”‚ Package source code +β”‚ β”œβ”€β”€ moduleA.py β”‚ +β”‚ └── moduleB.py β”˜ + tests/ ┐ + └── test-file1.py | Package tests + └── .... β”˜ +``` + + +```{note} +If you look at +[the `matplotlib` repository on GitHub](https://github.com/matplotlib/matplotlib), +you will notice it too has a src/ directory, however +that structure is there because `matplotlib` has uncompiled source code in that +directory. it is not adhering to the **src/** layout explicitly. +``` + +### Benefits of using the flat layout in your Python package + +There are some benefits to the scientific community in using the flat layout. + +* This structure has historically been used across the ecosystem and packages +using it are unlikely to change. Thus, you'd be following a convention that many +packages use already. +* This structure is simpler to setup to support packaging compared to the `src/` +approach given the package source code is in the root directory. This means that +your package will be "found" by build back-ends automagically. +* You can directly install the package directly from the root directory. +* Tests are shipped with your package. This allows developers to invoke tests on +various machines to troubleshoot installations if needed. (This could easily +be implemented using a `src/` layout too!) + +### The `src/` layout for Python packages + +The `src/` layout is another option to structure you Python package. This layout +is advocated for by the [PyPA](https://packaging.python.org/en/latest/tutorials/packaging-projects/) +The key characteristic of this layout is that your package +uses a `src/package-name` directory structure. Tests are +often located in a directory outside of the package (but they don't have to be). + +#### Pros of the src/ layout + +The benefits of the `src/` layout approach include: + +* It ensures that tests are always running on the installed version of your +package rather than on the flat files +* If `tests/` are outside of the `src/` directory, they aren't delivered to the user +installing your package. This makes the package size slightly smaller. +* This layout is semantically more clear. Code is always found in the +`src/package-name` directory, `tests/` are in the root and with docs/ + +#### Cons of the src/ layout +* Can be slightly trickier to configure. examples: + * Tools like build won't automatically find the package directory if it's + within `src/package-name` without being configured. +* Setting up continuous integration is more complex as you will have to account +for the `src/` directory when installing the package +* While this layout is common in the broader Python ecosystem, it does not +necessarily support the needs of the scientific Python ecosystem which often has +tools that wrap around other compiled languages such as C++. Often times code +that needs to be compiled is stored in `src/`. + +An example of the `src/` layout structure can be seen below: + +``` +myPackage +β”œβ”€β”€ CHANGELOG.md ┐ +β”œβ”€β”€ CODE_OF_CONDUCT.md β”‚ +β”œβ”€β”€ CONTRIBUTING.md β”‚ +β”œβ”€β”€ docs β”‚ Package documentation +β”‚ └── index.md +β”‚ └── ... β”‚ +β”œβ”€β”€ LICENSE β”‚ +β”œβ”€β”€ README.md β”˜ +β”œβ”€β”€ pyproject.toml ┐ +β”œβ”€β”€ src β”‚ +β”‚ └── myPackage β”‚ Package source code, metadata, +β”‚ β”œβ”€β”€ __init__.py β”‚ and build instructions +β”‚ β”œβ”€β”€ moduleA.py β”‚ +β”‚ └── moduleB.py β”˜ +└── tests ┐ + └── ... β”˜ Package tests +``` + + + + +To install your package in editable mode use: + +```bash +$ cd package-name +$ pip install -e .` +``` + +## Core file requirements for a Python package + +In the above example, notice that all of the core documentation files that +pyOpenSci requires live in the root of your project directory. These files +include: + + +* CHANGELOG.md +* CODE_OF_CONDUCT.md +* CONTRIBUTING.md +* LICENSE.txt +* README.md + +Also note that there is a **docs/** directory at the root where your user-facing +documentation website content lives. + +```{button-link} https://www.pyopensci.org/python-package guide/documentation +:color: primary +:class: sd-rounded-pill float-left +Click here to read about our packaging documentation requirements. <> +``` + +Finally, notice that the **tests/** directory containing your test suite is +located within the **packageName/** directory. + + + +```{important} +If your package tests require data, we suggest that you NOT include that +data within your package structure. We will discuss this in more detail in a +tutorial. +``` + + + +## Use a pyproject.toml file for your package configuration & metadata + +We strongly recommend that you [include all project based metadata and build system specifications in a `pyproject.toml` file.](https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/) [Using setup.py to manage both package setup and hold metadata can present numerous risks.](https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html) + + +```{note} +Moving away from the **setup.py** file provides two benefits: + +1. Because setup.py has a mixture of code and metadata, it opens the user to a potential bad code injection on their computer when the package is installed. +1. Including your package's metadata in a separate human-readable `pyproject.toml` format also allows someone to view the project's metadata without +running any Python code. +``` + +The [TOML (Tom's Obvious, Minimal Language) format](https://toml.io/en/), is an easy-to-read structure that is founded on key: value pairs. + +Each section in the file contains a `[table identifier]`. +Below that table identifier are key value pairs that +support configuration for that particular table. + +```{note} +[PEP518 describes the move away from setup.py to the pyproject.toml file.](https://peps.python.org/pep-0518/) +Python package standards are moving away from +including both package metadata and python code needed to setup a package installation in the same **setup.py** file. Instead we are moving towards using +a **proproject.toml** file sometimes combined with a **setup.cfg** file. + +In some cases +where a build is particularly complex, a **setup.py** file may still be required. +``` + +## Example pyproject.toml + +Below is an example build configuration for a Python project. This setup +requires: + +* **setuptools** to create the package structure, +* **wheel** which is used by `setuptools` to create the [**.whl** (wheel) file](https://realpython.com/python-wheels/). +* **setuptools build** to "build" the package +* **setuptools_scm** to manage package version updates + +In the example below `[build-system]` is the first table +of values. It has two keys that specify the build front end and backend for a package: + +1. `requires =` +1. `build-backend =` + +``` +[build-system] +requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"] +build-backend = "setuptools.build_meta" + +[project] +name = "examplePy" +authors = [ + {name = "Some Maintainer", email = "some-email@pyopensci.org"} +] +maintainers = [{name = "All the contributors"}] +license = {text = "BSD 3-Clause"} +description = "An example Python package used to support Python packaging tutorials" +keywords = ["pyOpenSci", "python packaging"] +readme = "README.md" + +dependencies = [ + "dependency-package-name-1", + "dependency-package-name-2", +] +``` + + +Notice that you also can specify dependencies in this file. + + + + + +```{tip} +PEPs stand for Python Enhancement Protocols. They provide guidelines for standardizing +Python code and packaging. +``` + + + \ No newline at end of file diff --git a/package-structure-code/python-package-versions.md b/package-structure-code/python-package-versions.md new file mode 100644 index 00000000..53c9019c --- /dev/null +++ b/package-structure-code/python-package-versions.md @@ -0,0 +1,152 @@ +# Creating New Versions of Your Python Package + +## Section about releases +* mention release should be incremetal +* rep changes in the code that are either patches, minor fixes, major updates + +pyOpenSci recommends that you follow the [Python PEP440](https://peps.python.org/pep-0440) which recommends using +[semantic versioning guidelines](https://www.python.org/dev/peps/pep-0440/#semantic-versioning) +when assigning release values to new package versions. + +[Semantic versioning, discussed in detail at semver.org,](https://semver.org/) uses the following approach to bumping your +package's versions: + +> Given a version number MAJOR.MINOR.PATCH, increment the: +> +> * **MAJOR version** when you make incompatible API changes +> * **MINOR version** when you add functionality in a backwards compatible manner +> * **PATCH version** when you make backwards compatible bug fixes +> Additional labels for pre-release and build metadata are +> available as extensions to the MAJOR.MINOR.PATCH format. + + +```{tip} +Resources: + +* Poetry bump version - go discussion here - https://py-pkgs.org/07-releasing-versioning#manual-version-bumping + +``` + + +```{important} +pyOpenSci requires that your package has an installable distribution that can be installed from a public community repository such as PyPI or a conda channel. +``` + +## Tools to manage versions for your Python package + +PyOpenSci doesn't require any specific tools to support +creating an installable distribution for (packaging) and releasing your package. HOwever there are a handful that +have been broadly adopted by the scientific python community, +and we provide an overview of each of those below. + +If you are on the fence about what tool to use, we suggest +that you use `setuptools-scm`. + +### Tools for bumping Python package versions +There are many tools available to manage "bumping" and naming versions for your Python +package. + +```{note} +Bumping a package version refers to the step of increasing the package +version after a set number of changes have been made to it. For example, +you might bump from version 0.8 to 0.9 of a package. or from 0.9 to 1.0. + +Using semantic versioning, there are three main "levels" +of versions that you might consider: + +Major, minor and patch. These are described in more detail below. +``` + +Below we discuss some of the tools that are most common +in the Python scientific package ecosystem. We do not require +you to use a specific tool. We simply recommend tools based on +what the community is currently using. + +The list below are the tools discussed on this page: + +* setuptools-scm +* bump2version (last commit mar 2 - is it maintained?) - https://github.com/c4urself/bump2version +* versioneer +* python-semantic-version + +### Tool 1: setuptools-scm a light weight version option + +[`Setuptools-scm`](https://github.com/pypa/setuptools_scm/) is one of the more popular tools see in our packages +both within the [pyOpenSci package ecosystem](https://www.pyopensci.org/python-packages) and across the Python Scientific ecosystem. + +We like `setuptools-scm` because: + +**Pros** +* It creates a single-source file that contains your package version. +* You never manually update the package version +* You can automate writing the version anywhere in your package including your documentation! +* It supports a purely GitHub based release workflow. This simplifies maintenance workflows. +* Version number is updated in your package via a hidden `_version.py` file. There is no manual configuration updates required. +* While we like detailed commit messages (See Python Semantic Version below), we know that sometimes when maintaining a package specific guidelines around commit messages can be hard to apply and manage. + +**Cons** +* In a CI workflow you will end up manually entering or creating the version number via a TAG on GitHub. +* Not well documented (we hope to fix that!) + +```{important} +pyOpenSci will be creating tutorials on working with `setuptools-scm` and GitHub releases to +update versions of your package and push to PyPI. These should be published sometime +during the spring/summer 2023. In the meantime [here is a high quality blog post +that will help you get started with using setuptools-scm](https://www.moritzkoerber.com/posts/versioning-with-setuptools_scm/) +``` + +### Tool 2: [Python semantic release](https://python-semantic-release.readthedocs.io/en/latest/) + + +**Pros** +* Follows semver versioning closely +* Enforces maintainers using descriptive commit messages which can simplify troubleshooting + +**Cons** +* requires very specific commit language to work. In practice some maintainers may not be able to maintain that level of specificity in commit messages (altho there are bots to help with commit checks) +* Release happens at the command line. This makes is harder to implement a GitHub based release workflow as the wrong commit message could trigger a release. +* The version number is manually updated in a configuration file such as `pyproject.toml` vs. in the package itself. + +As the name implies, Python Semantic Release follows python +semantic version release rules. +Python Semantic Release, similar to **setuptools-scm**, also helps you automate version release workflows. + +However, this tool differs +from **setuptools-scm**. With **setuptools-scm**, a version +control tag, is used to trigger a version update. +With Python semantic release, versions are triggered using +specific language found in a repository commit message. + +For example, the words `fix(attribute_warning):` trigger Python Semantic Release to implement a patch version bump. +So for instance if your package was at version 1.1.0 and you +made the commit below with the words fix(text-here), Python Semantic Release would bump your package to version 1.1.1. + +```bash +$ git commit -m "fix(mod_plotting): fix for warnings returned for some athlete attributes" +``` + +Similarly a feature (`feat()`) triggers a minor version bump. +For example from version 1.1 to version 1.2 + +```bash +git commit -m "feature(add_conversions): add value conversions to activity date using pint" +``` + +```{tip} +You can find a thoughtful discussion of python semantic version [in this Python package guide.](https://py-pkgs.org/07-releasing-versioning#automatic-version-bumping). Note that the guide hasn't been updated since 2020 and as such some of the commands are now dated in it. +``` + +### Tool 3: Versioneer + +I haven't used this tool before... +Ask martin??? + +### Tool 4: Bump version + +* Versioning can be done using [bumpversion](https://github.com/peritus/bumpversion), e.g. for a minor update: + +``` +bumpversion minor +``` + +"minor" can be replaced with "major" or "patch" depending on the level of update. \ No newline at end of file diff --git a/package-structure-code/testing-ci.md b/package-structure-code/testing-ci.md new file mode 100644 index 00000000..b323b82c --- /dev/null +++ b/package-structure-code/testing-ci.md @@ -0,0 +1,83 @@ +# Testing and CI?? + + + + + + +### Testing +- All packages should have a test suite that covers major functionality of the package. The tests should also cover the behavior of the package in case of errors. +- It is good practice to write unit tests for all functions, and all package code in general, ensuring key functionality is covered. Test coverage below 75% will likely require additional tests or explanation before being sent for review. +- We recommend using pytest for writing tests, but you can use other tools. Strive to write tests as you write each new function. This serves the obvious need to have proper testing for the package, but allows you to think about various ways in which a function can fail, and to defensively code against those. +- Consider using tox to test your package with multiple versions of Python 2 and 3. +- If you set up CI with code coverage, use your package's code coverage report to identify untested lines, and to add further tests. + +**Good/Better/Best:** +- **Good:** A test suite that covers major functionality of the package. +- **Better:** The above, with high code coverage. +- **Best:** All of the above, plus using tox to test multiple versions of Python. + +### Continuous Integration +All pyOpenSci packages must use some form of continuous integration. + +- For Linux and Mac OSX, we suggest GitHub Actions, Circle CI or Travis CI. +- For Windows, we suggest GitHub Actions or AppVeyor CI. +- In many cases, you will want CI for all platforms. Different continuous integration services will support builds on different operating systems. Packages should have CI for all platforms when they contain: + - Compiled code + - Java dependencies + - Dependencies on other languages + - Packages with system calls + - Text munging such as getting people’s names (in order to find encoding issues). + - Anything with file system / path calls + - In case of any doubt regarding the applicability of these criteria to your package, it’s better to add CI for all platforms, and most often not too much hassle. + +**Good/Better/Best:** +- **Good:** Some sort of CI service with status badge in your README. +- **Better:** The above plus integrated code coverage and linting. +- **Best:** Continuous integration for all platforms: Linux, Mac OSX, and Windows. + + + + + +This section provides guidelines and tips for creating a Python package to submit for peer-review. + +pyOpenSci packages must: +- Have a clear README _including_ installation instructions. +- Contain full documentation for any user-facing functions. +- Have a test suite that covers the major functionality of the package. +- Use continuous integration. +- Use an OSI approved software license. + + + + + +### License +pyOpenSci projects should use an open source software license that is approved by the Open Software Initiative (OSI). OSI's website has a [list of popular licenses](https://opensource.org/licenses), and GitHub has a [handy tool](https://choosealicense.com/) for choosing a license. + +**Good/Better/Best:** +- **Good:** Include a open source software license with your package. +- **Better/Best:** Choose a license based on your needs and future use of package, plus explain your choice in your submission for review. + +## Other recommendations +### Python version support +You should always be explicit about which versions of Python your package supports. +Keeping compatibility with old Python versions can be difficult as functionality changes. +A good rule of thumb is that the package should support, at least, +the latest three Python versions (e.g., 3.8, 3.7, 3.6). + +### Code Style +pyOpenSci encourages authors to consult [PEP 8](https://www.python.org/dev/peps/pep-0008/) for information on how to style your code. + +### Linting +An automatic linter (e.g. flake8) can help ensure your code is clean and free of syntax errors. These can be integrated with your CI. + +### Badges + +Badges are a useful way to draw attention to the quality of your project and to +assure users that it is well-designed, tested, and maintained. +It is common to provide a collection of badges in a table for others +to quickly browse. + +[See this example of a badge table](https://github.com/ropensci/drake). Such a table should be more wide than high. (Note that the badge for pyOpenSci peer-review will be provided upon acceptance.)