intersect-sdk-common 0.9.4__tar.gz → 0.9.6__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 (31) hide show
  1. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/PKG-INFO +1 -1
  2. intersect_sdk_common-0.9.6/pyproject.toml +188 -0
  3. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/__init__.py +13 -2
  4. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/control_plane_manager.py +12 -3
  5. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/topic_handler.py +7 -3
  6. intersect_sdk_common-0.9.6/src/intersect_sdk_common/utils/version_resolver.py +70 -0
  7. intersect_sdk_common-0.9.4/pyproject.toml +0 -185
  8. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/README.md +0 -0
  9. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/config.py +0 -0
  10. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/constants.py +0 -0
  11. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/__init__.py +0 -0
  12. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/brokers/__init__.py +0 -0
  13. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/brokers/amqp_client.py +0 -0
  14. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/brokers/broker_client.py +0 -0
  15. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/brokers/mqtt_client.py +0 -0
  16. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/custom_binary_protocol.py +0 -0
  17. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/definitions.py +0 -0
  18. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/messages/__init__.py +0 -0
  19. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/messages/event.py +0 -0
  20. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/messages/lifecycle.py +0 -0
  21. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/control_plane/messages/userspace.py +0 -0
  22. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/core_definitions.py +0 -0
  23. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/data_plane/__init__.py +0 -0
  24. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/data_plane/data_plane_manager.py +0 -0
  25. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/data_plane/minio_utils.py +0 -0
  26. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/exceptions.py +0 -0
  27. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/logger.py +0 -0
  28. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/py.typed +0 -0
  29. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/utils/__init__.py +0 -0
  30. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/utils/multi_flag_thread_event.py +0 -0
  31. {intersect_sdk_common-0.9.4 → intersect_sdk_common-0.9.6}/src/intersect_sdk_common/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: intersect-sdk-common
3
- Version: 0.9.4
3
+ Version: 0.9.6
4
4
  Summary: Python SDK to interact with INTERSECT
5
5
  Keywords: intersect
6
6
  Author: Lance Drane, Marshall McDonnell, Seth Hitefield, Andrew Ayres, Gregory Cage, Jesse McGaha, Robert Smith, Gavin Wiggins, Michael Brim, Rick Archibald, Addi Malviya Thakur
