stinger-python-utils 0.1.3__tar.gz → 0.1.4__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.
@@ -0,0 +1,29 @@
1
+ name: Python Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - name: Install UV
18
+ uses: astral-sh/setup-uv@v7
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ - name: Install project
22
+ run: |
23
+ uv sync --locked --all-extras --dev
24
+ - name: Run unit tests
25
+ run: |
26
+ uv run pytest
27
+ - name: Mypy Type Check
28
+ run: |
29
+ uv run mypy --check-untyped-defs ./src/
@@ -0,0 +1,23 @@
1
+ name: Python 3.7 Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-22.04
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - name: Setup Python
15
+ uses: actions/setup-python@v6
16
+ with:
17
+ python-version: '3.7'
18
+ pip-install: ".[dev]"
19
+ - name: Run unit tests
20
+ run: |
21
+ pip install pytest
22
+ python -m pytest
23
+
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: stinger-python-utils
3
+ Version: 0.1.4
4
+ Summary: Common utilities for Stinger Python services.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.7
8
+ Requires-Dist: pydantic>=2.5.3
9
+ Requires-Dist: pyqttier>=0.2.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # stinger-python-utils
13
+
14
+ Shared utilities for Stinger Python services, providing convenient message creation for MQTT communication.
15
+
16
+
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ uv add stinger-python-utils
22
+ ```
23
+
24
+ ## MessageCreator
25
+
26
+ `MessageCreator` is a utility class for creating MQTT messages with standardized properties and payloads.
27
+
28
+ ### Basic Usage
29
+
30
+ ```python
31
+ from pydantic import BaseModel
32
+ from stinger_python_utils.message_creator import MessageCreator
33
+
34
+ class MyPayload(BaseModel):
35
+ name: str
36
+ value: int
37
+
38
+ payload = MyPayload(name="test", value=42)
39
+ message = MessageCreator.signal_message("my/topic", payload)
40
+ ```
41
+
42
+ ### Methods
43
+
44
+ | Method | Purpose | Return Code |
45
+ |--------|---------|-------------|
46
+ | `signal_message(topic, payload)` | Send a signal with one-time delivery | QoS 1, no retain |
47
+ | `status_message(topic, payload, expiry_seconds)` | Send status that expires | QoS 1, retained, with expiry |
48
+ | `error_response_message(topic, return_code, correlation_id, debug_info)` | Error response to a request | QoS 1, user properties: `ReturnCode` |
49
+ | `response_message(topic, payload, return_code, correlation_id)` | Successful response to a request | QoS 1, user properties: `ReturnCode` |
50
+ | `property_state_message(topic, payload, state_version)` | Publish property state | QoS 1, retained, JSON content type |
51
+ | `property_update_request_message(topic, payload, version, response_topic, correlation_id)` | Request property update | QoS 1, user property: `PropertyVersion` |
52
+ | `property_response_message(topic, payload, version, return_code, correlation_id, debug_info)` | Respond to property update | QoS 1, user properties: `ReturnCode`, `PropertyVersion` |
53
+ | `request_message(topic, payload, response_topic, correlation_id)` | Send a request (auto-generates UUID if no correlation_id) | QoS 1, auto correlation ID |
54
+
55
+ ### Example: Request/Response Pattern
56
+
57
+ ```python
58
+ from pydantic import BaseModel
59
+ from stinger_python_utils.message_creator import MessageCreator
60
+
61
+ class Request(BaseModel):
62
+ action: str
63
+
64
+ request = Request(action="start")
65
+ msg = MessageCreator.request_message(
66
+ "devices/cmd",
67
+ request,
68
+ response_topic="devices/response"
69
+ )
70
+ # Returns a Message with auto-generated correlation ID
71
+ ```
72
+
73
+ ### Example: Error Response
74
+
75
+ ```python
76
+ msg = MessageCreator.error_response_message(
77
+ "devices/response",
78
+ return_code=500,
79
+ correlation_id="req-123",
80
+ debug_info="Device not found"
81
+ )
82
+ # User properties include: ReturnCode=500, DebugInfo=Device not found
83
+ ```
84
+
@@ -0,0 +1,73 @@
1
+ # stinger-python-utils
2
+
3
+ Shared utilities for Stinger Python services, providing convenient message creation for MQTT communication.
4
+
5
+
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ uv add stinger-python-utils
11
+ ```
12
+
13
+ ## MessageCreator
14
+
15
+ `MessageCreator` is a utility class for creating MQTT messages with standardized properties and payloads.
16
+
17
+ ### Basic Usage
18
+
19
+ ```python
20
+ from pydantic import BaseModel
21
+ from stinger_python_utils.message_creator import MessageCreator
22
+
23
+ class MyPayload(BaseModel):
24
+ name: str
25
+ value: int
26
+
27
+ payload = MyPayload(name="test", value=42)
28
+ message = MessageCreator.signal_message("my/topic", payload)
29
+ ```
30
+
31
+ ### Methods
32
+
33
+ | Method | Purpose | Return Code |
34
+ |--------|---------|-------------|
35
+ | `signal_message(topic, payload)` | Send a signal with one-time delivery | QoS 1, no retain |
36
+ | `status_message(topic, payload, expiry_seconds)` | Send status that expires | QoS 1, retained, with expiry |
37
+ | `error_response_message(topic, return_code, correlation_id, debug_info)` | Error response to a request | QoS 1, user properties: `ReturnCode` |
38
+ | `response_message(topic, payload, return_code, correlation_id)` | Successful response to a request | QoS 1, user properties: `ReturnCode` |
39
+ | `property_state_message(topic, payload, state_version)` | Publish property state | QoS 1, retained, JSON content type |
40
+ | `property_update_request_message(topic, payload, version, response_topic, correlation_id)` | Request property update | QoS 1, user property: `PropertyVersion` |
41
+ | `property_response_message(topic, payload, version, return_code, correlation_id, debug_info)` | Respond to property update | QoS 1, user properties: `ReturnCode`, `PropertyVersion` |
42
+ | `request_message(topic, payload, response_topic, correlation_id)` | Send a request (auto-generates UUID if no correlation_id) | QoS 1, auto correlation ID |
43
+
44
+ ### Example: Request/Response Pattern
45
+
46
+ ```python
47
+ from pydantic import BaseModel
48
+ from stinger_python_utils.message_creator import MessageCreator
49
+
50
+ class Request(BaseModel):
51
+ action: str
52
+
53
+ request = Request(action="start")
54
+ msg = MessageCreator.request_message(
55
+ "devices/cmd",
56
+ request,
57
+ response_topic="devices/response"
58
+ )
59
+ # Returns a Message with auto-generated correlation ID
60
+ ```
61
+
62
+ ### Example: Error Response
63
+
64
+ ```python
65
+ msg = MessageCreator.error_response_message(
66
+ "devices/response",
67
+ return_code=500,
68
+ correlation_id="req-123",
69
+ debug_info="Device not found"
70
+ )
71
+ # User properties include: ReturnCode=500, DebugInfo=Device not found
72
+ ```
73
+
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "stinger-python-utils"
7
- version = "0.1.3"
7
+ version = "0.1.4"
8
8
  description = "Common utilities for Stinger Python services."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -21,4 +21,5 @@ managed = true
