confkit 1.3.1__tar.gz → 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. {confkit-1.3.1 → confkit-2.0.0}/.github/workflows/bump-version-by-labels.yml +2 -1
  2. {confkit-1.3.1 → confkit-2.0.0}/PKG-INFO +1 -1
  3. {confkit-1.3.1 → confkit-2.0.0}/pyproject.toml +1 -1
  4. {confkit-1.3.1 → confkit-2.0.0}/ruff.toml +7 -2
  5. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/__init__.py +1 -0
  6. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/config.py +6 -7
  7. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/data_types.py +12 -7
  8. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/exceptions.py +2 -0
  9. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/ext/__init__.py +1 -0
  10. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/ext/pydantic.py +7 -1
  11. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/ext/reference.py +1 -0
  12. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/parsers.py +0 -2
  13. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/sentinels.py +1 -0
  14. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/watcher.py +6 -1
  15. {confkit-1.3.1 → confkit-2.0.0}/tests/test_config.py +10 -8
  16. {confkit-1.3.1 → confkit-2.0.0}/tests/test_config_classvars.py +8 -4
  17. {confkit-1.3.1 → confkit-2.0.0}/tests/test_config_decorators.py +7 -3
  18. {confkit-1.3.1 → confkit-2.0.0}/tests/test_config_detect_parser.py +2 -0
  19. {confkit-1.3.1 → confkit-2.0.0}/tests/test_data_types.py +2 -0
  20. {confkit-1.3.1 → confkit-2.0.0}/tests/test_enum_allowed_values.py +2 -0
  21. {confkit-1.3.1 → confkit-2.0.0}/tests/test_enum_auto_conversion.py +5 -1
  22. {confkit-1.3.1 → confkit-2.0.0}/tests/test_list_type.py +1 -0
  23. {confkit-1.3.1 → confkit-2.0.0}/tests/test_metaclass.py +2 -0
  24. {confkit-1.3.1 → confkit-2.0.0}/tests/test_msgspec.py +2 -0
  25. {confkit-1.3.1 → confkit-2.0.0}/tests/test_msgspecparser_branches.py +2 -0
  26. {confkit-1.3.1 → confkit-2.0.0}/tests/test_msgspecparser_no_msgspec.py +2 -0
  27. {confkit-1.3.1 → confkit-2.0.0}/tests/test_msgspecparser_read.py +6 -1
  28. {confkit-1.3.1 → confkit-2.0.0}/tests/test_msgspecparser_sections.py +2 -0
  29. {confkit-1.3.1 → confkit-2.0.0}/tests/test_multiple_configurations.py +2 -0
  30. {confkit-1.3.1 → confkit-2.0.0}/tests/test_on_file_change.py +2 -0
  31. {confkit-1.3.1 → confkit-2.0.0}/tests/test_sentinel.py +1 -0
  32. {confkit-1.3.1 → confkit-2.0.0}/tests/test_two_instances.py +2 -0
  33. {confkit-1.3.1 → confkit-2.0.0}/tests/test_unreachable_paths.py +2 -1
  34. {confkit-1.3.1 → confkit-2.0.0}/.bumpversion.toml +0 -0
  35. {confkit-1.3.1 → confkit-2.0.0}/.github/FUNDING.yml +0 -0
  36. {confkit-1.3.1 → confkit-2.0.0}/.github/copilot-instructions.md +0 -0
  37. {confkit-1.3.1 → confkit-2.0.0}/.github/dependabot.yml +0 -0
  38. {confkit-1.3.1 → confkit-2.0.0}/.github/workflows/coverage.yml +0 -0
  39. {confkit-1.3.1 → confkit-2.0.0}/.github/workflows/docs.yml +0 -0
  40. {confkit-1.3.1 → confkit-2.0.0}/.github/workflows/lint.yml +0 -0
  41. {confkit-1.3.1 → confkit-2.0.0}/.github/workflows/pypi.yml +0 -0
  42. {confkit-1.3.1 → confkit-2.0.0}/.github/workflows/release.yml +0 -0
  43. {confkit-1.3.1 → confkit-2.0.0}/.github/workflows/test.yml +0 -0
  44. {confkit-1.3.1 → confkit-2.0.0}/.gitignore +0 -0
  45. {confkit-1.3.1 → confkit-2.0.0}/.pre-commit-config.yaml +0 -0
  46. {confkit-1.3.1 → confkit-2.0.0}/.python-version +0 -0
  47. {confkit-1.3.1 → confkit-2.0.0}/.vscode/settings.json +0 -0
  48. {confkit-1.3.1 → confkit-2.0.0}/README.md +0 -0
  49. {confkit-1.3.1 → confkit-2.0.0}/docs/examples/argparse.md +0 -0
  50. {confkit-1.3.1 → confkit-2.0.0}/docs/examples/basic.md +0 -0
  51. {confkit-1.3.1 → confkit-2.0.0}/docs/examples/custom_data_type.md +0 -0
  52. {confkit-1.3.1 → confkit-2.0.0}/docs/examples/data_types.md +0 -0
  53. {confkit-1.3.1 → confkit-2.0.0}/docs/examples/decorators.md +0 -0
  54. {confkit-1.3.1 → confkit-2.0.0}/docs/examples/enums.md +0 -0
  55. {confkit-1.3.1 → confkit-2.0.0}/docs/examples/index.md +0 -0
  56. {confkit-1.3.1 → confkit-2.0.0}/docs/examples/list_types.md +0 -0
  57. {confkit-1.3.1 → confkit-2.0.0}/docs/examples/optional_values.md +0 -0
  58. {confkit-1.3.1 → confkit-2.0.0}/docs/index.md +0 -0
  59. {confkit-1.3.1 → confkit-2.0.0}/docs/reference/config.md +0 -0
  60. {confkit-1.3.1 → confkit-2.0.0}/docs/reference/data_types.md +0 -0
  61. {confkit-1.3.1 → confkit-2.0.0}/docs/reference/exceptions.md +0 -0
  62. {confkit-1.3.1 → confkit-2.0.0}/docs/reference/notes_two_instances.md +0 -0
  63. {confkit-1.3.1 → confkit-2.0.0}/docs/usage.md +0 -0
  64. {confkit-1.3.1 → confkit-2.0.0}/examples/api.ini +0 -0
  65. {confkit-1.3.1 → confkit-2.0.0}/examples/argparse_example.py +0 -0
  66. {confkit-1.3.1 → confkit-2.0.0}/examples/args.ini +0 -0
  67. {confkit-1.3.1 → confkit-2.0.0}/examples/basic.py +0 -0
  68. {confkit-1.3.1 → confkit-2.0.0}/examples/config.ini +0 -0
  69. {confkit-1.3.1 → confkit-2.0.0}/examples/custom_data_type.py +0 -0
  70. {confkit-1.3.1 → confkit-2.0.0}/examples/data_types.py +0 -0
  71. {confkit-1.3.1 → confkit-2.0.0}/examples/database.ini +0 -0
  72. {confkit-1.3.1 → confkit-2.0.0}/examples/decorators.py +0 -0
  73. {confkit-1.3.1 → confkit-2.0.0}/examples/enums.py +0 -0
  74. {confkit-1.3.1 → confkit-2.0.0}/examples/example.json +0 -0
  75. {confkit-1.3.1 → confkit-2.0.0}/examples/example.toml +0 -0
  76. {confkit-1.3.1 → confkit-2.0.0}/examples/example.yaml +0 -0
  77. {confkit-1.3.1 → confkit-2.0.0}/examples/file_change_event.py +0 -0
  78. {confkit-1.3.1 → confkit-2.0.0}/examples/list_types.py +0 -0
  79. {confkit-1.3.1 → confkit-2.0.0}/examples/multiple_configs.py +0 -0
  80. {confkit-1.3.1 → confkit-2.0.0}/examples/nested_config.py +0 -0
  81. {confkit-1.3.1 → confkit-2.0.0}/examples/nested_example.ini +0 -0
  82. {confkit-1.3.1 → confkit-2.0.0}/examples/nested_example.json +0 -0
  83. {confkit-1.3.1 → confkit-2.0.0}/examples/nested_example.toml +0 -0
  84. {confkit-1.3.1 → confkit-2.0.0}/examples/nested_example.yaml +0 -0
  85. {confkit-1.3.1 → confkit-2.0.0}/examples/optional_values.py +0 -0
  86. {confkit-1.3.1 → confkit-2.0.0}/examples/other_file_types.py +0 -0
  87. {confkit-1.3.1 → confkit-2.0.0}/examples/pydantic_example.py +0 -0
  88. {confkit-1.3.1 → confkit-2.0.0}/examples/references.py +0 -0
  89. {confkit-1.3.1 → confkit-2.0.0}/examples/url_example.py +0 -0
  90. {confkit-1.3.1 → confkit-2.0.0}/mkdocs.yml +0 -0
  91. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/ext/parsers.py +0 -0
  92. {confkit-1.3.1 → confkit-2.0.0}/src/confkit/py.typed +0 -0
  93. {confkit-1.3.1 → confkit-2.0.0}/tests/__init__.py +0 -0
  94. {confkit-1.3.1 → confkit-2.0.0}/tests/conftest.py +0 -0
  95. {confkit-1.3.1 → confkit-2.0.0}/tests/test_env_parser.py +0 -0
  96. {confkit-1.3.1 → confkit-2.0.0}/tests/test_examples_run.py +0 -0
  97. {confkit-1.3.1 → confkit-2.0.0}/tests/test_nested_config.py +0 -0
  98. {confkit-1.3.1 → confkit-2.0.0}/tests/test_pydantic_models.py +0 -0
