Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ lint.select = [
lint.ignore = [ "PLR2004", "S101", "SIM108" ]
lint.per-file-ignores."!tests/**/*.py" = [ "ARG002", "PT" ]
lint.per-file-ignores."src/usethis/_ui/interface/**/*.py" = [ "PLR0913" ]

lint.per-file-ignores."tests/**" = [ "D", "INP", "S603", "TC" ]
lint.flake8-bugbear.extend-immutable-calls = [ "typer.Argument", "typer.Option" ]
lint.flake8-builtins.strict-checking = true
Expand All @@ -155,6 +154,7 @@ lint.flake8-type-checking.quote-annotations = true
lint.flake8-type-checking.runtime-evaluated-base-classes = [ "pydantic.BaseModel" ]
lint.flake8-type-checking.strict = true
lint.pydocstyle.convention = "google"
lint.future-annotations = true

[tool.codespell]
skip = [ "site" ]
Expand Down
14 changes: 9 additions & 5 deletions src/usethis/_backend/uv/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from typing import TYPE_CHECKING

from pydantic import TypeAdapter, ValidationError

from usethis._backend.uv.call import call_uv_subprocess
from usethis._backend.uv.errors import (
UVDepGroupError,
Expand Down Expand Up @@ -37,12 +39,14 @@ def get_default_groups_via_uv() -> list[str]:
"""Get the default dependency groups from the uv configuration."""
try:
if UVTOMLManager().path.exists():
default_groups = UVTOMLManager()[["default-groups"]]
default_groups = TypeAdapter(list[str]).validate_python(
UVTOMLManager()[["default-groups"]]
)
else:
default_groups = PyprojectTOMLManager()[["tool", "uv", "default-groups"]]
if not isinstance(default_groups, list):
default_groups = []
except KeyError:
default_groups = TypeAdapter(list[str]).validate_python(
PyprojectTOMLManager()[["tool", "uv", "default-groups"]]
)
except (KeyError, ValidationError):
default_groups = []

return default_groups
8 changes: 6 additions & 2 deletions src/usethis/_core/status.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from pydantic import TypeAdapter, ValidationError

from usethis._console import tick_print
from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager
from usethis._init import ensure_pyproject_toml
Expand All @@ -19,8 +21,10 @@ def use_development_status(

mgr = PyprojectTOMLManager()
try:
existing_classifiers = mgr[["project", "classifiers"]]
except KeyError:
existing_classifiers = TypeAdapter(list[str]).validate_python(
mgr[["project", "classifiers"]]
)
except (KeyError, ValidationError):
existing_classifiers = []
existing_status_classifiers = {
classifier
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_file/ini/io_.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def __contains__(self, keys: Sequence[Key]) -> bool:
return True
return False

def __getitem__(self, item: Sequence[Key]) -> Any:
def __getitem__(self, item: Sequence[Key]) -> object:
keys = item

root = self.get()
Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_file/toml/io_.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def __contains__(self, keys: Sequence[Key]) -> bool:

return True

def __getitem__(self, item: Sequence[Key]) -> Any:
def __getitem__(self, item: Sequence[Key]) -> object:
keys = item
keys = _validate_keys(keys)

Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_file/yaml/io_.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def __contains__(self, keys: Sequence[Key]) -> bool:

return True

def __getitem__(self, item: Sequence[Key]) -> Any:
def __getitem__(self, item: Sequence[Key]) -> object:
keys = item
keys = _validate_keys(keys)

Expand Down
28 changes: 15 additions & 13 deletions src/usethis/_integrations/sonarqube/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import re

from pydantic import TypeAdapter
from pydantic import TypeAdapter, ValidationError

from usethis._config import usethis_config
from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager
Expand Down Expand Up @@ -71,38 +71,40 @@ def get_sonar_project_properties() -> str:

def _get_sonarqube_project_key() -> str:
try:
project_key = PyprojectTOMLManager()[
["tool", "usethis", "sonarqube", "project-key"]
]
project_key = TypeAdapter(str).validate_python(
PyprojectTOMLManager()[["tool", "usethis", "sonarqube", "project-key"]]
)
except KeyError:
msg = "Could not find SonarQube project key at 'tool.usethis.sonarqube.project-key' in 'pyproject.toml'."
raise MissingProjectKeyError(msg) from None
except FileNotFoundError:
msg = "Could not find 'pyproject.toml' for SonarQube project key at 'tool.usethis.sonarqube.project-key'."
raise MissingProjectKeyError(msg) from None
except ValidationError:
msg = "SonarQube project key at 'tool.usethis.sonarqube.project-key' in 'pyproject.toml' must be a string."
raise InvalidSonarQubeProjectKeyError(msg) from None
_validate_project_key(project_key)
return project_key


def _is_sonarqube_verbose() -> bool:
try:
verbose = PyprojectTOMLManager()[["tool", "usethis", "sonarqube", "verbose"]]
except (FileNotFoundError, KeyError):
verbose = TypeAdapter(bool).validate_python(
PyprojectTOMLManager()[["tool", "usethis", "sonarqube", "verbose"]]
)
except (FileNotFoundError, KeyError, ValidationError):
verbose = False
verbose = TypeAdapter(bool).validate_python(verbose)

return verbose


def _get_sonarqube_exclusions() -> list[str]:
try:
exclusions = PyprojectTOMLManager()[
["tool", "usethis", "sonarqube", "exclusions"]
]
except (FileNotFoundError, KeyError):
exclusions = TypeAdapter(list).validate_python(
PyprojectTOMLManager()[["tool", "usethis", "sonarqube", "exclusions"]]
)
except (FileNotFoundError, KeyError, ValidationError):
exclusions = []
# TypeAdapter(list).validate_python() ensures we have a list and returns a new list
exclusions = TypeAdapter(list).validate_python(exclusions)
for exclusion in exclusions:
TypeAdapter(str).validate_python(exclusion)

Expand Down
2 changes: 1 addition & 1 deletion src/usethis/_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def __contains__(self, keys: Sequence[Key]) -> bool:
raise NotImplementedError

@abstractmethod
def __getitem__(self, keys: Sequence[Key]) -> Any:
def __getitem__(self, keys: Sequence[Key]) -> object:
raise NotImplementedError

def __setitem__(self, keys: Sequence[Key], value: Any) -> None:
Expand Down
8 changes: 5 additions & 3 deletions src/usethis/_tool/impl/base/deptry.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

from typing import TYPE_CHECKING, final

from pydantic import TypeAdapter, ValidationError

from usethis._console import info_print
from usethis._file.pyproject_toml.io_ import PyprojectTOMLManager
from usethis._tool.base import Tool
from usethis._tool.impl.spec.deptry import DeptryToolSpec
from usethis._tool.rule import Rule

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._tool.rule import Rule


class DeptryTool(DeptryToolSpec, Tool):
Expand Down Expand Up @@ -39,8 +41,8 @@ def ignored_rules(self) -> list[Rule]:
(file_manager,) = self.get_active_config_file_managers()
keys = self._get_ignore_keys(file_manager)
try:
rules: list[Rule] = file_manager[keys]
except (KeyError, FileNotFoundError):
rules = TypeAdapter(list[Rule]).validate_python(file_manager[keys])
except (KeyError, FileNotFoundError, ValidationError):
rules = []

return rules
Expand Down
19 changes: 10 additions & 9 deletions src/usethis/_tool/impl/base/ruff.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pathlib import Path
from typing import TYPE_CHECKING, Literal, final

from pydantic import TypeAdapter, ValidationError
from typing_extensions import assert_never

from usethis._backend.dispatch import get_backend
Expand Down Expand Up @@ -30,7 +31,7 @@

if TYPE_CHECKING:
from usethis._io import KeyValueFileManager
from usethis._tool.rule import Rule, RuleConfig
from usethis._tool.rule import RuleConfig

_RUFF_VERSION = "v0.15.4" # Manually bump this version when necessary

Expand Down Expand Up @@ -204,8 +205,8 @@ def selected_rules(self) -> list[Rule]:

keys = self._get_select_keys(file_manager)
try:
rules: list[Rule] = file_manager[keys]
except (KeyError, FileNotFoundError):
rules = TypeAdapter(list[Rule]).validate_python(file_manager[keys])
except (KeyError, FileNotFoundError, ValidationError):
rules = []

return rules
Expand All @@ -216,8 +217,8 @@ def ignored_rules(self) -> list[Rule]:
(file_manager,) = self.get_active_config_file_managers()
keys = self._get_ignore_keys(file_manager)
try:
rules: list[Rule] = file_manager[keys]
except (KeyError, FileNotFoundError):
rules = TypeAdapter(list[Rule]).validate_python(file_manager[keys])
except (KeyError, FileNotFoundError, ValidationError):
rules = []

return rules
Expand Down Expand Up @@ -247,8 +248,8 @@ def get_ignored_rules_in_glob(self, glob: str) -> list[Rule]:
(file_manager,) = self.get_active_config_file_managers()
keys = self._get_per_file_ignore_keys(file_manager, glob=glob)
try:
rules: list[Rule] = file_manager[keys]
except (KeyError, FileNotFoundError):
rules = TypeAdapter(list[Rule]).validate_python(file_manager[keys])
except (KeyError, FileNotFoundError, ValidationError):
rules = []

return rules
Expand Down Expand Up @@ -309,8 +310,8 @@ def get_docstyle(self) -> Literal["numpy", "google", "pep257"] | None:
(file_manager,) = self.get_active_config_file_managers()
keys = self._get_docstyle_keys(file_manager)
try:
docstyle = file_manager[keys]
except (KeyError, FileNotFoundError):
docstyle = TypeAdapter(str).validate_python(file_manager[keys])
except (KeyError, FileNotFoundError, ValidationError):
docstyle = None

if docstyle not in ("numpy", "google", "pep257"):
Expand Down
6 changes: 3 additions & 3 deletions tests/usethis/_core/test_core_ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ def test_mentioned_in_file(
(uv_init_dir / "tests" / "conftest.py").touch()

with change_cwd(uv_init_dir), files_manager():
PyprojectTOMLManager()[["project"]]["requires-python"] = (
PyprojectTOMLManager()[["project", "requires-python"]] = (
">=3.12,<3.14"
)

Expand Down Expand Up @@ -596,7 +596,7 @@ def test_matrix_enabled_by_default(self, uv_init_dir: Path):
(uv_init_dir / "tests" / "conftest.py").touch()

with change_cwd(uv_init_dir), files_manager():
PyprojectTOMLManager()[["project"]]["requires-python"] = ">=3.12,<3.14"
PyprojectTOMLManager()[["project", "requires-python"]] = ">=3.12,<3.14"

# Act
use_ci_bitbucket()
Expand All @@ -623,7 +623,7 @@ def test_matrix_disabled_creates_single_step(
change_cwd(uv_init_dir),
files_manager(),
):
PyprojectTOMLManager()[["project"]]["requires-python"] = ">=3.12,<3.14"
PyprojectTOMLManager()[["project", "requires-python"]] = ">=3.12,<3.14"

# Act
use_ci_bitbucket(matrix_python=False)
Expand Down
19 changes: 12 additions & 7 deletions tests/usethis/_core/test_core_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from unittest.mock import MagicMock

import pytest
from pydantic import TypeAdapter

import usethis._backend.dispatch
import usethis._python.version
Expand Down Expand Up @@ -1558,9 +1559,11 @@ def test_no_duplicate_inp_rules(self, tmp_path: Path):
len(
[
rule
for rule in RuffTOMLManager()[
["lint", "per-file-ignores", "tests/**"]
]
for rule in TypeAdapter(list[str]).validate_python(
RuffTOMLManager()[
["lint", "per-file-ignores", "tests/**"]
]
)
if rule == "INP"
]
)
Expand Down Expand Up @@ -2753,9 +2756,9 @@ def test_registers_test_group(self, tmp_path: Path):
use_pytest()

# Assert
default_groups = PyprojectTOMLManager()[
["tool", "uv", "default-groups"]
]
default_groups = TypeAdapter(list[str]).validate_python(
PyprojectTOMLManager()[["tool", "uv", "default-groups"]]
)
assert "test" in default_groups

@pytest.mark.usefixtures("_vary_network_conn")
Expand All @@ -2768,7 +2771,9 @@ def test_registers_test_group_uv_toml(self, tmp_path: Path):
use_pytest()

# Assert
default_groups = UVTOMLManager()[["default-groups"]]
default_groups = TypeAdapter(list[str]).validate_python(
UVTOMLManager()[["default-groups"]]
)
assert "test" in default_groups

@pytest.mark.usefixtures("_vary_network_conn")
Expand Down
Loading
Loading