diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/docs_build_and _deploy.yml b/.github/workflows/docs_build_and _deploy.yml new file mode 100644 index 0000000..528408c --- /dev/null +++ b/.github/workflows/docs_build_and _deploy.yml @@ -0,0 +1,46 @@ +name: Build and Deploy Sphinx Docs + +# Generate the documentation on all merges to main, all pull requests, or by +# manual workflow dispatch. The build job can be used as a CI check that the +# docs still build successfully. The deploy job only runs when a tag is +# pushed and actually moves the generated html to the gh-pages branch +# (which triggers a GitHub pages deployment). +on: + push: + branches: + - main + tags: + - '*' + pull_request: + merge_group: + workflow_dispatch: + +jobs: + linting: + # scheduled workflows should not run on forks + if: (${{ github.event_name == 'schedule' }} && ${{ github.repository_owner == 'neuroinformatics-unit' }} && ${{ github.ref == 'refs/heads/main' }}) || (${{ github.event_name != 'schedule' }}) + runs-on: ubuntu-latest + steps: + - uses: neuroinformatics-unit/actions/lint@v2 + + build_sphinx_docs: + name: Build Sphinx Docs + runs-on: ubuntu-latest + steps: + - uses: neuroinformatics-unit/actions/build_sphinx_docs@main + with: + python-version: 3.12 + use-make: true + + deploy_sphinx_docs: + name: Deploy Sphinx Docs + needs: build_sphinx_docs + permissions: + contents: write + if: (github.event_name == 'push' && github.ref_type == 'tag') || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - uses: neuroinformatics-unit/actions/deploy_sphinx_docs@main + with: + secret_input: ${{ secrets.GITHUB_TOKEN }} + use-make: true diff --git a/README.md b/README.md index db84e76..3bcc060 100644 --- a/README.md +++ b/README.md @@ -1,313 +1,53 @@ -# cookiecutter-python -A tool to automatically create a Python project structure ready to release via GitHub and [PyPI](https://pypi.org/). -It will also set up: -* A blank `README.md` file -* A `LICENSE` file -* [Pre-commit hooks](https://pre-commit.com/) to automate linting checks and formatting -* Automatic versioning using [setuptools_scm](https://github.com/pypa/setuptools_scm) -* A structure for automated tests using [pytest](https://docs.pytest.org/en/7.0.x/) -* Automated formatting checks, testing and release using [GitHub actions](https://github.com/features/actions) -* Documentation using [Sphinx](https://www.sphinx-doc.org/en/master/) +[![Project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://neuroinformatics.zulipchat.com/#narrow/channel/406003-Python-cookiecutter) +[![License](https://img.shields.io/badge/License-BSD_3--Clause-orange.svg)](https://opensource.org/licenses/BSD-3-Clause) +[![Code style: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/format.json)](https://github.com/astral-sh/ruff) +[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) -**Based on [cookiecutter-napari-plugin](https://github.com/napari/cookiecutter-napari-plugin)** +# python-cookiecutter -## Table of contents -- [Table of contents](#table-of-contents) -- [Set up](#set-up) - - [Installing Cookiecutter](#installing-cookiecutter) - - [Creating a Cookiecutter project](#creating-a-cookiecutter-project) - - [Make it a git repo](#make-it-a-git-repo) -- [Add your modules and tests](#add-your-modules-and-tests) - - [Add dependencies](#add-dependencies) - - [Write tests](#write-tests) -- [Before committing your changes](#before-committing-your-changes) - - [Run the tests](#run-the-tests) - - [Install your package locally](#install-your-package-locally) - - [Pre-commit hooks](#pre-commit-hooks) -- [Versioning](#versioning) - - [Automated versioning](#automated-versioning) -- [GitHub actions workflow](#github-actions-workflow) -- [Documentation](#documentation) - - [Building the documentation locally](#building-the-documentation-locally) - - [Publishing the documentation](#publishing-the-documentation) - - [Docstrings and API documentation](#docstrings-and-api-documentation) +A tool to automatically create a Python project structure ready to release via GitHub and [PyPI](https://pypi.org/). -## Set up +**python-cookiecutter** automatically creates a structured project and sets up essential +tools, including: -### Installing Cookiecutter +- A blank `README.md` file for documentation. +- A `LICENSE` file to define usage rights. +- [Pre-commit hooks](https://pre-commit.com/) to maintain code + quality. +- Automatic versioning with + [setuptools_scm](https://setuptools-scm.readthedocs.io/en/latest/). +- A test setup using [pytest](https://docs.pytest.org/en/7.0.x/). +- Automated formatting, testing, and publishing via [GitHub + Actions](https://github.com/features/actions). +- A documentation setup with + [Sphinx](https://www.sphinx-doc.org/en/master/). -First, install cookiecutter in your desired environment. Running in the terminal in your environment, with Pip: +## Quick Install -`pip install cookiecutter` +First, install cookiecutter in your desired environment. Running in the terminal in your environment, with Pip: -or conda: +```sh +pip install cookiecutter -`conda install -c conda-forge cookiecutter` +# or conda: +conda install -c conda-forge cookiecutter +``` ### Creating a Cookiecutter project In the folder, you want to create the repo run: -```bash -cookiecutter https://github.com/neuroinformatics-unit/python-cookiecutter -``` - -You will be then asked a series of questions about how you want to set up your project. - -For each one, type your answer, enter a single number (or just hit return) to choose from a default option. - -* `full_name [Python developer]:` - e.g. `Adam Tyson` -* `email [yourname@example.com]:` - e.g. `cookiecutter@adamltyson.com` -* `github_username_or_organization [githubuser]:` - e.g. `adamltyson` -* `package_name [python-package]:` - e.g. `my-awesome-software` -* `Select github_repository_url:` - Default will be e.g. `https://github.com/adamltyson/my-awesome-software`, but you can also provide this later. -* `module_name [my_awesome_software]:` - The default will be the same as `package_name` but with hyphens converted to underscores. -* `short_description [A simple Python package]:` - Enter a simple, one-line description of your Python package. -* `Select license:` - choose from: - - `1 - BSD-3` - - `2 - MIT` - - `3 - Mozilla Public License 2.0` - - `4 - Apache Software License 2.0` - - `5 - GNU LGPL v3.0` - - `6 - GNU GPL v3.0` -* `Select create_docs:` - Whether to generate documentation using [Sphinx](https://www.sphinx-doc.org/en/master/), choose from: - - `1 - Yes` - - `2 - No` - -This is the structure cookiecutter will create: -``` -└── my-awesome-software/ - ├── LICENSE - ├── MANIFEST.in - ├── README.md - ├── pyproject.toml - ├── tox.ini - ├── my_awesome_software/ - │ └── __init__.py - └── tests/ - ├── __init__.py - ├── test_integration/ - │ └── __init__.py - └── test_unit/ - ├── __init__.py - └── test_placeholder.py -``` -A project with this information will then be written to the current working directory. - -If you respond positively to `Select create_docs:`, an additional `docs` folder will be created and two example Python modules (`math.py` and `greetings.py`) will be added to the above structure. -``` -└── my-awesome-software/ - └── docs/ - ├── make.bat - ├── Makefile - ├── requirements.txt - └── source/ - ├── api_index.rst - ├── conf.py - ├── getting_started.md - └── index.rst - └── my_awesome_software/ - ├── __init__.py - ├── greetings.py - └── math.py -``` - -### Make it a git repo - -Although it asks for a GitHub username or organization and package name, it does not initialize a git repository. - -To do so navigate to your project folder: -```bash -cd my-awesome-software -``` -and run: -```shell -git init -b main -``` - -**N.B. If you have an older version of Git (.github.io//`. To enable hosting, you will need to go to the settings of your repository, and under the "Pages" section, select the `gh-pages` branch as the source for your GitHub pages site. -* A popular alternative to GitHub pages for hosting the documentation is [Read the Docs](https://readthedocs.org/). To enable hosting on Read the Docs, you will need to create an account on the website and follow the instructions to link your GitHub repository to your Read the Docs account. +## Contributing -### Docstrings and API documentation -The journey towards good documentation starts with writing docstrings for all functions in your module code. In the example `math.py` and `greetings.py` modules you will find some docstrings that you can use as a template. We have written the example docstrings following the [numpy style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html) but you may also choose another widely used style, such as the [Google style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html). +We welcome contributions! See our [Contribution Guidelines](python-cookiecutter.neuroinformatics.dev/contributing) for workflow details. -Once you have written docstrings for all your functions, API documentation can be automatically generated via the [Sphinx autodoc extension](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html). We have given examples of how to do this in the `docs/source/api_index.rst` file. +## License +⚖️ [BSD 3-Clause](./LICENSE) diff --git a/docs/CNAME b/docs/CNAME index db786b8..03c5e00 100644 --- a/docs/CNAME +++ b/docs/CNAME @@ -1 +1 @@ -python-cookiecutter.neuroinformatics.dev \ No newline at end of file +python-cookiecutter.neuroinformatics.dev diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..dc1312a --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..1577543 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,13 @@ +linkify-it-py +myst-parser +nbsphinx +pydata-sphinx-theme +setuptools-scm +sphinx +sphinx-autodoc-typehints +sphinx-copybutton +sphinx-design +sphinx-gallery +sphinx-notfound-page +sphinx-sitemap +sphinx-togglebutton diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css new file mode 100644 index 0000000..2442c4d --- /dev/null +++ b/docs/source/_static/css/custom.css @@ -0,0 +1,64 @@ +html[data-theme=dark] { + --pst-color-primary: #04B46D; + --pst-color-link: var(--pst-color-primary); + } + +html[data-theme=light] { + --pst-color-primary: #03A062; + --pst-color-link: var(--pst-color-primary); +} + +body .bd-article-container { +max-width: 100em !important; +} + +.col { +flex: 0 0 50%; +max-width: 50%; +} + +.img-sponsor { +height: 50px; +padding-top: 5px; +padding-right: 5px; +padding-bottom: 5px; +padding-left: 5px; +} + +.things-in-a-row { +display: flex; +flex-wrap: wrap; +justify-content: space-between; +} + +/* grids to match theme colors */ +.sd-card-icon { + color: var(--sd-color-primary); + font-size: 1.5em; + margin-bottom: 0.5rem; +} + +.sd-card { + padding: 1.5rem; + transition: transform 0.2s; +} + +.sd-card:hover { + transform: translateY(-5px); +} + +/* Ensuring content area uses full width when sidebar is hidden */ +.bd-page-width { + max-width: 90% !important; +} + +/* Hide sidebar on pages with hide-sidebar metadata */ +body[data-hide-sidebar="true"] .bd-sidebar-primary { + display: none !important; +} + +/* Expand content width when sidebar hidden */ +body[data-hide-sidebar="true"] .bd-main { + flex-grow: 1; + max-width: 75%; +} diff --git a/docs/source/_static/dark-logo-niu.png b/docs/source/_static/dark-logo-niu.png new file mode 100644 index 0000000..324b1bc Binary files /dev/null and b/docs/source/_static/dark-logo-niu.png differ diff --git a/docs/source/_static/favicon.ico b/docs/source/_static/favicon.ico new file mode 100644 index 0000000..324b1bc Binary files /dev/null and b/docs/source/_static/favicon.ico differ diff --git a/docs/source/_static/light-logo-niu.png b/docs/source/_static/light-logo-niu.png new file mode 100644 index 0000000..efba940 Binary files /dev/null and b/docs/source/_static/light-logo-niu.png differ diff --git a/docs/source/_templates/footer_end.html b/docs/source/_templates/footer_end.html new file mode 100644 index 0000000..bc74856 --- /dev/null +++ b/docs/source/_templates/footer_end.html @@ -0,0 +1,7 @@ +
+ + Sponsors + Sponsors + +

python-cookiecutter is based on cookiecutter-napari-plugin.

+
diff --git a/docs/source/_templates/footer_start.html b/docs/source/_templates/footer_start.html new file mode 100644 index 0000000..f1592be --- /dev/null +++ b/docs/source/_templates/footer_start.html @@ -0,0 +1,9 @@ +

+ {% trans sphinx_version=sphinx_version|e %}Created using Sphinx {{ sphinx_version }}.{% endtrans %} +
+

+

+ {{ _("Built with the") }} + PyData Sphinx Theme + {{ theme_version }}. +

diff --git a/docs/source/_templates/sidebar-nav.html b/docs/source/_templates/sidebar-nav.html new file mode 100644 index 0000000..cfa575c --- /dev/null +++ b/docs/source/_templates/sidebar-nav.html @@ -0,0 +1,7 @@ + diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..24fe6a6 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,142 @@ +# conf.py +# Configuration file for the Sphinx documentation builder. +import setuptools_scm + + +project = "python-cookiecutter" +copyright = "2025, University College London" +author = "Neuroinformatics Unit" +try: + full_version = setuptools_scm.get_version(root="../..", relative_to=__file__) + # Splitting the release on '+' to remove the commit hash + release = full_version.split('+', 1)[0] +except LookupError: + # if git is not initialised, still allow local build + # with a dummy version + release = "0.0.0" + +# -- General configuration --------------------------------------------------- +extensions = [ + "myst_parser", + "sphinx_design", + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.githubpages", + "sphinx_autodoc_typehints", + "sphinx.ext.autosummary", + "sphinx.ext.viewcode", + "sphinx.ext.intersphinx", + "sphinx_sitemap", + "nbsphinx", +] + +# Configure MyST Parser +myst_enable_extensions = [ + "colon_fence", + "deflist", + "fieldlist", + "html_admonition", + "html_image", + "linkify", + "replacements", + "smartquotes", + "substitution", + "tasklist", + "attrs_inline" +] + +myst_heading_anchors = 4 + +source_suffix = { + '.md': 'markdown', + '.rst': 'restructuredtext' +} + +templates_path = ['_templates'] +exclude_patterns = [ + "**.ipynb_checkpoints", + "**/includes/**", +] + +# -- HTML output options ----------------------------------------------------- +html_theme = 'pydata_sphinx_theme' +html_logo = "_static/dark-logo-niu.png" +html_static_path = ["_static"] +html_title = "Python Cookiecutter" +html_favicon = "_static/favicon.ico" + + +html_theme_options = { + "navbar_start": ["navbar-logo"], + "navbar_center": ["navbar-nav"], + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/neuroinformatics-unit/python-cookiecutter", + "icon": "fa-brands fa-github", + "type": "fontawesome", + }, + { + "name": "Zulip (chat)", + "url": "https://neuroinformatics.zulipchat.com/#narrow/channel/406003-Python-cookiecutter", + "icon": "fa-solid fa-comments", + "type": "fontawesome", + }, + ], + "logo": { + "text": f"{project}", + }, + "footer_start": ["footer_start"], + "footer_end": ["footer_end"], + "external_links": [], +} + +# Sitemap configuration +github_user = "neuroinformatics-unit" +html_baseurl = "https://python-cookiecutter.neuroinformatics.dev" +sitemap_url_scheme = "{link}" + + +# -- HTML sidebar configuration --------------------------------------------- +html_sidebars = { + # Apply sidebar to ALL pages except index + "**": ["sidebar-nav.html"], + "index": [] +} +html_show_sourcelink = False + + +def setup(app): + app.add_css_file("css/custom.css") + + +# What to show on the 404 page +notfound_context = { + "title": "Page Not Found", + "body": """ +