@@ -30,9 +30,10 @@ jobs:
30
30
 
31
31
  - name: Determine version bump
32
32
  id: detect-bump
33
+ env:
34
+ LABELS: ${{ github.event.pull_request.labels.*.name }}
33
35
  run: |
34
36
  # Check PR labels to determine bump type
35
- LABELS="${{ github.event.pull_request.labels.*.name }}"
36
37
  if echo "$LABELS" | grep -q "Major"; then
37
38
  echo "bump_type=major" >> $env:GITHUB_OUTPUT
38
39
  elif echo "$LABELS" | grep -q "Minor"; then
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: confkit
3
- Version: 1.3.1
3
+ Version: 2.0.0
4
4
  Summary: Lightweight and Easy to use configuration manager for Python projects
5
5
  Author: HEROgold
6
6
  Requires-Python: >=3.11
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "confkit"
3
- version = "1.3.1"
3
+ version = "2.0.0"
4
4
  description = "Lightweight and Easy to use configuration manager for Python projects"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,8 +1,13 @@
1
- lint.select = ["ALL"]
2
- lint.ignore = ["D203", "G004"]
3
1
  line-length = 128
4
2
  target-version = "py311"
5
3
 
4
+ [lint]
5
+ select = ["ALL"]
6
+ ignore = ["D203", "G004"]
7
+
8
+ [lint.isort]
9
+ required-imports = ["from __future__ import annotations"]
10
+
6
11
  [lint.per-file-ignores]
