Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5

- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
29 changes: 29 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CodeQL

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 6 * * 1" # Mondays at 06:00 UTC

jobs:
analyze:
name: Analyze (python)
runs-on: ubuntu-latest
permissions:
security-events: write
actions: read
contents: read

steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: python
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:python"
18 changes: 18 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: pre-commit

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- uses: pre-commit/action@v3.0.1
23 changes: 9 additions & 14 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,20 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.12"
cache: "pip"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest pytest-cov
- name: Install package
run: |
pip install pytest pytest-cov
pip install .
- name: Run unittests
run: |
python -m pytest
- name: Test coverage
run: |
pytest --cov --cov-report=xml
- name: Run tests with coverage
run: pytest --cov --cov-report=xml
- name: Upload coverage report
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
token: ${{ secrets.CODECOV_TOKEN }}
36 changes: 7 additions & 29 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
Expand All @@ -11,38 +11,16 @@ repos:
- id: check-docstring-first
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: debug-statements
- id: requirements-txt-fixer
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.9 # Ruff version.
hooks: # Run the linter.
- id: ruff
types_or: [ python, pyi ]
args: [ --fix ]
- id: ruff-format # Run the formatter.
types_or: [ python, pyi ]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.9
hooks:
- id: python-use-type-annotations
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/adrienverge/yamllint
rev: v1.28.0
rev: v1.35.1
hooks:
- id: yamllint
args: ["-d", "{ignore: .pre-commit-config.yaml}"]
language: python
types: [yaml]
- repo: https://github.com/pycqa/pydocstyle
rev: 6.1.1
hooks:
- id: pydocstyle
language: python
types: [python]
- repo: https://github.com/asottile/blacken-docs
rev: v1.12.1
hooks:
- id: blacken-docs
language: python
types: [python]
additional_dependencies: [black==20.8b1]
17 changes: 17 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# License — placeholder

This file is a placeholder. **Replace it with the full text of your chosen
license before publishing the package.**

Pick a license at <https://choosealicense.com/> and paste its full text here.
Common choices for open-source Python packages: MIT, Apache-2.0, BSD-3-Clause.

After replacing this file, also:

- Update the year and copyright holder in the license text.
- Update the `license` field in `pyproject.toml` (e.g., `license = "MIT"`
using an SPDX expression per PEP 639).
- Update the LICENSE link in `docs/index.rst` if needed.

