From 3d900a4737680b692691e1b151db817f0aa0bf19 Mon Sep 17 00:00:00 2001 From: Thibaut de Saivre <thibaut.de-saivre@polytechnique.edu> Date: Thu, 12 Oct 2023 19:06:27 +0200 Subject: [PATCH] add python template --- .vscode/settings.json | 8 +- README.md | 7 ++ python/.gitignore | 2 + python/.vscode/extensions.json | 8 ++ python/.vscode/settings.json | 16 ++++ python/README.md | 133 +++++++++++++++++++++++++++++++++ python/lib/lib.py | 2 + python/main.py | 23 ++++++ python/pyproject.toml | 66 ++++++++++++++++ python/requirements.txt | 1 + 10 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 python/.gitignore create mode 100644 python/.vscode/extensions.json create mode 100644 python/.vscode/settings.json create mode 100644 python/README.md create mode 100644 python/lib/lib.py create mode 100644 python/main.py create mode 100644 python/pyproject.toml create mode 100644 python/requirements.txt diff --git a/.vscode/settings.json b/.vscode/settings.json index ce19b10..4d8407d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -65,7 +65,13 @@ "editor.defaultFormatter": "ms-python.black-formatter", // Use Black formatter for python "editor.formatOnType": true }, - "python.analysis.typeCheckingMode": "basic", // or "strict" : use stricter python type checking + "python.analysis.typeCheckingMode": "strict", // or "strict" : use stricter python type checking + + // Add explicit hints overlays for arguments, return types, variable types... + "python.analysis.inlayHints.functionReturnTypes": true, + "python.analysis.inlayHints.variableTypes": true, + "python.analysis.inlayHints.pytestParameters": true, + "python.analysis.inlayHints.callArgumentNames": "all", // Python Jupyter Notebooks "jupyter.interactiveWindow.textEditor.executeSelection": true, diff --git a/README.md b/README.md index 45122be..8573d78 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # VSCode Config Base VSCode config for a better dev experience. +Base Project configs for common languages. ## Recommended Extensions @@ -33,3 +34,9 @@ Good default settings for VSCode. - Configure **formatters** for every language you use. - Additional language configs (better linting, stricter python type checking...). + +## Default Project configs & Workspace settings + +- give example docker configs & gitlab-ci configs (pb : ça va être chiant si on doit faire de la ci sécurisée...) +- workspace settings +- readme avec requirements.txt, faire un venv... diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..01d7f95 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +venv \ No newline at end of file diff --git a/python/.vscode/extensions.json b/python/.vscode/extensions.json new file mode 100644 index 0000000..2c62afd --- /dev/null +++ b/python/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "ms-python.python", // Python language support + "ms-python.vscode-pylance", // Python language server + "ms-python.black-formatter", // FORMATTER (black, one of the best python formatters) + "charliermarsh.ruff" // LINTER (ruff, blazingly fast python linter, written in rust) + ] +} diff --git a/python/.vscode/settings.json b/python/.vscode/settings.json new file mode 100644 index 0000000..e57ae21 --- /dev/null +++ b/python/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + // Generic settings : + "editor.formatOnSave": true, // Format files on save + "formatFiles.runOrganizeImports": true, // Sort imports when formatting + "editor.codeActionsOnSave": { + // Organize imports on save + "source.organizeImports": true + }, + + // Python + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter", // Use Black formatter for python + "editor.formatOnType": true + }, + "python.analysis.typeCheckingMode": "strict" // or "strict" : use stricter python type checking +} diff --git a/python/README.md b/python/README.md new file mode 100644 index 0000000..705e604 --- /dev/null +++ b/python/README.md @@ -0,0 +1,133 @@ +# Template Python Project + +[](https://www.python.org/downloads/release/python-311/) +[](https://github.com/psf/black) +[](https://beta.ruff.rs/docs/) + +## VSCode Project Settings + +In order to ensure that everyone uses the same linting and formatting settings, +we duplicate the relevant settings in [`.vscode/settings.json`](.vscode/settings.json). + +Relevant settings with python : + +- format on save +- strict type checking +- format using **Black** + +## Formatter - Black + +[Black](https://github.com/psf/black) is a popular python formatter which is strict and opinionated. +Using a formatter ensures that the code you and your team write is consistent. +You should always use a formatter. + +Additional configuration for the formatter can be added to the [`pyproject.toml`](./pyproject.toml) file, +although the default settings are already pretty good. + +## Linter - Ruff + +[Ruff](https://github.com/astral-sh/ruff) is a good python linter, in the sense that it is strict enough, and coded in rust (blazingly fast). +It is the fastest python linter available, which is nice when you want reactive linting during typing. +Using a linter will help you write cleaner code, and avoid common mistakes. +You should always use a linter. + +Additional configuration for the formatter can be added to the [`pyproject.toml`](./pyproject.toml) file, +although the default settings are already pretty good. + +## Environment and reproducibility + +In order to make it easier for other developers to contribute to your project, +and to enable deploying your application easily, you must indicate which python version and dependencies you are using. + +You typically indicate the python version in your [`pyproject.toml`](./pyproject.toml) or (`README.md`)(./README.md) file. + +### Create a virtual environment + +If you do not use `conda`, you may create a virtual environment. Virtual environments are often contained in a `venv` folder. +Virtual environment enable you to have separate python versions and dependencies for each project. + +```bash +python --version # Ensure that your current python version is the one you want to use. Use `pyenv` to change it if needed (see wikibr). +python -m venv venv # Creates a virtual environment in the `venv` folder. + +# LINUX / MACOS : activate the virtual environment +source venv/bin/activate + +# WINDOWS : activate the virtual environment +venv\Scripts\activate +``` + +You must always activate your virtual environment before running your code or installing dependencies (else it will use your global python interpreter). + +### Install and Freeze dependencies + +Python depdendencies are typically indicated in a [`requirements.txt`](./requirements.txt) file. + +Install dependencies : + +```bash +pip install --upgrade pip # Update pip (python package manager) +pip install -r requirements.txt # Install dependencies read from the requirements file +``` + +Freeze dependencies : +You can write your dependencies by hand in the `requirements.txt` file, or you can "freeze" your current environment. + +```bash +pip freeze > requirements.txt # Write the current dependencies to the requirements file +``` + +### Git + +Some common gitignores for a python project : + +- `__pycache__` : python cache, generated at runtime. +- `venv` : virtual environment folder. + +## Python Clean Code + +Some often-overlooked python clean code rules : + +### Docstrings + +Always write docstrings in order to describe the purpose of your functions and classes. + +```python +def function(): + """This function does something.""" + pass +``` + +### Type hinting + +When you can (always), use type hints to indicate the types of the parameters of your functions, their return types, etc... +You sometimes need to use the `typing` module to indicate more complex types. + +```python +from typing import List +def add_to_list(my_list: List[int], element: int) -> List[int]: + my_list.append(element) + return my_list +``` + +Since python 3.10, you can use the `|` operator to indicate that a parameter can be of multiple types, and use `list` and `dict` directly as type hints. + +```python +def add_to_list(my_list: list[int | float], element: int | float) -> list[int | float]: + my_list.append(element) + return my_list +``` + +### Use modules + +When your code gets big, you should split it into multiple files. + +Use the following test in order to run module-specific code only when you run your module directly via `python module.py`, and not when you import it from another file. + +```python +def module_function(): + pass + +if __name__ == "__main__": + module_function() +``` diff --git a/python/lib/lib.py b/python/lib/lib.py new file mode 100644 index 0000000..ca1a381 --- /dev/null +++ b/python/lib/lib.py @@ -0,0 +1,2 @@ +def dependency_function(message: str) -> None: + print(f"Printing a message from the dependency: {message}") diff --git a/python/main.py b/python/main.py new file mode 100644 index 0000000..2ed7c8a --- /dev/null +++ b/python/main.py @@ -0,0 +1,23 @@ +from lib.lib import dependency_function + + +def test(a: int, b: int) -> bool: + """Tests the equality of two integers""" + return a == b + + +def wrong_input_type(test: int): + """If you pass anything other than an integer to this function, Pylance will warn you !""" + return test + + +if __name__ == "__main__": + a = 1 + b = 2 + + print(test(a, b)) + + # Pylance will warn about wrong types thanks to "typeCheckingMode": "strict" + wrong_input_type("test") + + dependency_function("message") diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 0000000..9f004e6 --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,66 @@ +########################################################################################### +# EXAMPLE RUFF CONFIG (https://github.com/astral-sh/ruff/blob/main/docs/configuration.md) # +########################################################################################### + +[tool.ruff] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +select = ["E4", "E7", "E9", "F"] +ignore = [] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", +] +per-file-ignores = {} + +# Same as Black. +line-length = 88 + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +# Assume Python 3.8 +target-version = "py38" + +################################################################################ +# EXAMPLE BLACK CONFIG (https://github.com/psf/black/blob/main/pyproject.toml) # +################################################################################ + +[tool.black] + +line-length = 88 +target-version = ['py37', 'py38'] +include = '\.pyi?$' +extend-exclude = ''' +/( + # The following are specific to Black, you probably don't want those. + tests/data + | profiling +)/ +''' diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 0000000..296d654 --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1 @@ +numpy \ No newline at end of file -- GitLab