Skip to content
Snippets Groups Projects
Commit 9fb1f9cb authored by Leštáková, Michaela's avatar Leštáková, Michaela
Browse files

Merge branch 'update-to-fst-version' into 'main'

updated template to match the FST version: linting with Ruff, instructions for...

See merge request !1
parents 6b423b55 365b947c
Branches main
No related tags found
1 merge request!1updated template to match the FST version: linting with Ruff, instructions for...
Pipeline #1752426 passed
......@@ -5,3 +5,14 @@
!.gitignore
!requirements.txt
!tests/
!tests/*.py
!docs/
!docs/*.rst
!docs/*.sh
!python_project_template/
!python_project_template/*.py
!README.md
!WORKFLOW.md
!SUPERVISOR_CHECKLIST.md
!pyproject.toml
!.gitlab-ci.yml
......@@ -38,11 +38,19 @@ lint:
#- pip install flake8 # you can also use tox
- pwd
- ls -lh
- flake8 . --filename=*.py --exclude=venv/ --max-line-length=80
# unit-test:
# tags:
# - env:docker
- ruff check
# pages:
# stage: deploy
# script:
# - pytest
# - sphinx-build -b html docs/ public
# artifacts:
# paths:
# - public
# rules:
# - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
unit-test:
tags:
- env:docker
script:
- pytest
......@@ -2,6 +2,7 @@
"recommendations": [
"ms-python.python",
"ms-python.vscode-pylance",
"charliermarsh.ruff",
"njpwerner.autodocstring",
"naumovs.color-highlight",
"ybaumes.highlight-trailing-white-spaces"
......
......
{
"python.formatting.provider": "black",
"python.sortImports.args": [
"--profile=black"
],
"python.linting.enabled": true,
"python.linting.lintOnSave": true,
"python.linting.flake8Enabled": true,
"python.linting.flake8Args": [
"--filename=*.py",
"--exclude=env/",
"--max-line-length=80",
"--docstring-convention=google"
],
"[python]": {
"editor.rulers": [80],
"editor.renderWhitespace": "none",
"editor.formatOnSave": false,
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.rulers": [
88
],
"editor.renderWhitespace": "trailing",
"editor.codeActionsOnSave": {
"source.organizeImports": true
"source.organizeImports.ruff": "explicit"
}
},
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"autoDocstring.docstringFormat": "google",
"files.exclude": {
"**/__pycache__": true
},
}
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
[[_TOC_]]
## Common Infrastructure
## Prerequisities
### Install git
Install git on your operating system following [these instructions](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
......@@ -13,19 +13,21 @@ After the installation is finished, you can install extensions of your choice. T
Note that there are hundreds of other extensions that can support you in various tasks - it is worth looking for tips and tricks online. We leave this up to you and name only the most basic ones for the start.
## Getting Started
### Clone this repository wherever you want to have it
Access the folder/directory of your choice and clone (make a local copy) of this repository on your machine by running
:warning: ADJUST THIS LINK
```bash
git clone https://git.rwth-aachen.de/fst-tuda/projects/emergencity/project-name.git
git clone https://git.rwth-aachen.de/fst-tuda/projects/project-name.git
```
If the authentication fails, you might need to add the ssh key beforehands - this will be the case if you want to access GitLab from a new machine.
### Create a virtual environment to get the required packages
On Windows, run
#### Microsoft Windows
On Windows, open VS Code and open a terminal. In the terminal, run
```cmd
py -m venv env
......@@ -43,7 +45,10 @@ Before you can start installing or using packages in your virtual environment yo
You can confirm you’re in the virtual environment by checking the location of your Python interpreter:
```cmd
# for cmd run
where python
# for powershell run
where.exe python
```
Tell pip to install all of the packages in the `requirements.txt` file using the -r flag:
......@@ -55,30 +60,94 @@ Update the `requirements.txt` file when you install new packages.
For more detailed instructions, check https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/.
Note for jupyter notebooks: it is possible to open and run Jupyter notebooks in VS Code. To use the created virtual environment `env` , you will need to select it as the kernel. VS Code has currently trouble finding newly created virtual environments. To solve this issue, open Command Palette in VS Code and execute `Developer: Reload Window`. After that, the `env` should be listed in the list of kernels.
#### GNU/Linux and Apple macOS
On Linux distributions (Ubuntu, Raspberry Pi OS, etc.), run
```cmd
python3 -m venv env
```
To create the virtual environment.
```cmd
source ./env/bin/activate
```
To activate a virtual environment.
```cmd
which python3
```
To check the location of your Python interpreter.
```cmd
python3 -m pip install -r requirements.txt
```
To install the packages in the `requirements.txt`.
### Code Structure
The source code (all modules that contain your classes and functions) is located in the directory `python-project-template` (the name of this project), i.e. we are following a flat layout (see https://ta_alex.pages.rwth-aachen.de/action-items/action-items/55_software_packaging_python/#project-setup). This directory is tracked by git.
For using the source code, it is recommended to reate a script in the root directory, for example `main.py` or a Jupyter notebook, e.g. `analysis.ipynb`. This script will in most cases not have to be tracked.
### Generating documentation
This project contains documentation that can be built using Sphinx. The `.rst` files in the `docs` directory contain developer-defined documentation. The `index.rst` file is the landing page that contains the table of contents. More `.rst` files can be added to explain the code structure, logic or give a minimal example.
The `conf.py` file contains settings used by Sphinx. Among other things, it contains settings to automatically generate API documentation from in-code docstrings.
You can generate the html files by executing the following command from within the pip env:
```cmd
cd docs
make html
```
The html documentation can be viewed in your browser by opening `docs/_build/html/index.html`.
# Writing Good Code
## Language Rules
### Linting
This project uses the `flake8` linter. Linting is a static code analysis for finding programming errors, bugs, stylistic errors and suspicious constructs that do not conform with a standard - in case of flake8, with the standard Pep8.
This project uses the `flake8` linter. Linting is a static code analysis for finding programming errors, bugs, stylistic errors and suspicious constructs that do not conform with a standard - in case of flake8, with the standard [PEP8](https://peps.python.org/pep-0008/).
`flake8` will inform you about pep8 errors upon saving directly in the editor by underlining the relevant parts of code.
### Autoformatting
This project uses the `black` autoformatter and formats your code on save. Autoformatted code will look the same regardless of who wrote it and regardless of the project, so that you can focus more on the content.
### Type Checking
This project uses the type checker `mypy` to run a static type checking analysis.
:warning: Provide type checking information wherever relevant, i.e. in all functions and classes.
## Style Rules
### Documentation
Documentation is an essential part of writing code.
:warning: All public functions, methods and classes must be properly documented with docstrings.
:warning: All public functions, methods, classes and modules must be properly documented with docstrings.
Use autogenerated **google** style docstrings.
To generate a docstring, right-click on a class, function, method or module and click on `Generate Docstring`.
This will generate a `google`-style docstring template that you have to fill out. An example for a good docstring:
```python
def find_largest_distance(point, polygon):
"""Finds the largest distance between a point and the edges of a polygon.
Args:
point (shapely.geometry.Point): shapely point object
polygon (shapely.geometry.Polygon): shapely polygon object
Returns:
float: the largest distance between a point and the edges of a polygon
"""
distance_list = np.array([])
for poly_point in list(zip(*polygon.exterior.coords.xy)):
distance = point.distance(Point(poly_point))
distance_list = np.append(distance_list, distance)
max_distance = max(distance_list)
return max_distance
```
because:
- [x] short and easy to understand description
- [x] starts with a verb in third person
- [x] `type` of the args are given
- [x] args and returns are described sufficiently
Where necessary, add additional information using comments.
......@@ -96,14 +165,14 @@ Follow [Guido](https://en.wikipedia.org/wiki/Guido_van_Rossum)'s recommendations
<tr>
<td>Packages</td>
<td><code>lower_with_under</code></td>
<td><code>shortlower</code></td>
<td></td>
</tr>
<tr>
<td>Modules</td>
<td><code>lower_with_under</code></td>
<td><code>_lower_with_under</code></td>
<td><code>short_lower</code></td>
<td><code>_module_in_c</code></td>
</tr>
<tr>
......@@ -162,15 +231,29 @@ Follow [Guido](https://en.wikipedia.org/wiki/Guido_van_Rossum)'s recommendations
</table>
For better readability, use meaningful names instead of hard-to-understand short names.
For better readability, use meaningful, expressive names instead of hard-to-understand short names. Don’t drop letters from your source code. Although dropped letters in names like `memcpy` (memory copy) and `strcmp` (string compare) were popular in the C programming language before the 1990s, they’re an unreadable style of naming that you shouldn’t use today. If a name isn’t easily pronounceable, it isn’t easily understood.
Additionally, feel free to use short phrases that can make your code read like plain English. For example, `number_of_trials` is more readable than simply `number_trials`.
[More on naming.](https://inventwithpython.com/beyond/chapter4.html)
Use a spell checker.
### Code Structure
The maximum line length is 80 characters.
The maximum line length is 120 characters.
Whitespaces should be automatically deleted; the autoformatted should take care of this.
Whitespaces should be automatically deleted; the autoformatter should take care of this.
Improve readability by limiting the number of nested statements.
Preferrably write short functions, and pure functions that can be tested.
Preferrably write short functions, and [pure functions](https://realpython.com/python-functional-programming/#:~:text=A%20pure%20function%20is%20a,to%20state%20or%20mutable%20data.) that can be tested.
### Packaging and Deployment
Check the [Packaging Flow](https://packaging.python.org/en/latest/flow/) for thorough and up-to-date information about packaging.
Update the `pyproject.toml` file by filling out the correct metadata, project names, dependencies and paths to modules.
If you have used a project structure with a `src` folder, you will need to replace the `"."` by `"src"` in `[tool.setuptools.packages.find]`.
To try out packaging before deploying a version you can be proud of into the world, use the test instance, TestPyPI (as stated in the packaging flow). The adjustment to PyPI is minor.
# Supervisor Checklist
This template contains the basic structure of a repository needed for starting a Python project.
To adjust the template to suit your needs the best, several settings need to be adjusted:
- [ ] README.md: replace the git clone link in line 22
- [ ] rename the directory `python-project-template` to the name of your project (this is where all of your modules will be stored)
- [ ] python-project-template/__init__.py: update the information
- [ ] .gitignore: replace `python-project-template/` with the name of your project
- [ ] pyproject.toml: replace `python-project-template` with the name of your project
- [ ] pyproject.toml: replace description, author names, keywords
- [ ] pyproject.toml: replace links in [project.urls]
- [ ] docs/conf.py: replace `python-project-template/` with the name of your project
- [ ] docs/conf.py: replace the copyright and author name
- [ ] docs/index.rst: replace replace `python-project-template/` with the name of your project
- [ ] docs/Getting Started.rst: replace the content (can be done later)
The README.md file is written mainly for developers and will need to be adjusted once the project is deployed to fit user perspective better (i.e., to give more information about what the project is rathen than how to contribute to it).
## Enabling the CI/CD Pipeline
To have GitLab automatically run the CI/CD pipeline upon every commit, push and merge request, you need to enable GitLab runners.
To do this, go to `Settings` > `CI/CD` > `Runners`. Under `Group runners`, check whether there is a runner available (`fst-121-docker`). If you have the owner role, you can also enable project runners.
## Documentation Via GitLab Pages
The `.gitlab-ci.yml` file contains script for generating GitLab Pages with Sphinx documentation of the project. These are commented out by default. Uncomment them to include them in the CI/CD pipeline.
You will find the link to the generated GitLab Pages under `Deploy > Pages`. It will look something like this:
https://fst-tuda.pages.rwth-aachen.de/project-templates/python-project-template/
Check the Access Control settings for the Pages to make sure they are visible to the desired group of users. To make your website publicly available, navigate to your project's `Settings > General > Visibility` and select Everyone in pages section.
## Packaging and Deployment
In order to be able to send your project over to PyPI (or TestPyPI), you will need to generate an API token and **set the variable `CI_PYPI_TOKEN` correctly** by following these steps:
1. Login to PyPI or TestPyPI
2. Go to `Account Configuration` > `API Token` and click on `Add an API Token`
3. Copy the generated token (you will **not** be able to do this later)
4. Go to your project on GitLab, open `Settings` > `CI/CD` and scroll down to `Variables`
5. Click on `Add variable`
6. Under key, paste `CI_PYPI_TOKEN`; under Value, paste the API token you have copied
The `CI_PYPI_TOKEN` is referenced in the `pyproject.toml` file for authentification purposes.
Getting Started
===============
Installation
------------
Install python-project-template
.. code-block:: python
pip install python-project-template
Use Case
--------
.. code-block:: python
from python-project-template import reverse_string
string_to_reverse = "hello world"
reversed_string = reverse_string(string_to_reverse)
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = "python-project-template"
copyright = "2023, Some Author"
author = "Some Author"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
"sphinx_rtd_theme",
"sphinx.ext.autosummary",
"autoapi.extension",
]
napoleon_google_docstring = True
autoapi_dirs = ["../python-project-template"]
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = "sphinx_rtd_theme"
html_static_path = ["_static"]
.. python-project-template documentation master file, created by
sphinx-quickstart on Thu Nov 16 14:06:52 2023.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to python-project-template's documentation!
===================================================
.. toctree::
:maxdepth: 2
Getting Started
autoapi/index
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "python_project_template"
dynamic = ["version"]
description = "This project does this and that."
readme = "README.md"
authors = [
{ name = "Some Author", email = "some.author@tu-darmstadt.de" }
]
keywords = ["FST", "engineering"]
license = {file = "LICENSE"}
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3 :: Only",
]
dependencies = [
"numpy",
"pandas",
"matplotlib",
]
requires-python = ">=3.9"
[project.optional-dependencies]
dev = [
"ruff",
"mypy",
]
test = [
"pytest>=7.1.2",
"pytest-cov>=3.0.0",
"mypy>=0.971",
"pandas-stubs>=1.4.3.220822",
"types-tqdm>=4.64.5",
"types-pytz>=2022.2.1.0",
]
doc = ["sphinx>=5.1.1", "sphinx-rtd-theme>=1.0.0", "sphinx-autoapi",]
[project.urls]
repository = "https://git.rwth-aachen.de/path-to-project"
documentation = "https://path-to-project.readthedocs.io/en/latest/"
[tool.setuptools.dynamic]
version = {attr = "python_project_template.__version__"} # this is the module; must contain an __init__.py file with __version__ in the header
[tool.setuptools.packages.find]
where = ["."]
[tool.mypy]
strict = true
show_error_codes = true
[[tool.mypy.overrides]]
module = [
"matplotlib",
"matplotlib.axes",
"matplotlib.pyplot",
"OMPython"
]
ignore_missing_imports = true
[tool.ruff]
line-length = 88
[tool.ruff.format]
docstring-code-format = true
docstring-code-line-length = 80
indent-style = "space"
[tool.ruff.lint]
select = [
"E",
"F",
"W",
# flake8-bugbear: finding likely bugs and design problems in your program.
"B",
# flake8-simplify: simplify your code.
"SIM",
# PEP8 naming
# "N",
# pydocstyle
# "D",
]
preview = true
[tool.ruff.lint.pydocstyle]
convention = "google"
"""Template project."""
__author__ = "Some Author"
__email__ = "some.author@tu-darmstadt.de"
__version__ = "0.0.0"
def reverse_string(str_to_reverse: str):
"""Reverses a string.
Args:
str_to_reverse (str): String that is to be reversed
Returns:
str: reversed string
"""
return str_to_reverse[::-1]
import pytest
from python_project_template.module import reverse_string
test_strings = [("hello", "olleh"), ("racecar", "racecar"), ("Racecar", "racecaR")]
@pytest.mark.parametrize("test_string, expected", test_strings)
def test_reverse_string(test_string, expected):
reversed_string = reverse_string(test_string)
assert reversed_string == expected
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment