diff --git a/documentation/repository-files/code-of-conduct-file.md b/documentation/repository-files/code-of-conduct-file.md index 71c205ab..c5cbc502 100644 --- a/documentation/repository-files/code-of-conduct-file.md +++ b/documentation/repository-files/code-of-conduct-file.md @@ -1,13 +1,13 @@ -# The CODE_OF_CONDUCT.md file In Your Python Open Source Package +# The CODE_OF_CONDUCT file - Python Packaging -```{admonition} Example Code of Conduct files +```{admonition} Example CODE_OF_CONDUCT files :class: tip * [SciPy Code of Conduct file - notice they included theirs in their documentation](https://docs.scipy.org/doc/scipy/dev/conduct/code_of_conduct.html) -* [fatiando code of conduct file](https://github.com/fatiando/community/blob/main/CODE_OF_CONDUCT.md) +* [fatiando CODE_OF_CONDUCT.md file](https://github.com/fatiando/community/blob/main/CODE_OF_CONDUCT.md) ``` -Your package should have a **CODE_OF_CONDUCT.md** file located +Your package should have a `CODE_OF_CONDUCT.md` file located the root of the repository. Once you have people using your package, you can consider the package itself as having a community around it. Some of this community uses your tool. These users @@ -17,20 +17,20 @@ Others in the community might want to contribute to your tool. They might fix bugs, update documentation and engage with the maintainer team. -## Why you need a code of conduct +## Why you need a CODE_OF_CONDUCT In order to keep this community healthy and to protect yourself, -your mainatianer team and your users from unhealthy behavior, -it is important to have a [code of conduct](https://opensource.guide/code-of-conduct/). +your maintainer team and your users from unhealthy behavior, +it is important to have a [`CODE_OF_CONDUCT`](https://opensource.guide/code-of-conduct/). -The code of conduct is important +The `CODE_OF_CONDUCT` is important as it establishes what you expect in terms of how users and contributors interact with maintainers and each other. It also establishes rules and expectations which can then be enforced -if need be to protect others from harmful and/or negative behaviors. +if need be to protect others from harmful and/or negative behaviors. If you are not comfortable -with creating your own code of conduct text, we encourage you to adopt the -code of conduct language used in the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). -[Many other communities](https://www.contributor-covenant.org/adopters/) have adopted this code of conduct as +with creating your own `CODE_OF_CONDUCT` text, we encourage you to adopt the +`CODE_OF_CONDUCT` language used in the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). +[Many other communities](https://www.contributor-covenant.org/adopters/) have adopted this `CODE_OF_CONDUCT` as their own. See the [Fatiando a Terra Geoscience Python community's example here.](https://github.com/fatiando/community/blob/main/CODE_OF_CONDUCT.md) diff --git a/documentation/repository-files/license-files.md b/documentation/repository-files/license-files.md index 93bd9874..83525f26 100644 --- a/documentation/repository-files/license-files.md +++ b/documentation/repository-files/license-files.md @@ -1,14 +1,70 @@ -# Your repository should have a LICENSE.md file +# License files for scientific Python open source software -The root of your GitHub repository should also have a LICENSE.txt file. +:::{button-link} https://www.pyopensci.org/about-peer-review/ +:color: primary +:class: sd-rounded-pill float-left -To be reviewed by pyOpenSci your project 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). GitHub also has a -[handy tool](https://choosealicense.com/) for choosing a license. +Want to learn how to add a license file to your GitHub repository? Check out this lesson. +::: + +## What is a Open Source License file? + +When we talk about LICENSE files, we are referring to a file in your +GitHub or GitLab repository that contains legally binding language +that describes to your users how they can legally use (and not use) your package. + +## Why licenses are important + +A license file is important for all open source projects because it protects both you as a maintainer and your users. The license file helps your users and the community understand: + +1. How they can use your software +2. Whether the software can be reused or adapted for other purposes +3. How people can contribute to your project + +and more. + +[Read more about why license files are critical in protecting both you as a maintainer and your users of your scientific Python open source package.](https://opensource.guide/legal/#just-give-me-the-tldr-on-what-i-need-to-protect-my-project) + + +## Where to store your license + +Your `LICENSE` file should be stored at root of your GitHub / GitLab repository. + +Some maintainers customize the language in their license files for specific reasons. However, if you are just getting started, we suggest that you select a +permissive license and then use the legal language templates provided both by GitHub and/or the [choosealicense.com](https://choosealicense.com/) website. + +Licenses are legally binding, as such you should avoid trying to create your own license unless you have the guidance of legal council. + +### Use open permissive licenses when possible + +We generally suggest that you use a permissive, license that is [Open Software Initiative (OSI) approved](https://opensource.org/licenses). If you are +[submitting your package to pyOpenSci for peer review](https://www.pyopensci.org/about-peer-review/index.html), then we require an OSI approved +license. + +### How to choose a license + +To select your license, we suggest that you use GitHub's +[Choose a License tool ](https://choosealicense.com/). + +If you choose your license when creating a new GitHub repository, you can also +automatically get a text copy of the license file to add to your repo. However +in some cases the license that you want is not available through that online +process. + +:::{admonition} License recommendations from the SciPy package +[The SciPy documentation has an excellent overview of licenses.](https://docs.scipy.org/doc/scipy/dev/core-dev/index.html#licensing). Once of the key elements +that these docs recommend is ensuring that the license that you select is +complementary to license used in the core scientific Python ecosystem. +Below is a highlight of this text which outlines license that are compatible +with the modified BSD license that SciPy uses. + +> Other licenses that are compatible with the modified BSD license that SciPy uses are 2-clause BSD, MIT and PSF. Incompatible licenses are GPL, Apache and custom licenses that require attribution/citation or prohibit use for commercial purposes. + +To coordinate with other packages in our scientific ecosystem, we also recommend +that you consider using either BSD or MIT as your +license. If you are unsure, the MIT license tends to be a simpler easier-to-understand option. +::: -If you choose your license through GitHub, you can also automatically get a copy of the license file to add to your repository. ## Important: make sure that you closely follow the guidelines outlines by the License that you chose @@ -19,18 +75,26 @@ If you borrow code from other tools or online sources, make sure that the license for the code that you are using also complies with the license that you selected for your package. -```{note} -An example of code that would not comply with a BSD or MIT license would be any code copied from StackOverflow website. -[Stack overflow users a Creative Commons Share Alike license.](https://stackoverflow.com/help/licensing) The sharealike license requires you to use the same sharealike license when you reuse any code from stackoverflow. Thus, if you use code from stack overflow in your package and have a MIT license applied to your package, you are violating stack overflow's license requirements! Proceed with caution here! -``` +:::{admonition} An example of how a license determine how code can be reused +:class: note + +Let's use stackOverflow as an example that highlights how a license determines how code can or can not be used. + +[Stack overflow uses a Creative Commons Share Alike license.](https://stackoverflow.com/help/licensing). The sharealike license requires you to use the same sharealike license when you reuse any code from stackoverflow. -[The SciPy documentation has an excellent license discussion that is worth reading and considering for your project's development guide.](https://docs.scipy.org/doc/scipy/dev/core-dev/index.html#licensing) +This means that technically, if you copy code from the Stack Overflow website, and use it in your package. And your packages uses a different license such as a MIT license, you are violating Stack Overflow's license requirements! +🚨 Proceed with caution! 🚨 +::: - diff --git a/package-structure-code/pyproject-toml-python-package-metadata.md b/package-structure-code/pyproject-toml-python-package-metadata.md index 60283d32..060177f3 100644 --- a/package-structure-code/pyproject-toml-python-package-metadata.md +++ b/package-structure-code/pyproject-toml-python-package-metadata.md @@ -1,14 +1,114 @@ # Use a pyproject.toml file for your package configuration & metadata + + +:::{admonition} Important pyproject.toml take aways +:class: todo + +1. There are only two tables that are required for an installable Python package: **[build-system]** and **[project]**. The **[project]** table stores your package's metadata. +2. There are only two _required_ fields in the **[project]** table: **name=** and **version=**. +3. We suggest you add additional metadata to your `pyproject.toml` file as it will make it easier for users to find your project on PyPI. +4. When you are adding classifiers to the [project] table, only use valid values from [PyPI’s classifier page](https://PyPI.org/classifiers/). An invalid value here will raise an error when you build your package or publish to PyPI. +5. There is no specific order for tables in the `pyproject.toml` file. However fields need to be placed within the correct table sections. For example `requires =` always need to be associated with the **[build-system]** table. +6. **python-requires**: is important to have in your `pyproject.toml` file as it helps pip install your package. + + + +## About the pyproject.toml file + +Every modern Python package should include a `pyproject.toml` file. If your project is pure Python and you're using a `setup.py` or `setup.cfg` file to describe its metadata, you should consider migrating your metadata and build information to a `pyproject.toml` file. + +If your project isn’t pure-python, you might still require a `setup.py` file to build the non Python extensions. However, a `pyproject.toml` file should still be used to store your project’s metadata. + +:::{admonition} What happened to setup.py & how do i migrate to pyproject.toml? +:class: note +Prior to August 2017, Python package metadata was stored either in the `setup.py` file or a `setup.cfg` file. In recent years, there has been a shift to storing Python package metadata in a much more user-readable `pyproject.toml` format. Having all metadata in a single file: + +- simplifies package management, +- allows you to use a suite of different [build backends](https://www.pyopensci.org/python-package-guide/package-structure-code/python-package-build-tools.html#build-back-ends) such as (flit-core, hatchling, pdm-build), and +- aligns with modern best practices. + + +::: + The standard file that Python packages use to [specify build requirements and metadata is called a **pyproject.toml**](https://packaging.python.org/en/latest/specifications/declaring-project-metadata/). Adding metadata, build requirements and package dependencies to a **pyproject.toml** file replaces storing that information in a setup.py or setup.cfg file. +### About the .toml format + The **pyproject.toml** file is written in [TOML (Tom's Obvious, Minimal Language) format](https://toml.io/en/). TOML is an easy-to-read structure that is founded on key/value pairs. Each section in the **pyproject.toml** file contains a `[table identifier]`. Below that table identifier are key/value pairs that support configuration for that particular table. +- Below `[build-system]` is considered a table in the toml language. +- Within the build-system table below requires = is a key. +- The associated value for requires is an array containing the value "hatchling". + +```toml +[build-system] # <- this is a table +requires = ["hatchling"] # requires = is a key and "hatchling" is a value contained within an array specified by square brackets []. + +``` + +### How the pyproject.toml is used when you build a package + + + +When you publish to PyPI, you will notice that each package has metadata listed. Let’s have a look at [xclim](https://pypi.org/project/xclim/), one of our [pyOpenSci packages](https://www.pyopensci.org/python-packages.html). Notice that on the PyPI landing page you see some metadata about the package including python, maintainer information and more. PyPI is able to populate this metadata because it was defined using correct syntax and classifiers by Xclim's maintainers, [pyproject.toml file](https://github.com/Ouranosinc/xclim/blob/master/pyproject.toml). This metadata when the xclim package is built, is translated into a distribution file that allows PyPI to read the metadata and print it out on their website. + +```{figure} ../images/python-build-package/pypi-metadata-classifiers.png +:scale: 50 % +:align: center +:alt: Image showing the left side bar of PyPI for the package xclim. The section at the top says Classifier. Below there is a list of items including Development status, intended audience, License, natural language, operating system, programming language and topic. Below each of those sections are various classifier options." width="300px"> + +When you add the classifier section to your pyproject.toml +and your package is built, the build tool organizes the metadata into a format that PyPI can understand and +represent on your PyPI landing page. These classifiers also allow users to sort through packages by version of python they support, categories and more. +``` + ## Benefits of using a pyproject.toml file Including your package's metadata in a separate human-readable **pyproject.toml** @@ -29,17 +129,109 @@ resources working with complex builds in the future. ``` -## Example pyproject.toml for building using PDM +## Optional vs. Required pyproject.toml file fields -Below is an example build configuration for a Python project. This example -package setup uses: +When you create your `pyproject.toml` file, there are numerous metadata fields that you can use. Below we suggest specific fields to get you started that support publication on PyPI and users finding your package. + +[An overview of all of the project metadata elements can be found here.](https://packaging.python.org/en/latest/specifications/core-metadata/#project-url-multiple-use) + +### Required fields for the [project] table + +As mentioned above, your pyproject.toml file needs to have a **name** and **version** field in order to properly build your package: + +- Name: This is the name of your project provided as a string +- Version: This is the version of your project. If you are using a scm tool for versioning (using git tags to determine versions), then the version may be dynamic (more on that below). + +### Optional fields to include in the `[project]` table + +We strongly suggest that you also add the metadata keys below as they will +help users finding your package on PyPI. These fields will make it +clear how your package is structured, what platforms you support and +what dependencies your package requires. + +- **Description:** this is a short one-line description of your package. +- **Readme:** A link to your README.md file is used for the long long-description. This information will be published on your packages PyPI landing page. +- **Requires-python** (used by pip): this is a field that is used by pip. Here you tell the installer whether you are using Python 2.x or 3.x. Most projects will be using 3.x. +- **License:** the license you are using +- **Authors:** these are the original authors of the package. Sometimes the authors are different from the maintainers. Other times they might be the same. +- **Maintainers:** you can choose to populate this or not. You can populate this using a list with a sub element for each author or maintainer name, email + +```toml +authors = [ + {name = "A. Random Developer", email = "author@example.com" } +] +``` + +- **dependencies:** dependencies are optional but we strongly suggest you include them in your pyproject.toml. Dependencies will be installed by pip when your project is installed creating a better user-experience. + +- **`[project.optional-dependencies]`:** the optional or development dependencies will be installed if someone runs `pip install projectname[dev]`. This is a nice way to include your development dependencies for users who may wish to contribute to your project. + +- **keywords:** These are the keywords that will appear on your PyPI landing page. Think of them as words that people might use to search for your package. +- **classifiers:** The classifiers section of your metadata is also important for the landing page of your package in PyPI and for filtering of packages in PyPI. A list of [all options for classifiers can be found her](https://PyPI.org/classifiers/)e. Some of the classifiers that you should consider including + - Development Status + - Intended Audience + - Topic + - LIcense + - Programming language + +### Advanced options in the pyproject.toml file + +The examples at the bottom of this page contain ... + +- **`[project.scripts]` (Entry points):** Entry points are optional. If you have a command line tool that runs a specific script hosted in your package, you may include an entry point to call that script directly at the command line (rather than at the Python shell). + - Here is an example of[ a package that has entry point script](https://github.com/pyOpenSci/update-web-metadata/blob/main/pyproject.toml#L60)s. Notice that there are several core scripts defined in that package that perform sets of tasks. pyOpenSci is using those scripts to process their metadata. +- **Dynamic Fields:** if you have fields that are dynamically populated. One example of this is if you are using scm / version control based version with tools like `setuptooms_scm`, then you might use the dynamic field. such as version (using scm) **dynamic = ["version"]** + +## Add dependencies to your pyproject.toml file -- **pdm.backend** to build the [package's sdist and wheels](python-package-distribution-files-sdist-wheel) +The pyproject.toml file can also be used as a replacement for the requirements.txt file which has been traditionally used to store development dependencies such as pytest, code formatters such as Black and documentation tools such as sphinx. +To add dependencies to your build, add a `[project.optional-dependencies]` table to your pyproject.toml file. + +Then specify dependency groups as follows: + +``` +[project.optional-dependencies] +tests = [ + "pytest, + "pytest-cov" +] +lint = [ + "black", + "flake8" +] +docs = [ + "sphinx", + "pydata-sphinx-theme +] ``` + +Following the above example, you install dependencies like this: + +- `pip install -e .[tests]` + +The above will install both your package in editable mode and all of the dependencies declared in the tests section of your `[project.optional-dependencies]` table. + +To install all dependencies and also your package, you'd use: + +`pip install -e .[tests,lint,docs]` + +:::{admonition} Recursive dependencies +:class: tip + +You can also setup sets of recursive dependencies. [See this blog post for more.](https://hynek.me/articles/python-recursive-optional-dependencies/) + +::: + +## Example pyproject.toml for building using hatchling + +Below is an example build configuration for a Python project. This example +package setup uses **hatchling** to build the [package's sdist and wheels](python-package-distribution-files-sdist-wheel). + +```toml [build-system] -requires = ["pdm-backend>=1.0.0"] -build-backend = "pdm.backend" +requires = ["hatchling"] +build-backend = "hatchling.build" [project] name = "examplePy" diff --git a/package-structure-code/python-package-distribution-files-sdist-wheel.md b/package-structure-code/python-package-distribution-files-sdist-wheel.md index 0af8f50f..19886502 100644 --- a/package-structure-code/python-package-distribution-files-sdist-wheel.md +++ b/package-structure-code/python-package-distribution-files-sdist-wheel.md @@ -145,7 +145,7 @@ or is more complex in its build, the two distributions will be very different. Also note that we are not discussing conda build workflows in this section. [You can learn more about conda builds here.](https://conda.io/projects/conda-build/en/latest/user-guide/tutorials/index.html) ``` - +(python-source-distribution)= ## Source Distribution (sdist) **Source files** are the unbuilt files needed to build your @@ -219,6 +219,7 @@ items including a metadata directory and if you use `setuptools_scm` or `hatch_v the sdist may also contain a file that stores the version. ``` +(python-wheel)= ## Wheel (.whl files): A wheel file is a ZIP-format archive whose filename follows a specific format