@@ -0,0 +1,188 @@
1
+ [project]
2
+ name = "intersect-sdk-common"
3
+ description = "Python SDK to interact with INTERSECT"
4
+ authors = [
5
+ { name = "Lance Drane", email = "dranelt@ornl.gov" },
6
+ { name = "Marshall McDonnell", email = "mcdonnellmt@ornl.gov" },
7
+ { name = "Seth Hitefield", email = "hitefieldsd@ornl.gov" },
8
+ { name = "Andrew Ayres", email = "ayresaf@ornl.gov" },
9
+ { name = "Gregory Cage", email = "cagege@ornl.gov" },
10
+ { name = "Jesse McGaha", email = "mcgahajr@ornl.gov" },
11
+ { name = "Robert Smith", email = "smithrw@ornl.gov" },
12
+ { name = "Gavin Wiggins", email = "wigginsg@ornl.gov" },
13
+ { name = "Michael Brim", email = "brimmj@ornl.gov" },
14
+ { name = "Rick Archibald", email = "archibaldrk@ornl.gov" },
15
+ { name = "Addi Malviya Thakur", email = "malviyaa@ornl.gov" },
16
+ ]
17
+ readme = "README.md"
18
+ license = { text = "BSD-3-Clause" }
19
+ requires-python = ">=3.10,<4.0"
20
+ keywords = ["intersect"]
21
+ version = "0.9.6"
22
+ classifiers = [
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ ]
29
+ dependencies = [
30
+ "pydantic>=2.7.0",
31
+ "paho-mqtt>=2.1.0,<3.0.0",
32
+ "pika>=1.3.2,<2.0.0",
33
+ "minio>=7.2.3",
34
+ ]
35
+
36
+ [dependency-groups]
37
+ dev = [
38
+ "pre-commit>=3.3.1",
39
+ "ruff==0.12.7",
40
+ "mypy>=1.10.0",
41
+ "codespell>=2.3.0",
42
+ "pytest>=7.3.2",
43
+ "pytest-cov>=4.1.0",
44
+ "httpretty>=1.1.4",
45
+ ]
46
+
47
+ [build-system]
48
+ requires = ["uv_build>=0.10.2,<0.11.0"]
49
+ build-backend = "uv_build"
50
+
51
+ [tool.ruff]
52
+ line-length = 100
53
+ format = { quote-style = 'single' }
54
+
55
+ [tool.ruff.lint]
56
+ isort = { known-first-party = ['src'] }
57
+ pydocstyle = { convention = 'google' }
58
+ flake8-quotes = { inline-quotes = 'single', multiline-quotes = 'double' }
59
+ mccabe = { max-complexity = 20 }
60
+ pylint = { max-args = 10, max-branches = 20, max-returns = 15, max-statements = 75 }
61
+ # pyflakes and the relevant pycodestyle rules are already configured
62
+ extend-select = [
63
+ 'C90', # mccabe complexity
64
+ 'I', # isort
65
+ 'N', # pep8-naming
66
+ 'D', # pydocstyle
67
+ 'UP', # pyupgrade
68
+ 'YTT', # flake8-2020
69
+ 'ANN', # flake8-annotations
70
+ 'ASYNC', # flake8-async
71
+ 'S', # flake8-bandit
72
+ 'BLE', # flake8-blind-except
73
+ 'B', # flake8-bugbear
74
+ 'A', # flake8-builtins
75
+ 'COM', # flake8-commas
76
+ 'C4', # flake8-comprehensions
77
+ 'DTZ', # flake8-datetimez
78
+ 'T10', # flake8-debugger
79
+ 'EM', # flake8-error-message
80
+ 'FA', # flake8-future-annotations
81
+ 'ISC', # flake8-implicit-string-concat
82
+ 'ICN', # flake8-import-conventions
83
+ 'G', # flake8-logging-format
84
+ 'INP', # flake8-no-pep420
85
+ 'PIE', # flake8-PIE
86
+ 'T20', # flake8-T20
87
+ 'PYI', # flake8-pyi
88
+ 'PT', # flake8-pytest-style
89
+ 'Q', # flake8-quotes
90
+ 'RSE', # flake8-raise
91
+ 'RET', # flake8-return
92
+ 'SLF', # flake8-self
93
+ 'SLOT', # flake8-slots
94
+ 'SIM', # flake8-simplify
95
+ 'TC', # flake8-type-checking
96
+ 'ARG', # flake8-unused-arguments
97
+ 'PTH', # flake8-use-pathlib
98
+ 'PGH', # pygrep-hooks
99
+ 'PL', # pylint
100
+ 'TRY', # tryceratops
101
+ 'FLY', # flynt
102
+ 'RUF', # RUFF additional rules
103
+ 'INT', # flake8-gettext
104
+ ]
105
+ # If you're seeking to disable a rule, first consider whether the rule is overbearing, or if it should only be turned off for your usecase.
106
+ ignore = [
107
+ 'COM812', # formatter, handled by Ruff format
108
+ 'ISC001', # formatter, handled by Ruff format
109
+ 'SIM105', # "with contextlib.suppress():" is slower than try-except-pass
110
+ 'ANN401', # allow explicit "Any" typing, use with care
111
+ 'PLR2004', # allow "magic numbers"
112
+ ]
113
+
114
+ [tool.ruff.lint.flake8-type-checking]
115
+ runtime-evaluated-base-classes = ["pydantic.BaseModel"]
116
+ runtime-evaluated-decorators = [
117
+ "pydantic.dataclasses.dataclass",
118
+ "pydantic.validate_call",
119
+ ]
120
+
121
+ [tool.ruff.lint.extend-per-file-ignores]
122
+ '__init__.py' = [
123
+ 'F401', # __init__.py commonly has unused imports
124
+ 'TC004', # do lazy imports when importing from the base module
125
+ ]
126
+ 'docs/*' = [
127
+ 'D', # the documentation folder does not need documentation
128
+ 'INP001', # docs are not a namespace package
129
+ ]
130
+ 'tests/*' = [
131
+ 'S101', # allow assert statements in tests
132
+ 'S106', # don't care about credentials in tests
133
+ 'S311', # don't care about cryptographic security in tests
134
+ 'SLF001', # allow private member access in tests
135
+ 'ANN', # tests in general don't need types, unless they are runtime types.
136
+ 'ARG', # allow unused parameters in tests
137
+ 'D', # ignore documentation in tests
138
+ 'RUF012', # permit "mutable" class attributes to not be annotated with typing.ClassVar (these shouldn't be mutated anyways...)
139
+ ]
140
+
141
+ # see https://mypy.readthedocs.io/en/stable/config_file.html for a complete reference
142
+ [tool.mypy]
143
+ strict = true
144
+ ignore_missing_imports = true # don't require typing for library stubs if they don't exist
145
+ disallow_untyped_decorators = false # this is needed for library decorator compatibility, i.e. "retrying"
146
+ plugins = ["pydantic.mypy"]
147
+
148
+ [tool.pydantic-mypy]
149
+ init_forbid_extra = true
150
+ init_typed = true
151
+ warn_required_dynamic_aliases = true
152
+ warn_untyped_fields = true
153
+
154
+ [tool.pytest.ini_options]
155
+ log_cli = true
156
+ addopts = "-ra"
157
+
158
+ [tool.coverage.report]
159
+ omit = [
160
+ '*__init__*', # __init__ files should just re-export other classes and functions
161
+ ]
162
+ exclude_also = [
163
+ 'pragma: no-cover', # standard
164
+ 'if (typing\\.)?TYPE_CHECKING:', # type checking blocks are not executed in coverage, but we don't care
165
+ '@(abc\\.)?abstractmethod', # don't try to cover abstract methods
166
+ "class .*\\bProtocol\\):", # don't cover protocol classes (similar to abstract classes)
167
+ 'raise NotImplementedError', # it's not implemented so shouldn't be covered
168
+ 'except.* ImportError', # these are usually used to throw a "friendlier" error and are not really worth testing
169
+ ]
170
+
171
+
172
+ #[project.scripts]
173
+ #test-all = "pytest tests/ --cov=src/intersect_sdk_common/ --cov-fail-under=80 --cov-report=html:reports/htmlcov/ --cov-report=xml:reports/coverage_report.xml --junitxml=reports/junit.xml"
174
+ #test-all-debug = "pytest tests/ --cov=src/intersect_sdk_common/ --cov-fail-under=80 --cov-report=html:reports/htmlcov/ --cov-report=xml:reports/coverage_report.xml --junitxml=reports/junit.xml -s"
175
+ #test-unit = "pytest tests/unit --cov=src/intersect_sdk_common/"
176
+ #test-e2e = "pytest tests/e2e --cov=src/intersect_sdk_common/"
177
+ #lint-docs = "sphinx-build -W --keep-going docs docs/_build"
178
+ #lint-format = "ruff format"
179
+ #lint-ruff = "ruff check --fix"
180
+ #lint-mypy = "mypy src/intersect_sdk_common/"
181
+ #lint-spelling = "codespell -w docs examples src tests *.md -S docs/_build"
182
+
183
+ [tool.codespell]
184
+ # Ref: https://github.com/codespell-project/codespell#using-a-config-file
185
+ skip = '.git*,*.lock,.venv,.*cache/*,./docs/_build/*'
186
+ check-hidden = true
187
+ # ignore-regex = ''
188
+ # ignore-words-list = ''
@@ -21,8 +21,17 @@ if TYPE_CHECKING:
21
21
  from .control_plane.definitions import MessageCallback