Until you replace this file, no license is granted and the work is "all
rights reserved" by default.
155 changes: 82 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,135 +2,144 @@
[![Documentation Status](https://readthedocs.org/projects/my_package/badge/?version=latest)](https://my_package.readthedocs.io/en/latest/?badge=latest)
[![codecov](https://codecov.io/gh/mahynski/my_package/branch/main/graph/badge.svg?token=YSLBQ33C7F)](https://codecov.io/gh/mahynski/my_package)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
[![DOI](https://zenodo.org/badge/331207062.svg)](https://zenodo.org/badge/latestdoi/)
<!--[![DOI](https://zenodo.org/badge/{github_id}.svg)](https://zenodo.org/badge/latestdoi/{github_id})-->

<!--
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
-->
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![DOI](https://zenodo.org/badge/{github_id}.svg)](https://zenodo.org/badge/latestdoi/{github_id})

PyPI Package Template Instructions
===

<img src="docs/_static/logo_transparent.png" align="right" width=200 />

1. Choose a name that does not exist in [pypi](https://pypi.org/). You can check by going to https://pypi.org/simple/{my_awesome_new_package}; if you recieve a 404 error the name has not been taken.
2. Replace all "my_package" names, strings, etc. in this repo with your desired package name (e.g., "my_awesome_new_package"). The command below makes this simple; run this after cloning the repo locally.
1. Choose a name that does not exist in [pypi](https://pypi.org/). You can check by going to `https://pypi.org/simple/{my_awesome_new_package}`; a 404 means the name is available.
2. Replace all `my_package` names, strings, etc. in this repo with your desired package name (e.g., `my_awesome_new_package`). The command below makes this simple; run it after cloning the repo locally. The `sed` syntax below is for GNU `sed` (Linux); on macOS use `sed -i ''` instead of `sed -i`.

~~~bash
$ for file in $(find . -type f -not -path "./.git/*"); do sed -i "s/my_package/my_awesome_new_package/g" $file; done
~~~
```bash
for file in $(find . -type f -not -path "./.git/*"); do
sed -i "s/my_package/my_awesome_new_package/g" "$file"
done
```

You should similarly replace all instances of "mahynski" with your GitHub username. Also, rename the `my_package` directory to match your chosen name.
Similarly, replace all instances of `mahynski` with your GitHub username, and rename the `my_package/` directory to match your chosen name.

~~~bash
$ mv my_package/ my_awesome_new_package/
$ git add my_awesome_new_package/
$ git commit -m "created my_awesome_new_package" .
~~~
```bash
git mv my_package my_awesome_new_package
git commit -m "rename package to my_awesome_new_package"
```

3. Get coding! Follow the instructions below concerning Documentation, etc. for good coding practices. When you are ready to publish the code, proceed to step 4.
4. Bump the [version](https://semver.org/) appropriately in "my_package/__init__.py" and in the CITATION.cff file.
5. When finished FIRST go to [Zenodo](https://zenodo.org/) and enable preservation of this repo; THEN create a release on GitHub. Zenodo will automatically detect the new release and create a DOI and badge. Update the badge in this `README.md` file and in the `docs/index.rst` file with the new one generated by Zenodo.
6. Finally, follow [these instructions](https://packaging.python.org/en/latest/tutorials/packaging-projects/) to publish to PyPI.
7. Create a "dev" branch from main on which future development should occur (not main); when future releases are ready they can be merged into the main branch. This follows a [gitflow](https://nvie.com/posts/a-successful-git-branching-model/) management model instead of Google's ["live at head"](https://nehckl0.medium.com/googles-live-at-head-approach-and-release-management-4f175b723dae) philosophy.
3. Replace the placeholder `LICENSE.md` with the full text of your chosen license (see [choosealicense.com](https://choosealicense.com/)) and update the `license` field in `pyproject.toml` accordingly.
4. Get coding! Follow the best practices below. When you are ready to publish, proceed to the next step.
5. Bump the [version](https://semver.org/) in `my_package/__init__.py` and in `CITATION.cff`.
6. When finished, first enable preservation of this repo on [Zenodo](https://zenodo.org/), then create a release on GitHub. Zenodo will detect the new release and mint a DOI and badge. Update the `{github_id}` placeholder in this `README.md` and in `docs/index.rst` with the ID generated by Zenodo.
7. Follow [these instructions](https://packaging.python.org/en/latest/tutorials/packaging-projects/) to publish to PyPI.
8. Develop on a `dev` branch and merge into `main` for releases. This template assumes that pattern; adjust to taste.

Best Practices
===

Environment
---

Create a conda/mamba environment then create an editable install so you can easily test and modify things during development.
Create a conda/mamba environment, then install the package in editable mode so changes are picked up immediately during development.

```bash
mamba create -n awesome_env python=3.12
mamba activate awesome_env
cd my_awesome_new_package
pip install -e . # editable install
python -m ipykernel install --user --name=awesome_env # register Jupyter kernel
jupyter notebook --port 4321 # launch on localhost (default auth enabled)
```

~~~bash
$ mamba create -n awesome_env python=3.10
$ mamba activate awesome_env
$ cd my_awesome_new_package
$ pip install -e . # Local installation
$ python -m ipykernel install --user --name=awesome_env # Install kernel
$ jupyter notebook --port 4321 --ip='*' --NotebookApp.token='' --NotebookApp.password='' # Launch jupyter server
~~~
Note: don't disable Jupyter's token/password or bind it to `0.0.0.0` unless you understand the access implications. The default (localhost + token) is the safe choice.

Documentation
---

Documentation is stored in the `docs/` folder and is currently set up to use [sphinx](https://www.sphinx-doc.org/en/master/).

First create the `requirements.txt` that will be needed to build the documentation. This is done by running `pip-compile` on the `requirements.in` file.
Documentation lives in `docs/` and is built with [Sphinx](https://www.sphinx-doc.org/en/master/).

Be sure to add any additional requirements you add to the `pyproject.toml` file etc. to `requirements.in` also.
The doc build pulls Sphinx extensions from `docs/requirements.in`. Pin them with `pip-compile` whenever you change them:

~~~bash
$ cd docs
$ pip install Sphinx
$ pip install pip-tools
$ pip-compile requirements.in # Modify as needed before executing
~~~
```bash
cd docs
pip install pip-tools
pip-compile requirements.in # produces requirements.txt
```

Adjust the `docs/conf.py` as desired. Then run `docs/make_docs.sh` to setup the documentation initially. You can manually add and adjust later.
Tweak `docs/conf.py` as needed, then generate the API reference and HTML output:

~~~bash
$ bash make_docs.sh
~~~
```bash
cd docs
pip install -r requirements.txt
bash make_docs.sh
```

This command creates documentation for your package, but you still need to adjust the main landing page (`docs/index.rst`) manually. Here is a [primer](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) on reStructured text markup language.
Adjust the landing page in `docs/index.rst` manually — see the [reStructuredText primer](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html).

Go to [https://about.readthedocs.com/](https://about.readthedocs.com/) to link your repo to build and host the documentation automatically! The `.readthedocs.yml` file contains the configuration for this which you can adjust as needed.
For hosted docs, link the repo at [Read the Docs](https://about.readthedocs.com/). The `.readthedocs.yml` already installs both `docs/requirements.txt` and the package itself; adjust as needed.

Alternatively, you can start the documentation setup fresh using `sphinx-quickstart` in the `docs/` directory:

~~~bash
$ sphinx-quickstart
~~~
To start fresh, run `sphinx-quickstart` in `docs/`.

Unittests
---

Build [unittests](https://docs.python.org/3/library/unittest.html) in the `tests/` directory. The `pyproject.toml` automatically configures pytest to look in `tests/`. The following will run all unittests in this directory.
Add [unittests](https://docs.python.org/3/library/unittest.html) in `tests/`. `pyproject.toml` configures pytest to look there automatically.

```bash
python -m pytest
```

~~~bash
$ python -m pytest
~~~
`.github/workflows/python-app.yml` runs these tests and reports coverage on every push and pull request to `main`. Edit the `on:` triggers if you want the workflow to run on other branches too.

The GitHub workflow in `.github/workflows/python-app.yml` will also run these tests and perform coverage checks using this command. This workflow is triggered automatically on the main branch, but you can adjust this file so this is automatically triggered on others as well.
Code coverage is wired to [codecov.io](https://app.codecov.io/). Enable the repo there, add your `CODECOV_TOKEN` as a [GitHub Actions secret](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions), and update the badge URL in this `README.md` and in `docs/index.rst` (find it under *Configuration → Badges & Graphs* on codecov).

Code coverage is configured to work with [codecov.io](https://app.codecov.io/); visit their website to enable this for your new project. Add your codecov *repository* token (`CODECOV_TOKEN`) to this repo [as an "Actions" secret](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions). Then update the badge (click on the repo on codecov.io and navigate to Configuration > Badges & Graphs) in this `README.md` file and in the `docs/index.rst` file.
CI/CD
---

Three additional workflows keep the repo healthy with no extra setup:

- `.github/workflows/pre-commit.yml` runs the pre-commit hooks on every push and PR to `main`, so lint/format failures are caught in CI rather than only locally.
- `.github/workflows/codeql.yml` runs GitHub's [CodeQL](https://codeql.github.com/) static-analysis scanner on every push, every PR, and weekly on Mondays. Findings appear under the repo's *Security → Code scanning* tab.
- `.github/dependabot.yml` opens weekly PRs to update GitHub Actions versions and pip dependencies. Tune the `interval` or `open-pull-requests-limit` if the volume is too high.

Linting
Linting and Formatting
---

Automatic code linting is provided via [pre-commit](https://pre-commit.com/); refer to the `.pre-commit-config.yaml` file for the specific configuration which you can adjust as needed.
Linting and formatting are handled by [ruff](https://docs.astral.sh/ruff/) via [pre-commit](https://pre-commit.com/). See `.pre-commit-config.yaml` for the full hook configuration.

Install the hooks once, then they'll run on every commit:

```bash
pre-commit install
```

Run pre-commit to lint new code, then commit the changes.
To run all hooks against the whole repo on demand:

~~~bash
$ pre-commit run --all-files
~~~
```bash
pre-commit run --all-files
```

Typing
---

While optional, it is best to include [typing](https://docs.python.org/3/library/typing.html) in your code - see [mypy](https://mypy-lang.org/).
Type hints are optional but recommended — see the [typing docs](https://docs.python.org/3/library/typing.html) and [mypy](https://mypy-lang.org/). Mypy is not run by pre-commit in this template; invoke it manually as needed:

~~~bash
$ mypy --ignore-missing-imports my_new_file.py
~~~
```bash
mypy --ignore-missing-imports my_new_file.py
```

Demo
---

Consider building a [streamlit](https://streamlit.io/) demo to illustrate the capabilities of the tool. This can be hosted for free in the [community cloud](https://streamlit.io/cloud). Refer to their documentation for easy setup, which allows you to put your `app.py` file right here in a public repo, e.g., `streamlit/app.py`. You may also consider using a [HuggingFace space](https://huggingface.co/spaces) instead.
If your package benefits from a UI, consider a [Streamlit](https://streamlit.io/) demo (free hosting via the [community cloud](https://streamlit.io/cloud)) or a [Hugging Face Space](https://huggingface.co/spaces). Either works directly from a public repo.

Logo
---

You can generate a logo or other art using [Google Gemini](https://gemini.google.com/app) or other AI tools. [Note](https://lib.guides.umd.edu/c.php?g=1340355&p=9896961#:~:text=The%20Chicago%20Manual%20of%20Style's,prompt%20that%20generated%20the%20image.) that "the Chicago Manual of Style's website recommends you cite AI-generated images like any other image, while including both the name of the AI tool that generated the image, the company that created the AI, and the prompt that generated the image." Modify the `html_logo` tag in the `docs/conf.py` to point to the logo.
Generate a logo with [Google Gemini](https://gemini.google.com/app) or any other AI tool, save it under `docs/_static/`, and point the `html_logo` setting in `docs/conf.py` at the new file. If you use an AI-generated image, [cite it](https://lib.guides.umd.edu/c.php?g=1340355&p=9896961) — name the tool, the company, and the prompt.

The logo for this repository (`docs/_static/logo.png`) was generated using Google Gemini (Imagen 3) on Nov. 28, 2024 with the prompt "Make a logo inspired by code as a template for python projects."
The logo for this repository (`docs/_static/logo.png`) was generated with Google Gemini (Imagen 3) on Nov. 28, 2024 from the prompt: *"Make a logo inspired by code as a template for python projects."*

Citation
---

Update the CITATION.cff, CODEOWNERS, and pyproject.toml files to include all code authors and maintainers appropriately.
Update `CITATION.cff`, `CODEOWNERS`, and `pyproject.toml` to list all authors and maintainers. The CITATION format is documented at [citation-file-format.github.io](https://citation-file-format.github.io/).
Loading
Loading