7
12
  "test_*.py" = ["S101", "D103", "SLF001", "D100","D101", "FBT003", "PLR2004", "N814", "S105"]
8
13
  "examples/*.py" = ["ALL"]
@@ -2,6 +2,7 @@
2
2
 
3
3
  It includes the Config class and various data types used for configuration values.
4
4
  """
5
+ from __future__ import annotations
5
6
 
6
7
  from .config import Config, ConfigContainerMeta
7
8
  from .data_types import (
@@ -60,6 +60,7 @@ class Config(Generic[VT]):
60
60
  _parser: ConfkitParser = UNSET
61
61
  _file: Path = UNSET
62
62
  _has_read_config: bool = False
63
+ _data_type: BaseDataType[VT]
63
64
 
64
65
  if TYPE_CHECKING:
65
66
  # Overloads for type checkers to understand the different settings of the Config descriptors.
@@ -69,13 +70,13 @@ class Config(Generic[VT]):
69
70
  def __init__(self, default: VT) -> None: ...
70
71
  # Specify the states of optional explicitly for type checkers.
71
72
  @overload
72
- def __init__(self: Config[VT], default: VT, *, optional: Literal[False]) -> None: ...
73
+ def __init__(self: Config[OVT], default: OVT, *, optional: Literal[False]) -> None: ...
73
74
  @overload
74
- def __init__(self: Config[VT], default: BaseDataType[VT], *, optional: Literal[False]) -> None: ...
75
+ def __init__(self: Config[OVT], default: BaseDataType[OVT], *, optional: Literal[False]) -> None: ...
75
76
  @overload
76
- def __init__(self: Config[VT | None], default: VT, *, optional: Literal[True]) -> None: ...
77
+ def __init__(self: Config[OVT | None], default: OVT, *, optional: Literal[True]) -> None: ...
77
78
  @overload
78
- def __init__(self: Config[VT | None], default: BaseDataType[VT], *, optional: Literal[True]) -> None: ...
79
+ def __init__(self: Config[OVT | None], default: BaseDataType[OVT], *, optional: Literal[True]) -> None: ...
79
80
 
80
81
  def __init__(
81
82
  self,
@@ -144,9 +145,7 @@ class Config(Generic[VT]):
144
145
 
145
146
  def convert(self, value: str) -> VT:
146
147
  """Convert the value to the desired type using the given converter method."""
147
- # Ignore the type error of VT, type checkers don't like None as an option
148
- # We handle it using the `optional` flag, or using Optional DataType. so we can safely ignore it.
149
- return self._data_type.convert(value) # type: ignore[reportReturnType]
148
+ return self._data_type.convert(value)
150
149
 
151
150
  @staticmethod
152
151
  def _warn_base_class_usage() -> None:
@@ -6,10 +6,10 @@ import enum
6
6
  from abc import ABC, abstractmethod
7
7
  from collections.abc import Sequence
8
8
  from datetime import UTC, date, datetime, time, timedelta, tzinfo
9
+ from enum import Enum as dEnum
9
10
  from enum import IntEnum as dIntEnum
10
11
  from enum import IntFlag as dIntFlag
11
12
  from enum import StrEnum as dStrEnum
12
- from enum import Enum as dEnum
13
13
  from typing import ClassVar, Generic, NotRequired, Required, TypedDict, TypeVar, Unpack, cast, overload
14
14
 
15
15
  from confkit.sentinels import UNSET
@@ -205,12 +205,17 @@ class NoneType(BaseDataType[None]):
205
205
  """Initialize the NoneType data type."""
206
206
  super().__init__(None)
207
207
 
208
- def convert(self, value: str) -> bool: # type: ignore[reportIncompatibleMethodOverride]
209
- """Convert a string value to None."""
210
- # Ignore type exception as convert should return True/False for NoneType
211
- # to determine if we have a valid null value or not.
208
+ def is_valid(self, value: str) -> bool:
209
+ """Check if the provided string value is in the set of null values."""
212
210
  return value.casefold().strip() in NoneType.null_values
213
211
 
212
+ def convert(self, value: str) -> None:
213
+ """Convert a string value to None."""
214
+ if self.is_valid(value):
215
+ return
216
+ msg = f"Value '{value}' is not a valid null value. Expected one of: {', '.join(NoneType.null_values)}."
217
+ raise ValueError(msg)
218
+
214
219
 
215
220
  class String(BaseDataType[str]):
216
221
  """A config value that is a string."""
@@ -361,8 +366,8 @@ class Optional(BaseDataType[T | None], Generic[T]):
361
366
 
362
367
  def convert(self, value: str) -> T | None:
363
368
  """Convert a string value to the optional type."""
364
- if self._none_type.convert(value):
365
- return None
369
+ if self._none_type.is_valid(value):
370
+ return self._none_type.convert(value)
366
371
  return self._data_type.convert(value)
367
372
 
368
373
  def validate(self) -> bool:
@@ -1,4 +1,6 @@
1
1
  """Module for custom exceptions used in the confkit package."""
2
+ from __future__ import annotations
3
+
2
4
 
3
5
  class InvalidDefaultError(ValueError):
4
6
  """Raised when the default value is not set or invalid."""
@@ -4,5 +4,6 @@ Modules inside this package may rely on optional extras. They are intentionally
4
4
  not imported eagerly so users can access the pieces they installed without
5
5
  pulling in additional dependencies.
6
6
  """
