diff --git a/.flake8 b/.flake8 deleted file mode 100644 index c84a0d1b..00000000 --- a/.flake8 +++ /dev/null @@ -1,8 +0,0 @@ -[flake8] -ignore = - E501 - W503 - E203 - TRY301 -per-file-ignores = - binder/ipython_config.py:E266 diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 41d4da70..d012f10a 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -1,30 +1,28 @@ --- name: Build Tutorial Container on: - push: - branches: - - main - paths-ignore: - - '*.md' - - slides/** - - images/** - - .gitignore - workflow_dispatch: - + push: + branches: + - main + paths-ignore: + - '*.md' + - slides/** + - images/** + - .gitignore + workflow_dispatch: jobs: - repo2docker: - runs-on: ubuntu-latest - permissions: - packages: write - steps: - - name: checkout files in repo - uses: actions/checkout@main - - - name: update jupyter dependencies with repo2docker - uses: jupyterhub/repo2docker-action@master - with: - DOCKER_USERNAME: ${{ github.actor }} - DOCKER_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - DOCKER_REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - FORCE_REPO2DOCKER_VERSION: jupyter-repo2docker==2023.06.0 + repo2docker: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: checkout files in repo + uses: actions/checkout@main + - name: update jupyter dependencies with repo2docker + uses: jupyterhub/repo2docker-action@master + with: + DOCKER_USERNAME: ${{ github.actor }} + DOCKER_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + DOCKER_REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + FORCE_REPO2DOCKER_VERSION: jupyter-repo2docker==2023.06.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 76ade282..c62419bc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,68 +1,34 @@ --- ci: - autoupdate_schedule: quarterly - + autoupdate_schedule: quarterly repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: - - id: end-of-file-fixer - - id: trailing-whitespace - exclude: miscellaneous/structures/SiO2.xyz - - id: check-yaml - - id: check-added-large-files - args: [--maxkb=6000] - - repo: https://github.com/pycqa/isort - rev: 5.13.2 - hooks: - - id: isort - args: [--profile, black, --filter-files] - - repo: https://github.com/PyCQA/autoflake - rev: v2.3.1 - hooks: - - id: autoflake - - repo: https://github.com/asottile/pyupgrade - rev: v3.16.0 - hooks: - - id: pyupgrade - args: [--py38-plus] - - repo: https://github.com/psf/black - rev: 24.4.2 - hooks: - - id: black - language_version: python3 # Should be a command that runs python3.6+ - - repo: https://github.com/PyCQA/flake8 - rev: 7.1.0 - hooks: - - id: flake8 - args: [--count, --show-source, --statistics] - additional_dependencies: - - flake8-bugbear - - flake8-builtins - - flake8-comprehensions - - flake8-debugger - - flake8-logging-format - - pep8-naming - - pyflakes - - tryceratops - # - repo: https://github.com/pre-commit/mirrors-mypy - # rev: v1.5.1 - # hooks: - # - id: mypy - # additional_dependencies: - # - types-click-spinner - # - types-requests - # - types-tabulate - # - types-toml - - repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt - rev: 0.2.3 - hooks: - - id: yamlfmt - - repo: https://github.com/asottile/setup-cfg-fmt - rev: v2.5.0 - hooks: - - id: setup-cfg-fmt - - repo: https://github.com/kynan/nbstripout - rev: 0.7.1 - hooks: - - id: nbstripout + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-yaml + - id: check-added-large-files + args: [--maxkb=6000] + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.4 + hooks: + # Ruff fix + - id: ruff + types_or: [python, pyi] + args: [--fix] + name: ruff (fix) + # Ruff formatter + - id: ruff-format + types_or: [python, pyi] + name: ruff (format) + - repo: https://github.com/google/yamlfmt + rev: v0.14.0 + hooks: + - id: yamlfmt + name: YAML (format) + types: [yaml] + - repo: https://github.com/kynan/nbstripout + rev: 0.8.1 + hooks: + - id: nbstripout diff --git a/.yamlfmt b/.yamlfmt new file mode 100644 index 00000000..437e1ceb --- /dev/null +++ b/.yamlfmt @@ -0,0 +1,5 @@ +formatter: + type: basic + include_document_start: true + retain_line_breaks: true +output_format: line diff --git a/binder/environment.yml b/binder/environment.yml index 9aa9817f..7d382e1d 100644 --- a/binder/environment.yml +++ b/binder/environment.yml @@ -1,23 +1,23 @@ --- name: python-tutorial channels: - - conda-forge + - conda-forge dependencies: - - python=3.10 - - pip - - pip: - - numpy - - matplotlib - - pandas - - ipywidgets - - ipynbname - - jupyterlab - - pytest - - pytest-timeout - - markdown - - pre-commit - - geostatspy - - gstools - - scikit-learn - - attrs - - multiprocess + - python=3.10 + - pip + - pip: + - numpy + - matplotlib + - pandas + - ipywidgets + - ipynbname + - jupyterlab + - pytest + - pytest-timeout + - markdown + - pre-commit + - geostatspy + - gstools + - scikit-learn + - attrs + - multiprocess diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f7e54a65 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,58 @@ +[tool.ruff] +# Line length (Black default) +line-length = 88 + +# Python target version +target-version = "py38" + +# Ignored rules for the entire project +[tool.ruff.lint] +ignore = [ + "E501", # Line too long + "E203", # Whitespace before ':' + # "TRY301", # Raise within try block (this is actually a good practice) + # "W503" # Line break before binary operator (not PEP8 enforced, so not implemented in Ruff) +] + +# flake8 plugins to enable: +# - flake8-bugbear B +# - flake8-builtins A +# - flake8-comprehensions C4 +# - flake8-debugger T10 +# - flake8-logging-format G +# - pep8-naming N +# - pyflakes F +# - tryceratops TRY +select = [ + "A", # flake8-builtins + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "T10", # flake8-debugger + "G", # flake8-logging-format + "N", # pep8-naming + "F", # pyflakes + "TRY", # tryceratops + "I", # isort + "E", # pycodestyle errors + "UP", # pyupgrade +] + +# Per-file rule ignores +[tool.ruff.lint.per-file-ignores] +# Trailing whitespace in comment +"binder/ipython_config.py" = ["E266"] +# suppress `raise ... from err` +# Why we ignore B904 from the object-oriented tests? +# We do want to raise an assertion error if the check on the solution function attributes fails, +# but Python by default will raise a TypeError via vars(solution_result) +# if the result is not a class and therefore doesn't have a __dict__ attribute. +"tutorial/tests/test_object_oriented_programming.py" = ["B904"] + +# Ruff formatting +[tool.ruff.format] +quote-style = "double" +indent-style = "space" + +# pytest +[tool.pytest.ini_options] +addopts = "-v --tb=short" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index bbd083ac..00000000 --- a/pytest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -addopts = --tb=short diff --git a/tutorial/tests/test_functional_programming.py b/tutorial/tests/test_functional_programming.py index 8cf0c240..d9ef3fda 100644 --- a/tutorial/tests/test_functional_programming.py +++ b/tutorial/tests/test_functional_programming.py @@ -143,7 +143,7 @@ def reference_exercise6(my_list: List[Tuple[str, int]]) -> List[Tuple[str, float def test_exercise6( - function_to_test: Callable[[List[Tuple[str, int]]], List[Tuple[str, float]]] + function_to_test: Callable[[List[Tuple[str, int]]], List[Tuple[str, float]]], ): input_data = reference_exercise5(get_data_exercise5()) assert function_to_test(input_data) == reference_exercise6(input_data) diff --git a/tutorial/tests/test_input_output.py b/tutorial/tests/test_input_output.py index 62c5a4ff..09403d49 100644 --- a/tutorial/tests/test_input_output.py +++ b/tutorial/tests/test_input_output.py @@ -98,9 +98,7 @@ def test_write_file(function_to_test, tmp_path: pl.Path): function_to_test(tmp_user) reference_write_file(tmp_test) - assert ( - tmp_user.exists() - ), """The file was not created. Make sure to create the file in the function. Hint: use `open(output_file, "w")`""" + assert tmp_user.exists(), """The file was not created. Make sure to create the file in the function. Hint: use `open(output_file, "w")`""" assert ( tmp_user.read_text() == tmp_test.read_text() diff --git a/tutorial/tests/test_object_oriented_programming.py b/tutorial/tests/test_object_oriented_programming.py index 5be87d87..43af192a 100644 --- a/tutorial/tests/test_object_oriented_programming.py +++ b/tutorial/tests/test_object_oriented_programming.py @@ -177,8 +177,8 @@ def test_oop_str_and_repr(first_name, last_name, function_to_test): validate_oop_str_method(solution_result) validate_oop_repr_method(solution_result) - assert str(solution_result) == str( - reference_result + assert ( + str(solution_result) == str(reference_result) ), "The __str__() result does not match the template 'My name is {first_name} {last_name}'." assert ( solution_result.__repr__() == reference_result.__repr__() diff --git a/tutorial/toc.py b/tutorial/toc.py index 627fe7a2..c1a382ed 100755 --- a/tutorial/toc.py +++ b/tutorial/toc.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """CLI script to build a table of contents for an IPython notebook""" + import argparse as ap import pathlib import re