22
22
  from .core_definitions import IntersectDataHandler, IntersectMimeType
23
23
  from .data_plane.data_plane_manager import DataPlaneManager
24
- from .exceptions import IntersectApplicationError, IntersectError, IntersectSetupError
25
- from .version import __version__, intersect_sdk_version_info, intersect_sdk_version_string
24
+ from .exceptions import (
25
+ IntersectApplicationError,
26
+ IntersectError,
27
+ IntersectSetupError,
28
+ )
29
+ from .utils.version_resolver import resolve_user_version
30
+ from .version import (
31
+ __version__,
32
+ intersect_sdk_version_info,
33
+ intersect_sdk_version_string,
34
+ )
26
35
 
27
36
  __all__ = (
28
37
  'ControlPlaneConfig',
@@ -41,6 +50,7 @@ __all__ = (
41
50
  '__version__',
42
51
  'intersect_sdk_version_info',
43
52
  'intersect_sdk_version_string',
53
+ 'resolve_user_version',
44
54
  )
45
55
 
46
56
  # PEP 562 stuff: do lazy imports for people who just want to import from the top-level module
@@ -62,6 +72,7 @@ __lazy_imports = {
62
72
  '__version__': '.version',
63
73
  'intersect_sdk_version_info': '.version',
64
74
  'intersect_sdk_version_string': '.version',
75
+ 'resolve_user_version': '.utils.version_resolver',
65
76
  }
66
77
 
67
78
 
@@ -10,14 +10,17 @@ from .brokers.broker_client import BrokerClient
10
10
  from .definitions import MessageCallback
11
11
  from .topic_handler import TopicHandler
12
12
 
13
- _CHANNEL_REGEX = re.compile(r'^[a-zA-Z0-9*/-]+[a-zA-Z0-9*#/-]$')
13
+ _CHANNEL_REGEX = re.compile(r'^[a-zA-Z0-9_*/-]+[a-zA-Z0-9_*#/-]$')
14
14
  """Allow for these patterns:
15
15
 
16
16
  - alphanumeric characters
17
17
  - hyphens
18
+ - underscores
18
19
  - `/` (topic separator; note that this is '.' on the broker)
19
20
  - `*` (wildcard for exactly one word)
20
21
  - `#` (wildcard for any number of words, but can ONLY appear at the end of the topic string, see section 4.7.1.2 at https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718107)
22
+
23
+ This should be permissive relative to other patterns. You must cover system/service/hierarchy regexes, capability regexes, and event name regexes.
21
24
  """
22
25
 
23
26
 
@@ -75,7 +78,11 @@ class ControlPlaneManager:
75
78
  self._wildcards: dict[str, TopicHandler] = {}
76
79
 
77
80
  def add_subscription_channel(
78
- self, channel: str, callbacks: set[MessageCallback], persist: bool, queue_name: str
81
+ self,
82
+ channel: str,
83
+ callbacks: set[MessageCallback],
84
+ persist: bool,
85
+ queue_name: str,
79
86
  ) -> None:
80
87
  """Start listening for messages on a channel on all configured brokers.
81
88
 
@@ -167,7 +174,9 @@ class ControlPlaneManager:
167
174
  return wildcard, topic_handler
168
175
  return None
169
176
 
170
- def get_all_subscription_channels(self) -> itertools.chain[tuple[str, TopicHandler]]:
177
+ def get_all_subscription_channels(
178
+ self,
179
+ ) -> itertools.chain[tuple[str, TopicHandler]]:
171
180
  """Get all subscription channels, including wildcard channels.
172
181
 
173
182
  This function is safe to call from the broker clients, as it does not mutate state.
@@ -23,7 +23,10 @@ class TopicHandler:
23
23
  """If this is a wildcard topic, the regex pattern to match incoming topics against (if no wildcards, this will be None)."""
24
24
 
25
25
  def __init__(
26
- self, topic_persist: bool, queue_name: str, regex_pattern: str | None = None
26
+ self,
27
+ topic_persist: bool,
28
+ queue_name: str,
29
+ regex_pattern: str | None = None,
27
30
  ) -> None:
28
31
  """Initialize a TopicHandler instance.
29
32
 
@@ -37,14 +40,15 @@ class TopicHandler:
37
40
  self.queue_name = queue_name
38
41
 
39
42
  if regex_pattern:
43
+ # the generated regex must be permissive and allow any non-special characters _CHANNEL_REGEX (ControlPlaneManager) allows
40
44
  regex_builder = ['^']
41
45
  for char in regex_pattern:
42
46
  if char == '*':
43
47
  # match a single word (do not include the separator)
44
- regex_builder.append('[a-zA-Z0-9-]+')
48
+ regex_builder.append('[a-zA-Z0-9_-]+')
45
49
  elif char == '#':
46
50
  # match any sequence of 0 or more words (make sure to allow for the separator)
47
- regex_builder.append('[a-zA-Z0-9/-]*')
51
+ regex_builder.append('[a-zA-Z0-9/_-]*')
48
52
  elif char == '/':
49
53
  # in-memory topics use '/' as the separator, it is the protocol handler's responsibility to convert topics to use '/' as the separator
50
54
  regex_builder.append('/')
@@ -0,0 +1,70 @@
1
+ """Handles version resolution via semantic versioning logic."""
2
+
3
+ from .. import (
4
+ IntersectDataHandler,
5
+ intersect_sdk_version_info,
6
+ intersect_sdk_version_string,
7
+ )
8
+ from ..logger import logger
9
+
10
+
11
+ def _resolve_user_version(
12
+ their_version: str,
13
+ their_source: str,
14
+ their_data_handler: IntersectDataHandler,
15
+ our_version: str,
16
+ our_version_info: tuple[int, int, int],
17
+ ) -> bool:
18
+ """Function which handles version resolution information.
19
+
20
+ Separated into private function for testing purposes
21
+ """
22
+ their_version_info = [int(x) for x in their_version.split('.')]
23
+
24
+ # logging rules: log "error" if it's definitely our fault, "warning" if it _might_ be our fault
25
+ if len(their_version_info) < 3:
26
+ logger.warning(f'Invalid version {their_version} received')
27
+ return False
28
+
29
+ if their_version_info[0] != our_version_info[0]:
30
+ logger.warning(
31
+ f'Problem with source {their_source}: Major version incompatibility between our SDK version {our_version} and their SDK version {their_version}'
32
+ )
33
+ return False
34
+ if our_version_info[0] == 0 and our_version_info[1] != their_version_info[1]:
35
+ logger.warning(
36
+ f'Problem with source {their_source}: Pre-release minor version incompatibility between our SDK version {our_version} and their SDK version {their_version}'
37
+ )
38
+ return False
39
+ if their_data_handler.value not in [e.value for e in IntersectDataHandler]:
40
+ logger.error(
41
+ f'Problem with source {their_source}: This adapter cannot handle data handler with code "{their_data_handler}", please upgrade to the latest intersect-sdk version.'
42
+ )
43
+ return False
44
+ # NOTE: consider implementing a content-type check here as well
45
+
46
+ return True
47
+
48
+
49
+ def resolve_user_version(
50
+ their_version: str,
51
+ their_source: str,
52
+ their_data_handler: IntersectDataHandler,
53
+ ) -> bool:
54
+ """This function handles all version compatibilities between our SDK version and an incoming message's SDK version.
55
+
56
+ Params
57
+ their_version - version information from headers
58
+ their_source - source information from headers
59
+ their_data_handler - data handler information from headers
60
+
61
+ Returns:
62
+ True if version resolution successful, false otherwise
63
+ """
64
+ return _resolve_user_version(
65
+ their_version,
66
+ their_source,
67
+ their_data_handler,
68
+ our_version=intersect_sdk_version_string,
69
+ our_version_info=intersect_sdk_version_info,
70
+ )
@@ -1,185 +0,0 @@
1
- [project]
2
- name = "intersect-sdk-common"
3
- description = "Python SDK to interact with INTERSECT"
4
- authors = [
5
- { name = "Lance Drane", email = "dranelt@ornl.gov" },
6
- { name = "Marshall McDonnell", email = "mcdonnellmt@ornl.gov" },
7
- { name = "Seth Hitefield", email = "hitefieldsd@ornl.gov" },
8
- { name = "Andrew Ayres", email = "ayresaf@ornl.gov" },
9
- { name = "Gregory Cage", email = "cagege@ornl.gov" },
10
- { name = "Jesse McGaha", email = "mcgahajr@ornl.gov" },
11
- { name = "Robert Smith", email = "smithrw@ornl.gov" },
12
- { name = "Gavin Wiggins", email = "wigginsg@ornl.gov" },
13
- { name = "Michael Brim", email = "brimmj@ornl.gov" },
14
- { name = "Rick Archibald", email = "archibaldrk@ornl.gov" },
15
- { name = "Addi Malviya Thakur", email = "malviyaa@ornl.gov" },
16
- ]
17
- readme = "README.md"
18
- license = { text = "BSD-3-Clause" }
19
- requires-python = ">=3.10,<4.0"
20
- keywords = ["intersect"]
21
- version = "0.9.4"
22
- classifiers = [
23
- "Programming Language :: Python :: 3",
24
- "Programming Language :: Python :: 3.10",
25
- "Programming Language :: Python :: 3.11",
26
- "Programming Language :: Python :: 3.12",
27
- "Programming Language :: Python :: 3.13",
28
- ]
29
- dependencies = [
30
- "pydantic>=2.7.0",
31
- "paho-mqtt>=2.1.0,<3.0.0",
32
- "pika>=1.3.2,<2.0.0",
33
- "minio>=7.2.3",
34
- ]
35
-
36
- [dependency-groups]
37
- dev = [
38
- "pre-commit>=3.3.1",
39
- "ruff==0.12.7",
40
- "mypy>=1.10.0",
41
- "codespell>=2.3.0",
42
- "pytest>=7.3.2",
43
- "pytest-cov>=4.1.0",
44
- "httpretty>=1.1.4",
45
- ]
46
-
47
- [build-system]
48
- requires = ["uv_build>=0.10.2,<0.11.0"]
49
- build-backend = "uv_build"
50
-
51
- [tool.ruff]
52
- line-length = 100
53
- format = { quote-style = 'single' }
54
-
55
- [tool.ruff.lint]
56
- isort = { known-first-party = ['src'] }
57
- pydocstyle = { convention = 'google' }
58
- flake8-quotes = { inline-quotes = 'single', multiline-quotes = 'double' }
59
- mccabe = { max-complexity = 20 }
60
- pylint = { max-args = 10, max-branches = 20, max-returns = 15, max-statements = 75 }
61
- # pyflakes and the relevant pycodestyle rules are already configured
62
- extend-select = [
63
- 'C90', # mccabe complexity
64
- 'I', # isort
65
- 'N', # pep8-naming
66
- 'D', # pydocstyle
67
- 'UP', # pyupgrade
68
- 'YTT', # flake8-2020
69
- 'ANN', # flake8-annotations
70
- 'ASYNC', # flake8-async
71
- 'S', # flake8-bandit
72
- 'BLE', # flake8-blind-except
73
- 'B', # flake8-bugbear
74
- 'A', # flake8-builtins
75
- 'COM', # flake8-commas
76
- 'C4', # flake8-comprehensions
77
- 'DTZ', # flake8-datetimez
78
- 'T10', # flake8-debugger
79
- 'EM', # flake8-error-message
80
- 'FA', # flake8-future-annotations
81
- 'ISC', # flake8-implicit-string-concat
82
- 'ICN', # flake8-import-conventions
83
- 'G', # flake8-logging-format
84
- 'INP', # flake8-no-pep420
85
- 'PIE', # flake8-PIE
86
- 'T20', # flake8-T20
87
- 'PYI', # flake8-pyi
88
- 'PT', # flake8-pytest-style
89
- 'Q', # flake8-quotes
90
- 'RSE', # flake8-raise
91
- 'RET', # flake8-return
92
- 'SLF', # flake8-self
93
- 'SLOT', # flake8-slots
94
- 'SIM', # flake8-simplify
95
- 'TC', # flake8-type-checking
96
- 'ARG', # flake8-unused-arguments
97
- 'PTH', # flake8-use-pathlib
98
- 'PGH', # pygrep-hooks
99
- 'PL', # pylint
100
- 'TRY', # tryceratops
101
- 'FLY', # flynt
102
- 'RUF', # RUFF additional rules
103
- 'INT', # flake8-gettext
104
- ]
105
- # If you're seeking to disable a rule, first consider whether the rule is overbearing, or if it should only be turned off for your usecase.
106
- ignore = [
107
- 'COM812', # formatter, handled by Ruff format
108
- 'ISC001', # formatter, handled by Ruff format
109
- 'SIM105', # "with contextlib.suppress():" is slower than try-except-pass
110
- 'ANN401', # allow explicit "Any" typing, use with care
111
- 'PLR2004', # allow "magic numbers"
112
- ]
113
-
114
- [tool.ruff.lint.flake8-type-checking]
115
- runtime-evaluated-base-classes = ["pydantic.BaseModel"]
116
- runtime-evaluated-decorators = ["pydantic.dataclasses.dataclass","pydantic.validate_call"]
117
-
118
- [tool.ruff.lint.extend-per-file-ignores]
119
- '__init__.py' = [
120
- 'F401', # __init__.py commonly has unused imports
121
- 'TC004', # do lazy imports when importing from the base module
122
- ]
123
- 'docs/*' = [
124
- 'D', # the documentation folder does not need documentation
125
- 'INP001', # docs are not a namespace package
126
- ]
127
- 'tests/*' = [
128
- 'S101', # allow assert statements in tests
129
- 'S106', # don't care about credentials in tests
130
- 'S311', # don't care about cryptographic security in tests
131
- 'SLF001', # allow private member access in tests
132
- 'ANN', # tests in general don't need types, unless they are runtime types.
133
- 'ARG', # allow unused parameters in tests
134
- 'D', # ignore documentation in tests
135
- 'RUF012', # permit "mutable" class attributes to not be annotated with typing.ClassVar (these shouldn't be mutated anyways...)
136
- ]
137
-
138
- # see https://mypy.readthedocs.io/en/stable/config_file.html for a complete reference
139
- [tool.mypy]
140
- strict = true
141
- ignore_missing_imports = true # don't require typing for library stubs if they don't exist
142
- disallow_untyped_decorators = false # this is needed for library decorator compatibility, i.e. "retrying"
143
- plugins = ["pydantic.mypy"]
144
-
145
- [tool.pydantic-mypy]
146
- init_forbid_extra = true
147
- init_typed = true
148
- warn_required_dynamic_aliases = true
149
- warn_untyped_fields = true
150
-
151
- [tool.pytest.ini_options]
152
- log_cli = true
153
- addopts = "-ra"
154
-
155
- [tool.coverage.report]
156
- omit = [
157
- '*__init__*', # __init__ files should just re-export other classes and functions
158
- ]
159
- exclude_also = [
160
- 'pragma: no-cover', # standard
161
- 'if (typing\\.)?TYPE_CHECKING:', # type checking blocks are not executed in coverage, but we don't care
162
- '@(abc\\.)?abstractmethod', # don't try to cover abstract methods
163
- "class .*\\bProtocol\\):", # don't cover protocol classes (similar to abstract classes)
164
- 'raise NotImplementedError', # it's not implemented so shouldn't be covered
165
- 'except.* ImportError', # these are usually used to throw a "friendlier" error and are not really worth testing
166
- ]
167
-
168
-
169
- #[project.scripts]
170
- #test-all = "pytest tests/ --cov=src/intersect_sdk_common/ --cov-fail-under=80 --cov-report=html:reports/htmlcov/ --cov-report=xml:reports/coverage_report.xml --junitxml=reports/junit.xml"
171
- #test-all-debug = "pytest tests/ --cov=src/intersect_sdk_common/ --cov-fail-under=80 --cov-report=html:reports/htmlcov/ --cov-report=xml:reports/coverage_report.xml --junitxml=reports/junit.xml -s"
172
- #test-unit = "pytest tests/unit --cov=src/intersect_sdk_common/"
173
- #test-e2e = "pytest tests/e2e --cov=src/intersect_sdk_common/"
174
- #lint-docs = "sphinx-build -W --keep-going docs docs/_build"
175
- #lint-format = "ruff format"
176
- #lint-ruff = "ruff check --fix"
177
- #lint-mypy = "mypy src/intersect_sdk_common/"
178
- #lint-spelling = "codespell -w docs examples src tests *.md -S docs/_build"
179
-
180
- [tool.codespell]
181
- # Ref: https://github.com/codespell-project/codespell#using-a-config-file
182
- skip = '.git*,*.lock,.venv,.*cache/*,./docs/_build/*'
183
- check-hidden = true
184
- # ignore-regex = ''
185
- # ignore-words-list = ''