7
+ from __future__ import annotations
7
8
 
8
9
  __all__: list[str] = []
@@ -1,7 +1,13 @@
1
1
  """Helper utilities for working with Pydantic models and confkit."""
2
+ from __future__ import annotations
2
3
 
3
- try:
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
4
7
  from pydantic import BaseModel
8
+
9
+ try:
10
+ import pydantic # noqa: F401
5
11
  except ImportError as exc:
6
12
  msg = (
7
13
  "confkit.ext.pydantic requires the optional 'pydantic' extra. "
@@ -2,6 +2,7 @@
2
2
 
3
3
  It allows you to create relative references to other configuration files
4
4
  """
5
+ from __future__ import annotations
5
6
 
6
7
  from pathlib import Path
7
8
  from typing import Any
@@ -177,7 +177,6 @@ class EnvParser(ConfkitParser):
177
177
  @override
178
178
  def set_section(self, section: str) -> None:
179
179
  """EnvParser has no sections, this is a no-op."""
180
- pass # noqa: PIE790
181
180
 
182
181
  @override
183
182
  def set_option(self, option: str) -> None:
@@ -188,7 +187,6 @@ class EnvParser(ConfkitParser):
188
187
  @override
189
188
  def add_section(self, section: str) -> None:
190
189
  """EnvParser has no sections, this is a no-op."""
191
- pass # noqa: PIE790
192
190
 
193
191
  @override
194
192
  def set(self, section: str, option: str, value: object) -> None:
@@ -1,4 +1,5 @@
1
1
  """Private sentinel for missing values."""
2
+ from __future__ import annotations
2
3
 
3
4
  from typing import Any
4
5
 
@@ -1,5 +1,10 @@
1
1
  """A simple file watcher to monitor changes in a file."""
2
- from pathlib import Path
2
+ from __future__ import annotations
3
+
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from pathlib import Path
3
8
 
4
9
 
5
10
  class FileWatcher: # noqa: D101
@@ -1,4 +1,6 @@
1
1
  """Test suite for the Config class and its descriptors."""
2
+ from __future__ import annotations
3
+
2
4
  import enum
3
5
  from enum import auto
4
6
  from pathlib import Path
@@ -105,10 +107,10 @@ class Test:
105
107
  binary_value = Config(Binary(0b101010))
106
108
  binary_value_2 = Config(Binary(b"101010"))
107
109
  # Test invalid setups (Type checkers like pyright will raise errors here)
108
- none_int = Config(Integer(None)) # type: ignore[reportArgumentType]
109
- none_string = Config(String(None)) # type: ignore[reportArgumentType]
110
- none_boolean = Config(Boolean(None)) # type: ignore[reportArgumentType]
111
- none_float = Config(Float(None)) # type: ignore[reportArgumentType]
110
+ none_int = Config(Integer(None))
111
+ none_string = Config(String(None))
112
+ none_boolean = Config(Boolean(None))
113
+ none_float = Config(Float(None))
112
114
  # Custom data type tests
113
115
  custom = Config(Integer(0))
114
116
  optional_custom = Config(Optional(Integer(0)))
@@ -153,7 +155,7 @@ class Test:
153
155
  list_of_paths = Config(List(["/path/to/file1", "/path/to/file2"]))
154
156
 
155
157
  @Config.with_setting(number)
156
- def setting(self, **kwargs): # type: ignore[reportMissingParameterType] # noqa: ANN003, ANN201, D102
158
+ def setting(self, **kwargs): # noqa: ANN003, ANN201, D102
157
159
  return kwargs.get("number")
158
160
 
159
161
  @pytest.mark.order(0)
@@ -183,19 +185,19 @@ def test_int_flag() -> None:
183
185
 
184
186
  def test_init_no_args() -> None:
185
187
  with pytest.raises((InvalidDefaultError, InvalidConverterError)):
186
- Config() # type: ignore[reportCallIssue]
188
+ Config()
187
189
 
188
190
 
189
191
  def test_init_no_default() -> None:
190
192
  with pytest.raises(InvalidDefaultError):
191
- Config() # type: ignore[reportCallIssue]
193
+ Config()
192
194
 
193
195
 
194
196
  def test_optional_validate_none_value() -> None:
195
197
  """Test Optional.validate when value is None."""
196
198
  optional_type = Optional(String("default"))
197
199
  # Use monkey patching to set internal state
198
- with patch.object(optional_type._data_type, "value", None): # type: ignore[attr-defined]
200
+ with patch.object(optional_type._data_type, "value", None):
199
201
  assert optional_type.validate() is True
200
202
 
201
203
 
@@ -2,10 +2,11 @@
2
2
 
3
3
  This also contains the test cases where specific settings are expected.
4
4
  """
5
+ from __future__ import annotations
6
+
5
7
  import tempfile
6
- from collections.abc import Callable
7
8
  from pathlib import Path
8
- from typing import Never, ParamSpec, TypeVar
9
+ from typing import TYPE_CHECKING, Never, ParamSpec, TypeVar
9
10
 
10
11
  import pytest
11
12
  from hypothesis import given
@@ -17,6 +18,9 @@ from confkit.exceptions import InvalidConverterError, InvalidDefaultError
17
18
  from confkit.parsers import IniParser
18
19
  from confkit.sentinels import UNSET
19
20
 
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Callable
23
+
20
24
  F = TypeVar("F")
21
25
  P = ParamSpec("P")
22
26
 
@@ -60,7 +64,7 @@ def test_config_validate_parser_unset() -> None:
60
64
  def test_config_converter_is_unset() -> None:
61
65
  """Test validate_strict_type when converter is UNSET - Line 154 in config.py."""
62
66
  class MockDataType(BaseDataType[str]):
63
- def convert(self, value: str) -> Never: # ty: ignore[invalid-return-type]
67
+ def convert(self, value: str) -> Never:
64
68
  ...
65
69
 
66
70
  # Create a temporary isolated environment
@@ -148,7 +152,7 @@ def test_config_type_mismatch_error() -> None:
148
152
  class WrongTypeDataType(BaseDataType[str]):
149
153
  def __init__(self, default: str) -> None:
150
154
  super().__init__(default)
151
- def convert(self, value: str) -> int: # type: ignore[override]
155
+ def convert(self, value: str) -> int:
152
156
  return int(value)
153
157
 
154
158
  # Create a temporary isolated environment
@@ -3,9 +3,10 @@
3
3
  These are usually not safe enough to test using a single file, at the same time.ArithmeticError
4
4
  These get their own test file.
5
5
  """
6
- from collections.abc import Callable
6
+ from __future__ import annotations
7
+
7
8
  from pathlib import Path
8
- from typing import ParamSpec, TypeVar
9
+ from typing import TYPE_CHECKING, ParamSpec, TypeVar
9
10
 
10
11
  import pytest
11
12
  from hypothesis import given
@@ -15,6 +16,9 @@ from confkit.config import Config as OG
15
16
  from confkit.parsers import IniParser
16
17
  from confkit.sentinels import UNSET
17
18
 
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Callable
21
+
18
22
  F = TypeVar("F")
19
23
  P = ParamSpec("P")
20
24
 
@@ -25,7 +29,7 @@ def config_new(func: Callable[P, F]) -> Callable[P, F]:
25
29
  """Save and restore the _file and _parser attributes for the Config."""
26
30
  def inner(*args: P.args, **kwargs: P.kwargs) -> F:
27
31
  restores = (getattr(Config, "_file", UNSET), getattr(Config, "_parser", UNSET))
28
- new_file = Path(f"{func.__name__}.ini") # ty: ignore[unresolved-attribute]
32
+ new_file = Path(f"{func.__name__}.ini")
29
33
  new_file.touch(exist_ok=True)
30
34
  Config._file = new_file
31
35
  Config._parser = IniParser()
@@ -1,4 +1,6 @@
1
1
  """Test Config.detect_parser behavior for different file extensions."""
2
+ from __future__ import annotations
3
+
2
4
  from pathlib import Path
3
5
 
4
6
  import pytest
@@ -1,4 +1,6 @@
1
1
  """Tests for data type classes in confkit.data_types."""
2
+ from __future__ import annotations
3
+
2
4
  from datetime import UTC, date, datetime, time, timedelta
3
5
  from typing import Final
4
6
 
@@ -1,4 +1,6 @@
1
1
  """Tests for enum types displaying allowed values."""
2
+ from __future__ import annotations
3
+
2
4
  import enum
3
5
  from enum import IntEnum, IntFlag, StrEnum, auto
4
6
 
@@ -3,10 +3,11 @@
3
3
  Tests that StrEnum, IntEnum, IntFlag, and Enum defaults are automatically
4
4
  wrapped in their corresponding data type converters when used with Config.
5
5
  """
6
+ from __future__ import annotations
6
7
 
7
8
  import enum
8
9
  from enum import IntEnum, IntFlag, StrEnum
9
- from pathlib import Path
10
+ from typing import TYPE_CHECKING
10
11
 
11
12
  from confkit import Config as ConfigBase
12
13
  from confkit.data_types import Enum as ConfigEnum
@@ -15,6 +16,9 @@ from confkit.data_types import IntFlag as ConfigIntFlag
15
16
  from confkit.data_types import StrEnum as ConfigStrEnum
16
17
  from confkit.parsers import IniParser
17
18
 
19
+ if TYPE_CHECKING:
20
+ from pathlib import Path
21
+
18
22
 
19
23
  class LogLevel(StrEnum):
20
24
  """String-based enum for log levels."""
@@ -1,4 +1,5 @@
1
1
  """Test the List data type."""
2
+ from __future__ import annotations
2
3
 
3
4
  import pytest
4
5
 
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from pathlib import Path
2
4
 
3
5
  from confkit.config import Config as OG
@@ -1,5 +1,7 @@
1
1
 
2
2
  """Tests for MsgspecParser (unified YAML/JSON/TOML parser)."""
3
+ from __future__ import annotations
4
+
3
5
  import pathlib
4
6
  import tempfile
5
7
 
@@ -1,4 +1,6 @@
1
1
  """Tests for MsgspecParser.get fallback and string branch in write."""
2
+ from __future__ import annotations
3
+
2
4
  import io
3
5
 
4
6
  from confkit.ext.parsers import MsgspecParser
@@ -1,4 +1,6 @@
1
1
  """Test MsgspecParser behavior when msgspec is not installed."""
2
+ from __future__ import annotations
3
+
2
4
  import re
3
5
  import sys
4
6
 
@@ -1,11 +1,16 @@
1
1
  """Tests for MsgspecParser.read edge cases."""
2
- from pathlib import Path
2
+ from __future__ import annotations
3
+
4
+ from typing import TYPE_CHECKING
3
5
 
4
6
  import msgspec
5
7
  import pytest
6
8
 
7
9
  from confkit.ext.parsers import MsgspecParser
8
10
 
11
+ if TYPE_CHECKING:
12
+ from pathlib import Path
13
+
9
14
 
10
15
  def test_read_file_does_not_exist(tmp_path: Path) -> None:
11
16
  file = tmp_path / "notfound.json"
@@ -1,4 +1,6 @@
1
1
  """Tests for MsgspecParser section/option methods."""
2
+ from __future__ import annotations
3
+
2
4
  from confkit.ext.parsers import MsgspecParser
3
5
 
4
6
 
@@ -1,4 +1,6 @@
1
1
  """Tests for multiple configurations."""
2
+ from __future__ import annotations
3
+
2
4
  from pathlib import Path
3
5
 
4
6
  import hypothesis
@@ -1,4 +1,6 @@
1
1
  """Tests for on_file_change method in Config descriptor."""
2
+ from __future__ import annotations
3
+
2
4
  from pathlib import Path
3
5
  from typing import Any, ClassVar
4
6
 
@@ -1,4 +1,5 @@
1
1
  """Test cases for the UNSET sentinel from confkit.sentinels."""
2
+ from __future__ import annotations
2
3
 
3
4
  from confkit.sentinels import UNSET
4
5
 
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from pathlib import Path
2
4
  from tempfile import TemporaryDirectory
3
5
 
@@ -1,4 +1,5 @@
1
1
  """Test suite for testing supposedly unreachable code paths in data_types.py."""
2
+ from __future__ import annotations
2
3
 
3
4
  import pytest
4
5
  from hypothesis import given
@@ -10,7 +11,7 @@ from confkit.data_types import BaseDataType
10
11
  class DataType(BaseDataType[str]):
11
12
  """Basic DataType that doesn't do anything."""
12
13
 
13
- def convert(self, value: str) -> str: ... # ty: ignore[invalid-return-type]. # noqa: D102
14
+ def convert(self, value: str) -> str: ... # noqa: D102
14
15
 
15
16
  class MockBase:
16
17
  """Mock base class without __args__."""
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes