diff --git a/cookiecutter.json b/cookiecutter.json index 642c0af..1cbd34d 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -12,7 +12,7 @@ "ci_pipeline": ["none", "gitlab"], "create_cli": ["no", "yes"], "config_file": ["none", "hocon", "yaml"], - "code_formatter": ["none", "black"], + "code_formatter": ["none", "black", "ruff"], "editor_settings": ["none", "pycharm", "vscode"], "__prompts__": { "full_name": "What's your [bold yellow]name[/]?", @@ -55,10 +55,11 @@ "yaml": "YAML" }, "code_formatter": { - "__prompt__": "What [bold yellow]code formatter[/] would you like to use?", - "none": "None", - "black": "Black" - }, + "__prompt__": "What [bold yellow]code formatter[/] would you like to use (Ruff includes linting)?", + "none": "None", + "black": "Black", + "ruff": "Ruff (includes linting)" + }, "editor_settings": { "__prompt__": "Which [bold yellow]editor settings[/] do you want to include?", "none": "None", diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 39f50af..496487f 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -21,6 +21,7 @@ 'requirements-dev.txt', 'setup.py', 'tests/pytest.ini', + '.ruff.toml' } files_conda = { @@ -28,6 +29,7 @@ 'environment-dev.yml', 'setup.py', 'tests/pytest.ini', + '.ruff.toml' } files_poetry = { @@ -155,7 +157,7 @@ def handle_config(): def handle_formatter(): code_formatter = '{{ cookiecutter.code_formatter }}' - if code_formatter in ['none', 'black']: + if code_formatter in ['none', 'black', 'ruff']: # no action necessary pass else: diff --git a/pyproject.toml b/pyproject.toml index 7e15db8..ded5c92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,12 +17,12 @@ packages = [{ include = "{{cookiecutter.module_name}}", from = "{{cookiecutter.p [tool.poetry.dependencies] python = "^3.9" cookiecutter = "^2.3" -pre-commit = "^4.1.0" -pytest-mock = "^3.12" +pre-commit = "^4.2.0" +pytest-mock = "^3.14.1" pyhocon = "^0.3.60" pyyaml = "^6.0" -typer = "^0.15.2" -setuptools = "^76.0.0" +typer = "^0.16.0" +setuptools = "^80.9.0" [tool.poetry.group.dev.dependencies] pytest = "^8.3.5" diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml index 141fa8e..cd59d41 100644 --- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml +++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml @@ -22,6 +22,7 @@ variables: stages: - build + - lint - test - deploy-nonprod - deploy-prod @@ -51,6 +52,59 @@ build-wheel: script: - python setup.py bdist_wheel{%- endif %} +{% if cookiecutter.code_formatter == 'ruff' and cookiecutter.package_manager == 'poetry' -%} +linter-happiness: + stage: lint + image: python:3.11 + cache: + key: + files: + - poetry.lock + paths: + - $PIP_CACHE_DIR + - $POETRY_CACHE_DIR + before_script: + - pip install poetry==$POETRY_VERSION + - poetry install --no-interaction --no-ansi --only lint + script: + - poetry run ruff check . --config pyproject.toml +{% elif cookiecutter.code_formatter == 'ruff' and cookiecutter.package_manager == 'conda' -%} +linter-happiness: + stage: lint + image: continuumio/miniconda3 + cache: + key: + files: + - environment.yml + - environment-dev.yml + paths: + - $CONDA_PKGS_DIRS/*.tar.bz2 + - $CONDA_PKGS_DIRS/urls.txt + before_script: + - export PATH="/opt/conda/bin:$PATH" + - conda env create -n .venv -f environment-dev.yml environment.yml + - source activate .venv + script: + - ruff check . --config .ruff.toml +{% elif cookiecutter.code_formatter == 'ruff' and cookiecutter.package_manager == 'pip' -%} +linter-happiness: + stage: lint + image: python:3.11 + cache: + key: + files: + - requirements.txt + - requirements-dev.txt + paths: + - $PIP_CACHE_DIR + before_script: + - python -m venv .venv + - source .venv/bin/activate + - pip install ruff + script: + - ruff check . --config .ruff.toml +{% endif %} + {% if cookiecutter.package_manager == 'poetry' -%} test-unit: stage: test diff --git a/{{cookiecutter.project_slug}}/.ruff.toml b/{{cookiecutter.project_slug}}/.ruff.toml new file mode 100644 index 0000000..1900821 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.ruff.toml @@ -0,0 +1,28 @@ +# Ruff configuration file +target-version = "py311" +line-length = 100 +exclude = ["__init__.py"] +extend-exclude = ["notebooks/*"] + +[lint] +select = [ + "E", # Pycodestyle errors + "F", # Pyflakes errors + "UP", # pyupgrade checks + "B", # Bugbear checks + "SIM",# Similarity checks + "I", # Import order checks (isort) + "N" # Naming convention checks +] +task-tags = ["TODO", "FIXME", "NOTE"] + +[lint.isort] +known-first-party = ["{{ cookiecutter.project_slug }}"] + +[lint.pycodestyle] +max-line-length = 100 + +[lint.flake8-quotes] +inline-quotes = "double" +multiline-quotes = "double" +docstring-quotes = "double" diff --git a/{{cookiecutter.project_slug}}/environment-dev.yml b/{{cookiecutter.project_slug}}/environment-dev.yml index 30c2d83..2e49c4c 100644 --- a/{{cookiecutter.project_slug}}/environment-dev.yml +++ b/{{cookiecutter.project_slug}}/environment-dev.yml @@ -17,4 +17,4 @@ dependencies: - pip: - black>=25.1.0{% else %} - pip: - - ruff>=0.11.0{% endif %} + - ruff>=0.12.2{% endif %} diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index 20597e8..a7e54a7 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -17,53 +17,65 @@ PyYAML = "^6.0" {%- endif %} {%- if cookiecutter.create_cli == 'yes' %} typer = "^0.15.1" +{{ cookiecutter.project_slug }} = "{{ cookiecutter.module_name }}.main:app" {%- endif %} [tool.poetry.group.test.dependencies] pytest = "^8.3.4" -pytest-cov = "^4.0" +pytest-cov = "^6.0.0" [tool.poetry.group.linter.dependencies] {%- if cookiecutter.code_formatter == 'black' %} black = "^25.1.0" {%- else %} -ruff = "^0.1.7" +ruff = "^0.12.2" {%- endif %} isort = "^5.12.0" -[tool.poetry.group.dev.dependencies] -pre-commit = "^3.0" -{%- if cookiecutter.use_notebooks == 'yes' %} -jupyterlab = "^3.5" -{%- endif %} - [tool.poetry.extras] build = ["setuptools", "cython"] -[tool.poetry.scripts] -{%- if cookiecutter.create_cli == "yes" %} -{{ cookiecutter.project_slug }} = "{{ cookiecutter.module_name }}.main:app" -{%- endif %} - -[tool.isort] -{%- if cookiecutter.code_formatter == 'black' %} -profile = "black" -{%- else %} +[tool.poetry.group.dev.dependencies] +pre-commit = "^4.1.0"{% if cookiecutter.use_notebooks == 'yes' %} +jupyterlab = "^4.3.5"{% endif %} +{% if cookiecutter.code_formatter != 'black' %} +[tool.ruff] +line-length = 100 +src = ["src", "tests"] +ignore = ["F401"] +target-version = "py39" +exclude = ["__init__.py"] +extend-exclude = ["notebooks/*"] + +[tool.ruff.lint] +select = [ + "E", # Pycodestyle errors + "F", # Pyflakes errors + "UP", # pyupgrade checks + "B", # Bugbear checks + "SIM",# Simplifcation checks + "I", # Import order checks (isort) + "N" # Naming convention checks +] +task-tags = ["TODO", "FIXME", "NOTE"] +{% endif %} +[tool.isort]{% if cookiecutter.code_formatter == 'black' %} +profile = "black"{% else %} +py_version = 310 line_length = 100 -multi_line_output = 3 -{%- endif %} +multi_line_output = 3{% endif %} [tool.pytest.ini_options] minversion = "7.0" testpaths = ["tests"] -[tool.uv] -default-groups = ["dev", "test", "linter"] - [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" +[tool.uv] +default-groups = ["dev", "test", "linter"] + {%- else %}[project] name = "{{ cookiecutter.project_slug }}" version = "0.1.0" @@ -104,11 +116,11 @@ linter = [ {%- if cookiecutter.code_formatter == 'black' %} "black>=25.1.0", {%- else %} -"ruff>=0.1.7", +"ruff>=0.12.2", {%- endif %} "isort>=5.12.0" ] -test = ["pytest>=8.3.4", "pytest-cov>=4.0"] +test = ["pytest>=8.3.4", "pytest-cov>=6.0"] [tool.isort] {%- if cookiecutter.code_formatter == 'black' %} diff --git a/{{cookiecutter.project_slug}}/requirements-dev.txt b/{{cookiecutter.project_slug}}/requirements-dev.txt index 9057926..7496bf5 100644 --- a/{{cookiecutter.project_slug}}/requirements-dev.txt +++ b/{{cookiecutter.project_slug}}/requirements-dev.txt @@ -1,8 +1,17 @@ # This file defines the additional requirements for a developer working # with this project (e.g. for testing or useful development tools). -# The regular project dependencies are defined in requirements.txt{% if cookiecutter.code_formatter == 'black' %} -black~=25.1.0{% endif %} +# The regular project dependencies are defined in requirements.txt +{% if cookiecutter.code_formatter == 'black' %} +black~=25.1.0 +{% elif cookiecutter.code_formatter == 'ruff' %} +ruff~=0.12.2 +{% endif %} pre-commit~=4.1 pytest~=8.3.5 pytest-cov~=6.0 wheel~=0.45.1 +{% if cookiecutter.code_formatter == 'none' or cookiecutter.code_formatter == 'black' %} +isort~=5.12.0 +pyupgrade~=3.15.0 +{% endif %} +