Page Not Found

+ +

Sorry, we couldn't find that page.

+ +

We occasionally restructure the python-cookiecutter website, and some links may have broken.

+ +

Try using the search box or go to the homepage.

+""", +} + +# needed for GH pages (vs readthedocs), +# because we have no '///' in the URL +notfound_urls_prefix = None + +# The linkcheck builder will skip verifying that anchors exist when checking +# these URLs +linkcheck_anchors_ignore_for_url = [ + "https://neuroinformatics.zulipchat.com/", + "https://neuroinformatics.zulipchat.com/#narrow/channel/406003-Python-cookiecutter", + "https://github.com/pypa/setuptools_scm#default-versioning-scheme", +] +# A list of regular expressions that match URIs that should not be checked +linkcheck_ignore = [ + "https://github.com/", + "https://opensource.org/license/bsd-3-clause/", # to avoid odd 403 error +] diff --git a/docs/source/contributing.md b/docs/source/contributing.md new file mode 100644 index 0000000..b131a6a --- /dev/null +++ b/docs/source/contributing.md @@ -0,0 +1,82 @@ +# Contributing + +Thank you for considering contributing to python-cookiecuttter! We welcome contributions that improve the project, whether it's code, +documentation, or anything else. + +:::{note} +To discuss contributing, you can join the [Zulip Channel](https://neuroinformatics.zulipchat.com/#narrow/channel/406003-Python-cookiecutter) here. +::: + +Please follow the guidelines below to ensure a smooth process. + +1. **Fork and Clone the Repository** Fork the main repository on + GitHub, then clone your fork locally: + + ``` sh + git clone https://github.com//python-cookiecutter.git + cd python-cookiecutter + ``` + +2. **Create a New Branch** Create and switch to a new branch for your + changes: + + ``` sh + git checkout -b my_new_feature + ``` + +3. **Make Your Changes** Edit the content, code, or + documentation as needed. When you're ready, add and commit your + changes with a descriptive message: + + ``` sh + git add . + git commit -m "Describe your changes in short here" + ``` + +4. **Push Your Branch and Create a Pull Request** Push the new branch + to GitHub: + + ``` sh + git push origin --set-upstream origin my_new_feature + ``` + + Then, open a pull request against the ``main`` branch of the main repository. This will automatically trigger a GitHub Action to verify that the builds correctly. + +5. **Review and Merge** If the build checks pass, assign someone to review your pull request. Once approved and merged into the ``main`` branch, another GitHub Action will build and add your changes to the ``main`` branch. + + +## Documentation Local Testing + +If you are contributing to the Documentation, Before pushing your changes, you can test locally: + +- **First-Time Setup:** Install the required dependencies and build + the docs: + + ``` sh + pip install -r docs/requirements.txt + sphinx-build docs/source docs/build + ``` + +Alternatively, you can use the following commands to install the +dependencies and build the docs: + + ``` sh +pip install -r docs/requirements.txt +cd docs +make html +``` + +- **Rebuilding:** Each time you update the documentation, + rebuild the docs by running: + + ``` sh + rm -rf docs/build && sphinx-build docs/source docs/build + ``` + or + + ```sh + cd docs + make clean && make html + ``` + + Then, open `docs/build/index.html` in your browser to preview the changes. diff --git a/docs/source/index.md b/docs/source/index.md new file mode 100644 index 0000000..2b50b5c --- /dev/null +++ b/docs/source/index.md @@ -0,0 +1,66 @@ +--- +hide-sidebar: true +--- + +# python-cookiecutter + +A tool to automatically create a Python project structure ready to +release via GitHub and [PyPI](https://pypi.org/). + +## Getting Started + +**Python-cookiecutter** automatically creates a structured project and sets up essential +tools, including: + +- A blank `README.md` file for documentation. +- A `LICENSE` file to define usage rights. +- [Pre-commit hooks](https://pre-commit.com/) to maintain code + quality. +- Automatic versioning with + [setuptools_scm](https://setuptools-scm.readthedocs.io/en/latest/). +- A test setup using [pytest](https://docs.pytest.org/en/7.0.x/). +- Automated formatting, testing, and publishing via [GitHub + Actions](https://github.com/features/actions). +- A documentation setup with + [Sphinx](https://www.sphinx-doc.org/en/master/). + + +::::{grid} 1 2 2 3 +:gutter: 3 +:class-container: sd-p-3 + +:::{grid-item-card} {fas}`tools;sd-text-primary` Project Setup +:link: project_setup +:link-type: doc +:text-align: center + +Installation and Setup +::: + +:::{grid-item-card} {fas}`book-open;sd-text-primary` Infrastructure +:link: infrastructure +:link-type: doc +:text-align: center + +Pre-commit hooks, Versioning, GitHub Actions & Documentation +::: + +:::{grid-item-card} {fas}`handshake-angle;sd-text-primary` Contributing +:link: contributing +:link-type: doc +:text-align: center + +How to improve the cookiecutter +::: +:::: + + +```{toctree} +:maxdepth: 2 +:caption: Documentation +:hidden: + +project_setup +infrastructure +contributing +``` diff --git a/docs/source/infrastructure.md b/docs/source/infrastructure.md new file mode 100644 index 0000000..6e3b8f6 --- /dev/null +++ b/docs/source/infrastructure.md @@ -0,0 +1,155 @@ +# Infrastructure + +## Tests + +Write your test methods and classes in the `test` folder. We are using [pytest](https://docs.pytest.org/en/7.2.x/getting-started.html). +In your test module you can call your methods in a simple way: + +```python +# filename: test_math.py +from my_awesome_software import math + +# here your test function +``` + +If you're testing a small piece of code, make it a unit test. If you want to test whether two or more software units work well together, create an integration test. + +:::{important} +Before committing your changes +::: + +### Run the tests + +Be sure that you have installed pytest and run it +```bash +pip install pytest +pytest +``` +You should also see coverage information. + +### Install your package locally + +For a local, editable install, in the project directory, run: +```bash +pip install -e . +``` + +For a local, editable install with all the development tools (e.g. testing, formatting etc.) run: +```bash +pip install -e '.[dev]' +``` + +You might want to install your package in an _ad hoc_ environment. + +To test if the installation works, try to call your modules with python in another folder from the same environment. +```python +from my_awesome_sofware.math import add_two_integers +add_two_integers(1, 2) +``` + +# Pre-commit hooks + +Running `pre-commit install` will set up [pre-commit hooks](https://pre-commit.com/) to ensure the code is +formatted correctly. Currently, these are: +* [ruff](https://github.com/charliermarsh/ruff) does a number of jobs, including linting, auto-formatting code (with `ruff-format`), and sorting import statements. +* [mypy](https://mypy.readthedocs.io/en/stable/index.html) a static type checker +* [check-manifest](https://github.com/mgedmin/check-manifest) to ensure that the right files are included in the pip package. +* [codespell](https://github.com/codespell-project/codespell) to check for common misspellings. + + +These will prevent code from being committed if any of these hooks fail. To run them individually: +```sh +ruff check --fix # Lint all files in the current directory, and fix any fixable errors. +ruff format # Format all files in the current directory. +mypy -p my_awesome_software +check-manifest +codespell +``` + +You can also execute all the hooks using +```sh +pre-commit run +``` +or +```sh +pre-commit run --all-files +``` + + The best time to run this is after you have staged your changes, but before you commit them. + +In the case you see `mypy` failing with an error like `Library stubs not installed for this-package`, you do have to edit the `.pre-commit-config.yaml` file by adding the additional dependency to `mypy`: +``` sh +- id: mypy + additional_dependencies: + - types-setuptools + - types-this-package +``` + +# Versioning + +We recommend the use of [semantic versioning](https://semver.org/), which uses a `MAJOR`.`MINOR`.`PATCH` versiong number where these mean: + +* PATCH = small bugfix +* MINOR = new feature +* MAJOR = breaking change + +## Automated versioning +[`setuptools_scm`](https://github.com/pypa/setuptools_scm) can be used to automatically version your package. It has been pre-configured in the `pyproject.toml` file. [`setuptools_scm` will automatically infer the version using git](https://github.com/pypa/setuptools_scm#default-versioning-scheme). To manually set a new semantic version, create a tag and make sure the tag is pushed to GitHub. Make sure you commit any changes you wish to be included in this version. E.g. to bump the version to `1.0.0`: + +```sh +git add . +git commit -m "Add new changes" +git tag -a v1.0.0 -m "Bump to version 1.0.0" +git push --follow-tags +``` +:::{tip} +It is also possible to perform this step by using the [GitHub web interface or CLI](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository). +::: + +# GitHub actions workflow + +A GitHub actions workflow (`.github/workflows/test_and_deploy.yml`) has been set up to run (on each commit/PR): +* Linting checks (pre-commit). +* Testing (only if linting checks pass) +* Release to PyPI (only if a git tag is present and if tests pass). Requires `TWINE_API_KEY` from PyPI to be set in repository secrets. + +This automation ensures that each commit or pull request is validated and that releases are published only when all checks pass. + +# Documentation + +Software documentation is important for effectively communicating how to use the software to others as well as to your future self. + +If you want to include documentation in your package, make sure to respond with `1 - Yes` when prompted during the `cookiecutter` setup. This will instantiate a `docs` folder with a skeleton documentation system, that you can build upon. + +The documentation source files are located in the `docs/source` folder and should be written in either [reStructuredText](https://docutils.sourceforge.io/rst.html) or [markdown](https://myst-parser.readthedocs.io/en/stable/syntax/typography.html). The `index.rst` file corresponds to the main page of the documentation website. Other `.rst` or `.md` files can be included in the main page via the `toctree` directive. + +The documentation is built using [Sphinx](https://www.sphinx-doc.org/en/master/) and the [PyData Sphinx Theme](https://pydata-sphinx-theme.readthedocs.io/en/latest/). The `docs/source/conf.py` file contains the `Sphinx` configuration. + +## Building the documentation locally +You can build and view the documentation website locally, on your machine. To do so, run the following commands from the root of your project: + +```sh +# Install the documentation build dependencies +pip install -r docs/requirements.txt +# Build the documentation +sphinx-build docs/source docs/build +``` +This should create a `docs/build` folder. You can view the local build by opening `docs/build/index.html` in a browser. +To refresh the documentation, after making changes, remove the `docs/build` folder and re-run the above command: + +```sh +rm -rf docs/build +sphinx-build docs/source docs/build +``` + +## Publishing the documentation +We have included an extra GitHub actions workflow in `.github/workflows/docs_build_and_deploy.yml` that will build the documentation and deploy it to [GitHub pages](https://pages.github.com/). +* The build step is triggered every time a pull request is opened or a push is made to the `main` branch. This way you can make sure that the documentation does not break before merging your changes. +* The deployment is triggered only when a tag is present (see [Automated versioning](#automated-versioning)). This ensures that new documentation versions are published in tandem with the release of a new package version on PyPI (see [GitHub actions workflow](#github-actions-workflow)). +* The published docs are by default hosted at `https://.github.io//`. To enable hosting, you will need to go to the settings of your repository, and under the "Pages" section, select the `gh-pages` branch as the source for your GitHub pages site. +* A popular alternative to GitHub pages for hosting the documentation is [Read the Docs](https://readthedocs.org/). To enable hosting on Read the Docs, you will need to create an account on the website and follow the instructions to link your GitHub repository to your Read the Docs account. + +## Docstrings and API documentation +The journey towards good documentation starts with writing docstrings for all functions in your module code. In the example `math.py` and `greetings.py` modules you will find some docstrings that you can use as a template. We have written the example docstrings following the [numpy style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html) but you may also choose another widely used style, such as the [Google style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html). + +Once you have written docstrings for all your functions, API documentation can be automatically generated via the [Sphinx autodoc extension](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html). We have given examples of how to do this in the `docs/source/api_index.rst` file. diff --git a/docs/source/project_setup.md b/docs/source/project_setup.md new file mode 100644 index 0000000..09f16c5 --- /dev/null +++ b/docs/source/project_setup.md @@ -0,0 +1,206 @@ +# Project Setup + +This section describes how to set up your project using cookiecutter. + +## Installation & Setup + +First, install cookiecutter in your desired environment. Running in the terminal in your environment: + +:::{note} +You must have Python installed to use Cookiecutter +::: + +using pip or conda: + +::::{tab-set} +:::{tab-item} pip +Install using pip +```sh +pip install cookiecutter +``` +::: +:::{tab-item} conda +Install using conda +```sh +conda install -c conda-forge cookiecutter +``` +::: +:::: + + +## Creating a Cookiecutter Project + +In the folder or directory, you want to create the project: + + +Then Run the following command: + +```sh +cookiecutter https://github.com/neuroinformatics-unit/python-cookiecutter +``` + +You will be then asked a series of questions about how you want to set up your project. + +For each one, type your answer, enter a single number (or just hit return) to choose from a default option. + +such as: + + +* `full_name [Python developer]:` - e.g. `Adam Tyson` +* `email [yourname@example.com]:` - e.g. `cookiecutter@adamltyson.com` +* `github_username_or_organization [githubuser]:` - e.g. `adamltyson` +* `package_name [python-package]:` - e.g. `my-awesome-software` +* `Select github_repository_url:` - Default will be e.g. `https://github.com//my-awesome-software`, but you can also provide this later. +* `module_name [my_awesome_software]:` - The default will be the same as `package_name` but with hyphens converted to underscores. +* `short_description [A simple Python package]:` - Enter a simple, one-line description of your Python package. +* `Select license:` - choose from: + - `1 - BSD-3` + - `2 - MIT` + - `3 - Mozilla Public License 2.0` + - `4 - Apache Software License 2.0` + - `5 - GNU LGPL v3.0` + - `6 - GNU GPL v3.0` +* `Select create_docs:` - Whether to generate documentation using [Sphinx](https://www.sphinx-doc.org/en/master/), choose from: + - `1 - Yes` + - `2 - No` + +This is the structure cookiecutter will create: +``` +└── my-awesome-software/ + ├── LICENSE + ├── MANIFEST.in + ├── README.md + ├── pyproject.toml + ├── tox.ini + ├── my_awesome_software/ + │ └── __init__.py + └── tests/ + ├── __init__.py + ├── test_integration/ + │ └── __init__.py + └── test_unit/ + ├── __init__.py + └── test_placeholder.py +``` + +A project with this information will then be written to the current working directory. + +:::{important} +If you respond positively to `Select create_docs:`, an additional `docs` folder will be created and two example Python modules (`math.py` and `greetings.py`) will be added to the above structure. +::: + +``` +└── my-awesome-software/ + └── docs/ + ├── make.bat + ├── Makefile + ├── requirements.txt + └── source/ + ├── api_index.rst + ├── conf.py + ├── getting_started.md + └── index.rst + └── my_awesome_software/ + ├── __init__.py + ├── greetings.py + └── math.py +``` + +## Creating a GitHub Repository + +1. **Sign In to GitHub:** Visit [GitHub](https://github.com) and sign in with your account. + + +2. **Create a New Repository:** + + - Click on the **+** icon in the upper-right corner of the page and select **New repository**. + - Alternatively, you can navigate directly to: [New Repository](https://github.com/new) + +3. **Fill in Repository Details:** + + - **Repository Name:** Enter a name for your project (e.g: ``my-awesome-software``). + - **Description:** Optionally, provide a short description of your project. + - **Repository Visibility:** Choose between **Public** or **Private**. + + - **Initialize Repository:** You may leave the repository empty (without a README, .gitignore, or license) if you plan to push your existing local project. If you prefer, you can initialize it with a README file. + +:::{warning} +If you initialize with a README, you will need to pull those changes before pushing your local repository. +::: + +4. **Create the Repository:** Click the **Create repository** button. GitHub will then create your new repository and provide you + with the repository URL (e.g.``https://github.com/your-username/my-awesome-software.git``). + +## Initializing a Git Repository + +:::{note} +When creating a cookiecutter project, it asks for a GitHub username or organization and package name. However this does not initialize a git repository. +::: + +Navigate to your project folder and initialize git: + +```sh +cd my-awesome-software +``` +```sh +git init -b main +``` + +If you're using an older Git version (``<2.28``), use: + +```sh +git init +``` +```sh +git checkout -b main +``` + +Then, add and commit your changes: + +```sh +git add . +``` +```sh +git commit -m "Initial commit" +``` + +Finally, add the remote origin and push to GitHub: + +```sh +git remote add origin https://github.com//my-awesome-software.git +``` +```sh +git push --set-upstream origin main +``` +That\'s it! Your project is now set up and ready to go. 🚀 + +# Modules + +Your methods and classes would live inside the folder `my_awesome_software`. Split the functionalities into modules, and save them as `.py` files, e.g.: +``` +my_awesome_software + ├── __init__.py + ├── greetings.py + └── math.py +``` + +If you want to import methods and classes from one module to another you can use the dot: +```python +# filename: greetings.py +from .math import subtract_two_integers +``` + +If you want to import all the modules when installing you can add the following to your `__init__.py`: +```python +from . import * +``` + +## Add dependencies +To ensure any dependencies are installed at the same time as installing +your package, add them to your `pyproject.toml` file. E.g. to add `numpy` +and `pandas` as dependencies, add them to the `dependencies = []` list under +the `[project]` heading: + +```toml +dependencies = ["numpy", "pandas"] +```