21
21
  dev = [
22
22
  "black>=23.3.0",
23
23
  "mypy>=1.4.1",
24
+ "pytest>=7.0.0",
24
25
  ]
@@ -60,7 +60,7 @@ class MessageCreator:
60
60
  def response_message(
61
61
  cls,
62
62
  response_topic: str,
63
- response_obj: BaseModel | str | bytes,
63
+ response_obj: Union[BaseModel, str, bytes],
64
64
  return_code: int,
65
65
  correlation_id: Union[str, bytes, None] = None,
66
66
  ) -> Message:
@@ -0,0 +1 @@
1
+ """Test package configuration"""
@@ -0,0 +1,208 @@
1
+ """Tests for MessageCreator class"""
2
+ import pytest
3
+ from pydantic import BaseModel
4
+ from stinger_python_utils.message_creator import MessageCreator
5
+
6
+
7
+ class SamplePayload(BaseModel):
8
+ """Sample payload for testing"""
9
+ name: str
10
+ value: int
11
+
12
+
13
+ class TestSignalMessage:
14
+ def test_signal_message_creates_message(self):
15
+ """Test that signal_message creates a message with correct properties"""
16
+ payload = SamplePayload(name="test", value=42)
17
+ message = MessageCreator.signal_message("test/topic", payload)
18
+
19
+ assert message.topic == "test/topic"
20
+ assert message.qos == 1
21
+ assert message.retain is False
22
+ assert b'"name":"test"' in message.payload
23
+
24
+
25
+ class TestStatusMessage:
26
+ def test_status_message_creates_retained_message(self):
27
+ """Test that status_message creates a retained message with expiry"""
28
+ payload = SamplePayload(name="status", value=100)
29
+ message = MessageCreator.status_message("status/topic", payload, 3600)
30
+
31
+ assert message.topic == "status/topic"
32
+ assert message.qos == 1
33
+ assert message.retain is True
34
+ assert message.message_expiry_interval == 3600
35
+
36
+
37
+ class TestErrorResponseMessage:
38
+ def test_error_response_with_string_correlation_id(self):
39
+ """Test error response with string correlation ID"""
40
+ message = MessageCreator.error_response_message(
41
+ "error/topic",
42
+ return_code=500,
43
+ correlation_id="abc-123"
44
+ )
45
+
46
+ assert message.topic == "error/topic"
47
+ assert message.qos == 1
48
+ assert message.retain is False
49
+ assert message.user_properties["ReturnCode"] == "500"
50
+ assert message.correlation_data == b"abc-123"
51
+
52
+ def test_error_response_with_bytes_correlation_id(self):
53
+ """Test error response with bytes correlation ID"""
54
+ message = MessageCreator.error_response_message(
55
+ "error/topic",
56
+ return_code=400,
57
+ correlation_id=b"xyz-789"
58
+ )
59
+
60
+ assert message.correlation_data == b"xyz-789"
61
+ assert message.user_properties["ReturnCode"] == "400"
62
+
63
+ def test_error_response_with_debug_info(self):
64
+ """Test error response includes debug info"""
65
+ message = MessageCreator.error_response_message(
66
+ "error/topic",
67
+ return_code=500,
68
+ debug_info="Something went wrong"
69
+ )
70
+
71
+ assert message.user_properties["DebugInfo"] == "Something went wrong"
72
+
73
+
74
+ class TestResponseMessage:
75
+ def test_response_message_with_pydantic_model(self):
76
+ """Test response message with Pydantic model"""
77
+ payload = SamplePayload(name="response", value=200)
78
+ message = MessageCreator.response_message(
79
+ "response/topic",
80
+ payload,
81
+ return_code=200,
82
+ correlation_id="req-123"
83
+ )
84
+
85
+ assert message.topic == "response/topic"
86
+ assert message.qos == 1
87
+ assert message.user_properties["ReturnCode"] == "200"
88
+ assert message.correlation_data == b"req-123"
89
+
90
+ def test_response_message_with_string(self):
91
+ """Test response message with string payload"""
92
+ message = MessageCreator.response_message(
93
+ "response/topic",
94
+ "success",
95
+ return_code=200
96
+ )
97
+
98
+ assert message.payload == b"success"
99
+
100
+ def test_response_message_with_bytes(self):
101
+ """Test response message with bytes payload"""
102
+ message = MessageCreator.response_message(
103
+ "response/topic",
104
+ b"binary data",
105
+ return_code=200
106
+ )
107
+
108
+ assert message.payload == b"binary data"
109
+
110
+
111
+ class TestPropertyStateMessage:
112
+ def test_property_state_message_basic(self):
113
+ """Test property state message creation"""
114
+ payload = SamplePayload(name="property", value=42)
115
+ message = MessageCreator.property_state_message("property/state", payload)
116
+
117
+ assert message.topic == "property/state"
118
+ assert message.qos == 1
119
+ assert message.retain is True
120
+ assert message.content_type == "application/json"
121
+
122
+ def test_property_state_message_with_version(self):
123
+ """Test property state message with version"""
124
+ payload = SamplePayload(name="property", value=42)
125
+ message = MessageCreator.property_state_message(
126
+ "property/state",
127
+ payload,
128
+ state_version=5
129
+ )
130
+
131
+ assert message.user_properties["PropertyVersion"] == "5"
132
+
133
+
134
+ class TestPropertyUpdateRequestMessage:
135
+ def test_property_update_request_message(self):
136
+ """Test property update request message"""
137
+ payload = SamplePayload(name="new_value", value=99)
138
+ message = MessageCreator.property_update_request_message(
139
+ "property/update",
140
+ payload,
141
+ version="1.0",
142
+ response_topic="property/response",
143
+ correlation_id="update-123"
144
+ )
145
+
146
+ assert message.topic == "property/update"
147
+ assert message.response_topic == "property/response"
148
+ assert message.user_properties["PropertyVersion"] == "1.0"
149
+ assert message.correlation_data == b"update-123"
150
+
151
+
152
+ class TestPropertyResponseMessage:
153
+ def test_property_response_message(self):
154
+ """Test property response message"""
155
+ payload = SamplePayload(name="updated", value=99)
156
+ message = MessageCreator.property_response_message(
157
+ "property/response",
158
+ payload,
159
+ version="1.0",
160
+ return_code=200,
161
+ correlation_id="update-123"
162
+ )
163
+
164
+ assert message.topic == "property/response"
165
+ assert message.user_properties["ReturnCode"] == "200"
166
+ assert message.user_properties["PropertyVersion"] == "1.0"
167
+ assert message.correlation_data == b"update-123"
168
+
169
+ def test_property_response_message_with_debug_info(self):
170
+ """Test property response message with debug info"""
171
+ payload = SamplePayload(name="updated", value=99)
172
+ message = MessageCreator.property_response_message(
173
+ "property/response",
174
+ payload,
175
+ version="1.0",
176
+ return_code=500,
177
+ debug_info="Update failed"
178
+ )
179
+
180
+ assert message.user_properties["DebugInfo"] == "Update failed"
181
+
182
+
183
+ class TestRequestMessage:
184
+ def test_request_message_auto_correlation_id(self):
185
+ """Test request message generates correlation ID if not provided"""
186
+ payload = SamplePayload(name="request", value=1)
187
+ message = MessageCreator.request_message(
188
+ "request/topic",
189
+ payload,
190
+ response_topic="response/topic"
191
+ )
192
+
193
+ assert message.topic == "request/topic"
194
+ assert message.response_topic == "response/topic"
195
+ assert message.correlation_data is not None
196
+ assert len(message.correlation_data) > 0
197
+
198
+ def test_request_message_with_correlation_id(self):
199
+ """Test request message with provided correlation ID"""
200
+ payload = SamplePayload(name="request", value=1)
201
+ message = MessageCreator.request_message(
202
+ "request/topic",
203
+ payload,
204
+ response_topic="response/topic",
205
+ correlation_id="req-456"
206
+ )
207
+
208
+ assert message.correlation_data == b"req-456"
@@ -261,6 +261,20 @@ wheels = [
261
261
  { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
262
262
  ]
263
263
 
264
+ [[package]]
265
+ name = "exceptiongroup"
266
+ version = "1.3.1"
267
+ source = { registry = "https://pypi.org/simple" }
268
+ dependencies = [
269
+ { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" },
270
+ { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" },
271
+ { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.13'" },
272
+ ]
273
+ sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
274
+ wheels = [
275
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
276
+ ]
277
+
264
278
  [[package]]
265
279
  name = "importlib-metadata"
266
280
  version = "6.7.0"
@@ -274,6 +288,43 @@ wheels = [
274
288
  { url = "https://files.pythonhosted.org/packages/ff/94/64287b38c7de4c90683630338cf28f129decbba0a44f0c6db35a873c73c4/importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5", size = 22934, upload-time = "2023-06-18T21:44:33.441Z" },
275
289
  ]
276
290
 
291
+ [[package]]
292
+ name = "iniconfig"
293
+ version = "2.0.0"
294
+ source = { registry = "https://pypi.org/simple" }
295
+ resolution-markers = [
296
+ "python_full_version < '3.8'",
297
+ ]
298
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" }
299
+ wheels = [
300
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" },
301
+ ]
302
+
303
+ [[package]]
304
+ name = "iniconfig"
305
+ version = "2.1.0"
306
+ source = { registry = "https://pypi.org/simple" }
307
+ resolution-markers = [
308
+ "python_full_version == '3.9.*'",
309
+ "python_full_version == '3.8.*'",
310
+ ]
311
+ sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
312
+ wheels = [
313
+ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
314
+ ]
315
+
316
+ [[package]]
317
+ name = "iniconfig"
318
+ version = "2.3.0"
319
+ source = { registry = "https://pypi.org/simple" }
320
+ resolution-markers = [
321
+ "python_full_version >= '3.10'",
322
+ ]
323
+ sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
324
+ wheels = [
325
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
326
+ ]
327
+
277
328
  [[package]]
278
329
  name = "librt"
279
330
  version = "0.7.4"
@@ -646,6 +697,46 @@ wheels = [
646
697
  { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" },
647
698
  ]
648
699
 
700
+ [[package]]
701
+ name = "pluggy"
702
+ version = "1.2.0"
703
+ source = { registry = "https://pypi.org/simple" }
704
+ resolution-markers = [
705
+ "python_full_version < '3.8'",
706
+ ]
707
+ dependencies = [
708
+ { name = "importlib-metadata", marker = "python_full_version < '3.8'" },
709
+ ]
710
+ sdist = { url = "https://files.pythonhosted.org/packages/8a/42/8f2833655a29c4e9cb52ee8a2be04ceac61bcff4a680fb338cbd3d1e322d/pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3", size = 61613, upload-time = "2023-06-21T09:12:28.745Z" }
711
+ wheels = [
712
+ { url = "https://files.pythonhosted.org/packages/51/32/4a79112b8b87b21450b066e102d6608907f4c885ed7b04c3fdb085d4d6ae/pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", size = 17695, upload-time = "2023-06-21T09:12:27.397Z" },
713
+ ]
714
+
715
+ [[package]]
716
+ name = "pluggy"
717
+ version = "1.5.0"
718
+ source = { registry = "https://pypi.org/simple" }
719
+ resolution-markers = [
720
+ "python_full_version == '3.8.*'",
721
+ ]
722
+ sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" }
723
+ wheels = [
724
+ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" },
725
+ ]
726
+
727
+ [[package]]
728
+ name = "pluggy"
729
+ version = "1.6.0"
730
+ source = { registry = "https://pypi.org/simple" }
731
+ resolution-markers = [
732
+ "python_full_version >= '3.10'",
733
+ "python_full_version == '3.9.*'",
734
+ ]
735
+ sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
736
+ wheels = [
737
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
738
+ ]
739
+
649
740
  [[package]]
650
741
  name = "pydantic"
651
742
  version = "2.5.3"
@@ -1066,6 +1157,15 @@ wheels = [
1066
1157
  { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
1067
1158
  ]
1068
1159
 
1160
+ [[package]]
1161
+ name = "pygments"
1162
+ version = "2.19.2"
1163
+ source = { registry = "https://pypi.org/simple" }
1164
+ sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
1165
+ wheels = [
1166
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
1167
+ ]
1168
+
1069
1169
  [[package]]
1070
1170
  name = "pyqttier"
1071
1171
  version = "0.2.0"
@@ -1078,6 +1178,89 @@ wheels = [
1078
1178
  { url = "https://files.pythonhosted.org/packages/c5/95/7c249d48c6f7bfde659606570955534c09dc5559151ce581cb963c97a181/pyqttier-0.2.0-py3-none-any.whl", hash = "sha256:2da862cbd3485842b380f0006598e064edc3376a0ac36a7cee8c90ba9b0664a2", size = 9827, upload-time = "2025-12-16T23:22:04.22Z" },
1079
1179
  ]
1080
1180
 
1181
+ [[package]]
1182
+ name = "pytest"
1183
+ version = "7.4.4"
1184
+ source = { registry = "https://pypi.org/simple" }
1185
+ resolution-markers = [
1186
+ "python_full_version < '3.8'",
1187
+ ]
1188
+ dependencies = [
1189
+ { name = "colorama", marker = "python_full_version < '3.8' and sys_platform == 'win32'" },
1190
+ { name = "exceptiongroup", marker = "python_full_version < '3.8'" },
1191
+ { name = "importlib-metadata", marker = "python_full_version < '3.8'" },
1192
+ { name = "iniconfig", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" },
1193
+ { name = "packaging", version = "24.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" },
1194
+ { name = "pluggy", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" },
1195
+ { name = "tomli", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" },
1196
+ ]
1197
+ sdist = { url = "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", size = 1357116, upload-time = "2023-12-31T12:00:18.035Z" }
1198
+ wheels = [
1199
+ { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287, upload-time = "2023-12-31T12:00:13.963Z" },
1200
+ ]
1201
+
1202
+ [[package]]
1203
+ name = "pytest"
1204
+ version = "8.3.5"
1205
+ source = { registry = "https://pypi.org/simple" }
1206
+ resolution-markers = [
1207
+ "python_full_version == '3.8.*'",
1208
+ ]
1209
+ dependencies = [
1210
+ { name = "colorama", marker = "python_full_version == '3.8.*' and sys_platform == 'win32'" },
1211
+ { name = "exceptiongroup", marker = "python_full_version == '3.8.*'" },
1212
+ { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" },
1213
+ { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" },
1214
+ { name = "pluggy", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" },
1215
+ { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" },
1216
+ ]
1217
+ sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" }
1218
+ wheels = [
1219
+ { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" },
1220
+ ]
1221
+
1222
+ [[package]]
1223
+ name = "pytest"
1224
+ version = "8.4.2"
1225
+ source = { registry = "https://pypi.org/simple" }
1226
+ resolution-markers = [
1227
+ "python_full_version == '3.9.*'",
1228
+ ]
1229
+ dependencies = [
1230
+ { name = "colorama", marker = "python_full_version == '3.9.*' and sys_platform == 'win32'" },
1231
+ { name = "exceptiongroup", marker = "python_full_version == '3.9.*'" },
1232
+ { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" },
1233
+ { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" },
1234
+ { name = "pluggy", version = "1.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" },
1235
+ { name = "pygments", marker = "python_full_version == '3.9.*'" },
1236
+ { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" },
1237
+ ]
1238
+ sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
1239
+ wheels = [
1240
+ { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
1241
+ ]
1242
+
1243
+ [[package]]
1244
+ name = "pytest"
1245
+ version = "9.0.2"
1246
+ source = { registry = "https://pypi.org/simple" }
1247
+ resolution-markers = [
1248
+ "python_full_version >= '3.10'",
1249
+ ]
1250
+ dependencies = [
1251
+ { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
1252
+ { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" },
1253
+ { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
1254
+ { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
1255
+ { name = "pluggy", version = "1.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
1256
+ { name = "pygments", marker = "python_full_version >= '3.10'" },
1257
+ { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
1258
+ ]
1259
+ sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
1260
+ wheels = [
1261
+ { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
1262
+ ]
1263
+
1081
1264
  [[package]]
1082
1265
  name = "pytokens"
1083
1266
  version = "0.3.0"
@@ -1089,7 +1272,7 @@ wheels = [
1089
1272
 
1090
1273
  [[package]]
1091
1274
  name = "stinger-python-utils"
1092
- version = "0.1.2"
1275
+ version = "0.1.4"
1093
1276
  source = { editable = "." }
1094
1277
  dependencies = [
1095
1278
  { name = "pydantic", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" },
@@ -1107,6 +1290,10 @@ dev = [
1107
1290
  { name = "mypy", version = "1.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" },
1108
1291
  { name = "mypy", version = "1.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" },
1109
1292
  { name = "mypy", version = "1.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
1293
+ { name = "pytest", version = "7.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" },
1294
+ { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" },
1295
+ { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" },
1296
+ { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
1110
1297
  ]
1111
1298
 
1112
1299
  [package.metadata]
@@ -1119,6 +1306,7 @@ requires-dist = [
1119
1306
  dev = [
1120
1307
  { name = "black", specifier = ">=23.3.0" },
1121
1308
  { name = "mypy", specifier = ">=1.4.1" },
1309
+ { name = "pytest", specifier = ">=7.0.0" },
1122
1310
  ]
1123
1311
 
1124
1312
  [[package]]
@@ -1,13 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: stinger-python-utils
3
- Version: 0.1.3
4
- Summary: Common utilities for Stinger Python services.
5
- License-Expression: MIT
6
- License-File: LICENSE
7
- Requires-Python: >=3.7
8
- Requires-Dist: pydantic>=2.5.3
9
- Requires-Dist: pyqttier>=0.2.0
10
- Description-Content-Type: text/markdown
11
-
12
- # stinger-python-utils
13
- Common code needed for stinger python generations
@@ -1,2 +0,0 @@
1
- # stinger-python-utils
2
- Common code needed for stinger python generations