python-corekit 0.1.0__py3-none-any.whl

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 (125) hide show
  1. corekit/__init__.py +0 -0
  2. corekit/api/__init__.py +9 -0
  3. corekit/api/handler.py +76 -0
  4. corekit/api/responses.py +40 -0
  5. corekit/api/routers.py +115 -0
  6. corekit/concurrency/__init__.py +9 -0
  7. corekit/concurrency/decorators.py +72 -0
  8. corekit/concurrency/thread_local.py +99 -0
  9. corekit/concurrency/worker.py +65 -0
  10. corekit/config/__init__.py +47 -0
  11. corekit/config/loader.py +153 -0
  12. corekit/config/settings.py +161 -0
  13. corekit/config/sources.py +125 -0
  14. corekit/connections/__init__.py +31 -0
  15. corekit/connections/connectable.py +212 -0
  16. corekit/connections/decorators.py +92 -0
  17. corekit/connections/redis/__init__.py +7 -0
  18. corekit/connections/redis/connection.py +239 -0
  19. corekit/connections/registry.py +80 -0
  20. corekit/connections/sql/__init__.py +10 -0
  21. corekit/connections/sql/connection.py +342 -0
  22. corekit/connections/sql/fields/__init__.py +7 -0
  23. corekit/connections/sql/fields/jsonb.py +67 -0
  24. corekit/connections/sql/migration/__init__.py +57 -0
  25. corekit/connections/sql/migration/base.py +40 -0
  26. corekit/connections/sql/migration/operations.py +416 -0
  27. corekit/connections/sql/migration/registry.py +166 -0
  28. corekit/connections/sql/migration/table.py +27 -0
  29. corekit/connections/sql/query.py +68 -0
  30. corekit/connections/sql/table.py +96 -0
  31. corekit/constants.py +45 -0
  32. corekit/crypto/__init__.py +1 -0
  33. corekit/crypto/constants.py +7 -0
  34. corekit/crypto/enum.py +11 -0
  35. corekit/crypto/hasher.py +89 -0
  36. corekit/data/__init__.py +81 -0
  37. corekit/data/dataset.py +340 -0
  38. corekit/data/expressions/__init__.py +46 -0
  39. corekit/data/expressions/comparison.py +252 -0
  40. corekit/data/expressions/expression.py +98 -0
  41. corekit/data/record.py +147 -0
  42. corekit/data/stats.py +157 -0
  43. corekit/decorators/__init__.py +2 -0
  44. corekit/decorators/exception_handling.py +43 -0
  45. corekit/decorators/warnings.py +35 -0
  46. corekit/docker/__init__.py +7 -0
  47. corekit/docker/watchdog.py +222 -0
  48. corekit/etl/__init__.py +44 -0
  49. corekit/etl/connection.py +44 -0
  50. corekit/etl/extract/__init__.py +0 -0
  51. corekit/etl/extract/extractor.py +48 -0
  52. corekit/etl/extract/schemas.py +18 -0
  53. corekit/etl/load/__init__.py +0 -0
  54. corekit/etl/load/loader.py +53 -0
  55. corekit/etl/load/schemas.py +33 -0
  56. corekit/etl/orchestrator.py +201 -0
  57. corekit/etl/schemas.py +22 -0
  58. corekit/etl/transform/__init__.py +0 -0
  59. corekit/etl/transform/schemas.py +15 -0
  60. corekit/etl/transform/transformer.py +28 -0
  61. corekit/events/__init__.py +38 -0
  62. corekit/events/enum.py +58 -0
  63. corekit/events/frames.py +51 -0
  64. corekit/events/models.py +23 -0
  65. corekit/events/publisher.py +75 -0
  66. corekit/events/reader.py +132 -0
  67. corekit/events/sse.py +109 -0
  68. corekit/events/websocket.py +97 -0
  69. corekit/exceptions/__init__.py +0 -0
  70. corekit/exceptions/base.py +45 -0
  71. corekit/exceptions/custom/__init__.py +0 -0
  72. corekit/exceptions/http/__init__.py +0 -0
  73. corekit/exceptions/http/exceptions.py +37 -0
  74. corekit/exceptions/types.py +17 -0
  75. corekit/files/__init__.py +25 -0
  76. corekit/files/base.py +117 -0
  77. corekit/files/enum.py +30 -0
  78. corekit/files/json.py +12 -0
  79. corekit/files/pickle.py +12 -0
  80. corekit/files/toml.py +43 -0
  81. corekit/http/__init__.py +0 -0
  82. corekit/http/client.py +176 -0
  83. corekit/http/exponential_backoff.py +100 -0
  84. corekit/http/response.py +12 -0
  85. corekit/log_monitor/__init__.py +23 -0
  86. corekit/log_monitor/constants.py +8 -0
  87. corekit/log_monitor/models.py +150 -0
  88. corekit/log_monitor/service.py +418 -0
  89. corekit/notifications/__init__.py +8 -0
  90. corekit/notifications/base.py +51 -0
  91. corekit/notifications/models.py +34 -0
  92. corekit/observability/__init__.py +21 -0
  93. corekit/observability/benchmarkable.py +12 -0
  94. corekit/observability/loggable.py +29 -0
  95. corekit/observability/timing/__init__.py +0 -0
  96. corekit/observability/timing/constants.py +1 -0
  97. corekit/observability/timing/split.py +20 -0
  98. corekit/observability/timing/timer.py +30 -0
  99. corekit/py.typed +0 -0
  100. corekit/registry/__init__.py +12 -0
  101. corekit/registry/registry.py +134 -0
  102. corekit/schemas/__init__.py +0 -0
  103. corekit/schemas/dataclasses/__init__.py +0 -0
  104. corekit/schemas/enum.py +49 -0
  105. corekit/schemas/models/__init__.py +0 -0
  106. corekit/schemas/models/arbitrary.py +11 -0
  107. corekit/schemas/models/date_models.py +18 -0
  108. corekit/schemas/pydantic/__init__.py +0 -0
  109. corekit/schemas/pydantic/fields.py +35 -0
  110. corekit/schemas/types.py +40 -0
  111. corekit/serialization/__init__.py +0 -0
  112. corekit/serialization/enum.py +21 -0
  113. corekit/serialization/serializable.py +42 -0
  114. corekit/serialization/serializer.py +179 -0
  115. corekit/utils/__init__.py +5 -0
  116. corekit/utils/ids.py +5 -0
  117. corekit/utils/raise_exc.py +8 -0
  118. corekit/utils/time.py +21 -0
  119. corekit/utils/validators.py +15 -0
  120. corekit/utils/void.py +8 -0
  121. python_corekit-0.1.0.dist-info/METADATA +417 -0
  122. python_corekit-0.1.0.dist-info/RECORD +125 -0
  123. python_corekit-0.1.0.dist-info/WHEEL +5 -0
  124. python_corekit-0.1.0.dist-info/licenses/LICENSE +21 -0
  125. python_corekit-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,8 @@
1
+ from typing import Any
2
+
3
+
4
+ def raise_exc(ex: Exception, *args: Any, **kwargs: Any) -> None:
5
+ """
6
+ Generic callback function that raises the exception
7
+ """
8
+ raise ex
corekit/utils/time.py ADDED
@@ -0,0 +1,21 @@
1
+ from datetime import UTC, datetime, timedelta
2
+
3
+
4
+ def time_now() -> datetime:
5
+ """
6
+ Helper for getting the current UTC time
7
+ """
8
+ return datetime.now(UTC)
9
+
10
+
11
+ def timedelta_now(delta_seconds: int | None = None, add_time: bool = False, **kwargs) -> datetime:
12
+ """
13
+ Helper for getting a quick timedelta
14
+ """
15
+ if delta_seconds is not None:
16
+ kwargs = {"seconds": delta_seconds}
17
+
18
+ delta = timedelta(**kwargs)
19
+ if add_time:
20
+ return time_now() + delta
21
+ return time_now() - delta
@@ -0,0 +1,15 @@
1
+ from typing import Any
2
+
3
+
4
+ def true_validator(*args: Any, **kwargs: Any) -> bool:
5
+ """
6
+ Generic placeholder validator function that always returns True
7
+ """
8
+ return True
9
+
10
+
11
+ def false_validator(*args: Any, **kwargs: Any) -> bool:
12
+ """
13
+ Generic placeholder validator function that always returns False
14
+ """
15
+ return False
corekit/utils/void.py ADDED
@@ -0,0 +1,8 @@
1
+ from typing import Any
2
+
3
+
4
+ def void(ex: Exception, *args: Any, **kwargs: Any) -> None:
5
+ """
6
+ Generic callback function that does nothing and returns nothing
7
+ """
8
+ pass
@@ -0,0 +1,417 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-corekit
3
+ Version: 0.1.0
4
+ Summary: Shared foundations for Python projects: logging, benchmarking, registries, FastAPI routers/handlers, data stores, and ETL
5
+ Author: Steven Jacobsen
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/stevejaker/corekit
8
+ Project-URL: Issues, https://github.com/stevejaker/corekit/issues
9
+ Keywords: fastapi,etl,homelab,logging,benchmarking
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: pydantic<3,>=2.10
19
+ Requires-Dist: pydantic-settings<3,>=2.0
20
+ Requires-Dist: fastapi<1,>=0.115
21
+ Requires-Dist: sqlmodel<0.1,>=0.0.16
22
+ Requires-Dist: SQLAlchemy<3,>=2.0
23
+ Requires-Dist: redis<7,>=5.0
24
+ Requires-Dist: httpx<1,>=0.27
25
+ Requires-Dist: docker<8,>=7.0
26
+ Requires-Dist: PyYAML<7,>=6.0
27
+ Requires-Dist: dill<0.5,>=0.3.8
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == "dev"
30
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
31
+ Requires-Dist: pytest-cov; extra == "dev"
32
+ Requires-Dist: ruff<0.16,>=0.15; extra == "dev"
33
+ Requires-Dist: mypy; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # corekit
37
+
38
+ Shared foundations for Python projects: structured logging, benchmarking,
39
+ registries, FastAPI routers with built-in handlers, an in-memory record store,
40
+ and ETL scaffolding.
41
+
42
+ Requires Python 3.11+.
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ pip install python-corekit
48
+ ```
49
+
50
+ No credentials, no SSH key, no token — which means a project that depends on
51
+ corekit can be cloned and built by anyone, including inside a Docker build.
52
+
53
+ Every dependency corekit needs is installed with it. There are no optional
54
+ extras to remember, and no import that fails because something was left out.
55
+
56
+ The distribution is `python-corekit`; the import is `corekit`. Pin a compatible
57
+ release rather than tracking whatever is newest:
58
+
59
+ ```
60
+ python-corekit~=0.1.0
61
+ ```
62
+
63
+ Before 1.0, the minor version carries breaking changes.
64
+
65
+ ## Logging
66
+
67
+ Inherit from `Loggable` and every instance gets a logger named after its class.
68
+
69
+ ```python
70
+ from corekit.observability import Loggable
71
+
72
+ class Importer(Loggable):
73
+ def run(self) -> None:
74
+ self.info("starting")
75
+ try:
76
+ ...
77
+ except Exception:
78
+ self.exception("import failed", exc_info=True)
79
+ ```
80
+
81
+ `Benchmarkable` adds split timing on top:
82
+
83
+ ```python
84
+ from corekit.observability import Benchmarkable
85
+
86
+ class Report(Benchmarkable):
87
+ def build(self) -> None:
88
+ self.timing() # start the clock
89
+ ...
90
+ self.timing("queried") # logs the time since the previous split
91
+ ```
92
+
93
+ ## Routers and handlers
94
+
95
+ A router and the handler holding its business logic travel together. Declare the
96
+ handler type in square brackets and the router builds it for you.
97
+
98
+ ```python
99
+ from corekit.api import BaseHandler, SmartRouter
100
+
101
+ class AdminHandler(BaseHandler):
102
+ """
103
+ Admin operations.
104
+
105
+ Handlers inherit logging and benchmarking, and register themselves by name.
106
+ """
107
+
108
+ async def list_users(self) -> list[str]:
109
+ return ["ada", "bob"]
110
+
111
+ router = SmartRouter[AdminHandler](route_prefix="/admin", tags=["Admin"])
112
+
113
+ @router.get("/users")
114
+ async def list_users() -> list[str]:
115
+ return await router.handler.list_users()
116
+ ```
117
+
118
+ Mount it with `router.include(app)` — the router adds itself, rather than the
119
+ application having to know about it.
120
+
121
+ The handler is built on first use, and `router.handler` can be assigned, so
122
+ tests can substitute a double without constructing the real thing:
123
+
124
+ ```python
125
+ router.handler = FakeAdminHandler()
126
+ ```
127
+
128
+ Handlers register themselves under a normalized name, so any spelling finds them:
129
+
130
+ ```python
131
+ BaseHandler.get_handler_by_name("admin_handler") # also "AdminHandler", "Admin Handler"
132
+ ```
133
+
134
+ ## Datasets
135
+
136
+ An in-memory, schema-fixed collection with composable filters. Standard library
137
+ only — no pandas.
138
+
139
+ ```python
140
+ from corekit.data import Dataset, Field
141
+
142
+ people = Dataset(id_key="name", schema=["name", "age"])
143
+ people.add({"name": "Ada", "age": 36})
144
+ people.add({"name": "Bob", "age": 17})
145
+
146
+ adults = people.filter(Field("age") >= 18)
147
+ people.get_record("Ada").age # O(1) lookup by id
148
+ ```
149
+
150
+ Stores pickle cleanly, including their dynamically generated record class.
151
+
152
+ ## Homelab pieces
153
+
154
+ ### Container control
155
+
156
+ ```python
157
+ from corekit.docker import Watchdog
158
+
159
+ watchdog = Watchdog(enforce_label=True)
160
+ watchdog.restart_container_by_name("minecraft")
161
+ watchdog.find_and_stop(label="app", value="staging")
162
+ ```
163
+
164
+ `enforce_label` limits the blast radius: with it on, only containers carrying
165
+ the `watchdog=true` label can be started, stopped or paused, so a mistyped name
166
+ cannot take down something unrelated. Leave it on unless the watchdog is meant
167
+ to control everything on the host.
168
+
169
+ ### Reacting to logs
170
+
171
+ Describe what to watch for and what to do about it:
172
+
173
+ ```yaml
174
+ # config.yaml
175
+ containers:
176
+ - name: "minecraft-.*"
177
+ rules:
178
+ - name: "out of memory"
179
+ pattern: "java.lang.OutOfMemoryError"
180
+ severity: critical
181
+ send_notification: true
182
+ actions:
183
+ - type: restart_container
184
+ max_restarts: 3
185
+ restart_window: 3600
186
+ advanced:
187
+ ignore_patterns:
188
+ - "healthcheck"
189
+ rate_limits:
190
+ restart_container:
191
+ count: 5
192
+ period: hour
193
+ ```
194
+
195
+ ```python
196
+ from corekit.log_monitor import LogMonitor
197
+
198
+ LogMonitor.run("config.yaml")
199
+ ```
200
+
201
+ Restarts are capped per container, so a crash loop cannot become a restart loop.
202
+
203
+ ### Notifications
204
+
205
+ ```python
206
+ from corekit.notifications import BaseNotificationService, Notification, NotificationType
207
+
208
+ class DiscordNotifier(BaseNotificationService):
209
+ """
210
+ Sends notifications to a Discord channel.
211
+ """
212
+
213
+ def _send(self, message: str) -> None:
214
+ discord.post(message)
215
+
216
+ notifier.notify(Notification(message="disk full", type=NotificationType.ERROR))
217
+ ```
218
+
219
+ Override `_send`, not `send`. `notify()` formats the message and calls `_send`,
220
+ so an override with any other name is silently ignored.
221
+
222
+ ### Real-time updates
223
+
224
+ Publish from wherever the work happens:
225
+
226
+ ```python
227
+ from corekit.events import EventPublisher
228
+
229
+ publisher = EventPublisher.for_resource("minecraft", "server", "survival")
230
+ publisher.publish("backup_finished", {"size": "4.2GB"})
231
+ ```
232
+
233
+ Stream it to the browser:
234
+
235
+ ```python
236
+ from corekit.api import SSEResponse
237
+ from corekit.events import SSEStream
238
+
239
+ @router.get("/events")
240
+ async def events(channel: str) -> SSEResponse:
241
+ return SSEResponse(SSEStream(channel, keepalive_interval=15))
242
+ ```
243
+
244
+ The browser side is three lines, and reconnects on its own:
245
+
246
+ ```javascript
247
+ const source = new EventSource("/events?channel=minecraft:server:survival");
248
+ source.addEventListener("backup_finished", e => console.log(JSON.parse(e.data)));
249
+ ```
250
+
251
+ `SSEStream` sends a `connected` frame on subscribe, an optional `initial_state`
252
+ so a client arriving late renders immediately, and a comment frame every
253
+ `keepalive_interval` seconds so proxies do not close an idle connection. For
254
+ WebSockets, `WebSocketBridge` relays the same channel and stops on a terminal
255
+ status.
256
+
257
+ Publishing never raises: an event that cannot be delivered should not take down
258
+ the operation that produced it. `publish` returns whether it worked.
259
+
260
+ ## Parallel work
261
+
262
+ ```python
263
+ from corekit.concurrency import parallelize
264
+
265
+ @parallelize()
266
+ def fetch(url: str) -> Response:
267
+ return client.get(url)
268
+
269
+ for response in fetch(urls):
270
+ ...
271
+ ```
272
+
273
+ Results arrive as they finish; pass `ordered=True` for input order. The thread
274
+ count comes from `concurrency.default_threads` unless you name one, and is
275
+ capped at `max_threads` either way — asking for 9,999 threads gets you the
276
+ ceiling, not 9,999 threads.
277
+
278
+ Failures propagate by default. Pass `raise_on_error=False` to log and skip them
279
+ instead, which loses results silently and so is opt-in.
280
+
281
+ ## HTTP clients
282
+
283
+ ```python
284
+ from corekit.http.client import BaseApiClient
285
+
286
+ class GithubClient(BaseApiClient):
287
+ """
288
+ Talks to the GitHub API.
289
+ """
290
+
291
+ @property
292
+ def base_url(self) -> str:
293
+ return "https://api.github.com"
294
+
295
+ response = GithubClient().get("/users/octocat")
296
+ response.data["login"]
297
+ ```
298
+
299
+ Retries 429 and 5xx with exponential backoff. Every response is a
300
+ `BaseApiResponse`, so a non-JSON error page leaves `data` empty rather than
301
+ raising. `async_get`, `async_post` and friends do the same without blocking.
302
+
303
+ ## Serialization
304
+
305
+ ```python
306
+ from corekit.serialization.serializer import Serializer
307
+ from corekit.serialization.enum import SerializerEngine
308
+
309
+ serializer = Serializer(SerializerEngine.JSON)
310
+ serializer.deserialize(serializer.serialize({"a": 1}))
311
+ ```
312
+
313
+ JSON is the default because it cannot execute code. `pickle` and `dill` can,
314
+ so selecting either requires a key, and payloads are authenticated with an
315
+ HMAC that is verified before anything is decoded:
316
+
317
+ ```python
318
+ Serializer(SerializerEngine.PICKLE, key=os.environ["APP_KEY"])
319
+ ```
320
+
321
+ Never deserialize untrusted bytes with an engine that executes code, even
322
+ signed. The key proves the payload came from you, not that its contents are safe.
323
+
324
+ ## Configuration
325
+
326
+ Configuration is optional. corekit never reads the environment at import time, so
327
+ importing it can never fail for want of a variable.
328
+
329
+ Precedence, highest first: explicit argument, environment, config file, default.
330
+
331
+ ```toml
332
+ # corekit.toml, or a [tool.corekit] table in pyproject.toml
333
+ [standards]
334
+ require_handler_docstrings = true
335
+
336
+ [concurrency]
337
+ default_threads = 4 # used when a caller does not say
338
+ max_threads = 32 # never exceeded, however it is asked
339
+
340
+ [database]
341
+ url = "postgresql://localhost/app"
342
+
343
+ [crypto]
344
+ salt = "..."
345
+ ```
346
+
347
+ Settings are grouped by concern, so `get_settings().concurrency.max_threads`
348
+ says where a value belongs. Environment variables use a double underscore for
349
+ the section: `COREKIT_CONCURRENCY__MAX_THREADS=16`.
350
+
351
+ Environment variables use a `COREKIT_` prefix (`COREKIT_CRYPTO_SALT`). Empty
352
+ values are treated as unset, because container runtimes routinely pass `FOO=`
353
+ for a variable that was never set.
354
+
355
+ ```python
356
+ from corekit.config import CorekitSettings, StandardsSettings, set_settings
357
+
358
+ set_settings(CorekitSettings(standards=StandardsSettings(require_handler_docstrings=True)))
359
+ ```
360
+
361
+ ### Requiring docstrings
362
+
363
+ Off by default. Turn it on and every `BaseHandler` subclass must carry a
364
+ multiline docstring or fail at import. Individual classes can opt out with
365
+ `__require_doc__ = False`.
366
+
367
+ ## Layout
368
+
369
+ Packages are named for what they are, and sit in the layer they belong to.
370
+ Imports go downward only.
371
+
372
+ ```
373
+ corekit/
374
+ config.py constants.py
375
+
376
+ exceptions/ error types
377
+
378
+ observability/ Loggable, Benchmarkable, Timer
379
+ registry/ schemas/ utils/ SmartRegistry, enums and fields, helpers
380
+ data/ Dataset and its filter expressions
381
+ crypto/ files/ serialization/
382
+ concurrency/ ThreadLocalRegistry, ThreadWorker
383
+ decorators/
384
+
385
+ connections/ the Connectable lifecycle and @connect
386
+ sql/ SQLConnection, queries, migrations
387
+ redis/ RedisConnection
388
+ http/ BaseApiClient, retries, responses
389
+
390
+ api/ handlers, routers, responses
391
+ docker/ notifications/ etl/
392
+
393
+ events/ log_monitor/ built on the capabilities above
394
+ ```
395
+
396
+ `sql` and `redis` sit under `connections` because both implement `Connectable`.
397
+ `docker` does not -- `Watchdog` manages containers and has no connection
398
+ lifecycle -- so it stays a top-level integration.
399
+
400
+ `tests/test_architecture.py` enforces the direction: it fails on a cycle, on an
401
+ import pointing upward, or on a new package that has not been placed in the
402
+ layering deliberately.
403
+
404
+ ## Development
405
+
406
+ ```bash
407
+ pip install -e ".[dev,all]"
408
+ pytest
409
+ ruff format . && ruff check --fix .
410
+ ```
411
+
412
+ `tests/test_imports.py` imports every module in the package. Keep it passing:
413
+ several modules were broken for months because nothing ever imported them.
414
+
415
+ ## Licence
416
+
417
+ MIT.
@@ -0,0 +1,125 @@
1
+ corekit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ corekit/constants.py,sha256=18_SbFpb5EaY_T32yLvYqz6WAzxzJsaykUL3hggPm8w,1098
3
+ corekit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ corekit/api/__init__.py,sha256=02s3b1UKPDcFLIc6NgW7oalZb9cwClpUCONuASbpSEI,288
5
+ corekit/api/handler.py,sha256=vYrao8_B5e6MZGI7GLm1Mr-e8NH8rYt8TNAcnoEuzRQ,2659
6
+ corekit/api/responses.py,sha256=bvADoj7snxqCKHIWFiU5rH8Mg5dmvi-ldxVLJbtVEQs,1258
7
+ corekit/api/routers.py,sha256=bT2mNIfwhRhU8EWHNMxUBsL36V4JftiWamJMPaLPIWs,3902
8
+ corekit/concurrency/__init__.py,sha256=MjdyVTaBzhYqn_cVumM43oGidiUMhIjVQQKVrXZQvok,322
9
+ corekit/concurrency/decorators.py,sha256=6Yn95QN3g06NPDP1Q4yZSF7uNnVQ91HCUa8GaL77UvQ,2656
10
+ corekit/concurrency/thread_local.py,sha256=Kh_bWuHzUwJ2jUr0JUCxaUSdahpBt8HvOwpgg7qmpOA,3206
11
+ corekit/concurrency/worker.py,sha256=QAwgucT9tadelFmb8io7J509zpQqlOvByHcVUVpveAM,1969
12
+ corekit/config/__init__.py,sha256=dw2Y2HvpHw1MXwLSVxdARGPYse9j6IteVN3q70c3Kic,1227
13
+ corekit/config/loader.py,sha256=KeEAgN0ZPcOQOwjzPQBFZXhhSI1tw9VAiFRWylFTtGY,5041
14
+ corekit/config/settings.py,sha256=-Bnaa-37TDhXTC3lYE8jCQpG-Lre3K9OZd0wR1cBUD4,4818
15
+ corekit/config/sources.py,sha256=kdeji29XnEOaDGRctiwEfp7bKuA2jBYGbvAdVEs21KE,3923
16
+ corekit/connections/__init__.py,sha256=MkQzfeh-0T-qNSLcA8ERHo61GOTpGzHm7s6HYh_pGIM,1090
17
+ corekit/connections/connectable.py,sha256=d3ND7KZYXTw7iHHzmN3yvNGCzaDMI4S9kJHELdu4XV0,6525
18
+ corekit/connections/decorators.py,sha256=7xfmLuMr3kkceEkkxHxY_Pen2OakJEVukXtiOYAd-6Q,3606
19
+ corekit/connections/registry.py,sha256=BR04dGthcGCtON2MxNsl-TlhRgbRlvGj0RLymDFBkGQ,3119
20
+ corekit/connections/redis/__init__.py,sha256=BsWLZNqpKAqIoreHGLEtftnMqVw-ESK8jbexDAughag,174
21
+ corekit/connections/redis/connection.py,sha256=5ZziF-P8AuE_4s7hrQTflcnAQ5iVdrn2bD4KKlG7QBA,7846
22
+ corekit/connections/sql/__init__.py,sha256=nsxphRJnzN-Vs94kY1_1YM-uAQM8sCPfecf2Qe8pieI,435
23
+ corekit/connections/sql/connection.py,sha256=-b2LLnw06cKsCPpXmiASZwKByfK-FV-UCpiXFlU213Y,10613
24
+ corekit/connections/sql/query.py,sha256=_o5tVmengCjZDWSgoPw41K9mdY5PcAl-xVpSS_IceeM,1774
25
+ corekit/connections/sql/table.py,sha256=B3DiCbISDj83W2HEvBCcX-1e4c5ne7ZPOrStgW9RfaY,2663
26
+ corekit/connections/sql/fields/__init__.py,sha256=cqY_OdBkesDOAjx_mgrnXjPR0htXipX84EI8Njpc0eM,150
27
+ corekit/connections/sql/fields/jsonb.py,sha256=tj2qXh1TaU1llsA995ZeJfAgDSHlN6kpGFh0oQ1rdMI,2051
28
+ corekit/connections/sql/migration/__init__.py,sha256=1LhYawqBvxjzWCo5JmvOV-BYJhtYqi6bOX4P5RwWZzg,1537
29
+ corekit/connections/sql/migration/base.py,sha256=0faFzUuXGHesfhDPlkvo1VetGgOHkCNZy4Xbowq5mmc,1317
30
+ corekit/connections/sql/migration/operations.py,sha256=iO3Qq4K3TBxS-K6I77w8BnIJ2rKHGvrVlvaPUwV-N3I,11759
31
+ corekit/connections/sql/migration/registry.py,sha256=_S3kFGJVGqwZFpy_ls3uYc9Q5yTODLmlpXF_GmMfTEY,6710
32
+ corekit/connections/sql/migration/table.py,sha256=CpOkJl-4rcG2hTZYq-Yt7OvGTpVhVJ42IrubVO3BUvs,666
33
+ corekit/crypto/__init__.py,sha256=dtu06iE3reJWRp99gOvV7QQ6iw5V8F7e4kU1CR8ppos,27
34
+ corekit/crypto/constants.py,sha256=KALouS9fJKm5Ajk3Yco3aKyJ_LHHlJXWyPtLwwJTjiA,351
35
+ corekit/crypto/enum.py,sha256=0csj06cATCYDPY_QTNy66j_hB16WCqcHg8tJOO0lMNU,212
36
+ corekit/crypto/hasher.py,sha256=qiJgeUsI2EeRLRPRf7YPY3ylZkRx3ZXfzsUk5Wg9uW4,2930
37
+ corekit/data/__init__.py,sha256=y3VOJJwL4WKYxJcWIQooqCBjGV0xNAl_nKIDQYthN5w,1845
38
+ corekit/data/dataset.py,sha256=aRozqHF4nGmHQgOGxooTnKWZOymyvW76H_3T9S3gYKY,12623
39
+ corekit/data/record.py,sha256=y-tPj6zbzWGTosuqIfG7xXRLhcyzUxif2ckQZLNAgJc,5380
40
+ corekit/data/stats.py,sha256=EsmApb7ryjaVOOxU7l9S9CgfjLrV1VLynXMyyMzHBaQ,4915
41
+ corekit/data/expressions/__init__.py,sha256=2H-c-SvJGnGUT6FNMI5vLLtYp6teKnwHe3g1KpA59Gc,972
42
+ corekit/data/expressions/comparison.py,sha256=3e7MqJ5ISRmhbXX3HVzX002jnVKUhjMUJFEZqMkSB2U,9433
43
+ corekit/data/expressions/expression.py,sha256=CP-potcEhym7F8XwgqKxqPcQ9wo3jFH3xl4jN1E8ZFA,3492
44
+ corekit/decorators/__init__.py,sha256=R1RE9t0uZhSPi0L2NAC_EbY9vWnmSpoKV9iDW3yoJOM,85
45
+ corekit/decorators/exception_handling.py,sha256=W6JSrJ_nKVpbwudb4-MpcJoENg11fC0ZA0uVbMaDTLk,1561
46
+ corekit/decorators/warnings.py,sha256=RS7AS6OS05VxiWaMjkj6kYky9FbpT4Z-Cb7TQoPOObk,1109
47
+ corekit/docker/__init__.py,sha256=LtrmEvqorme1-YwL0jGNFLEASdgn69s0eYuh490anmg,104
48
+ corekit/docker/watchdog.py,sha256=yreDLBQCMJYA-1IZzF2lHTmkQvvgF69zplPeS1L2NNA,7949
49
+ corekit/etl/__init__.py,sha256=0agaPi5Lv8q8hTAC5JKj2Dm73vW52l9ZQRnb8JLu5oQ,1341
50
+ corekit/etl/connection.py,sha256=DBFP_2mrB0wtauWnibTTCW4yqPrpCl-DbG0KqS74s84,1279
51
+ corekit/etl/orchestrator.py,sha256=4hPPHYQ-mzaBWLFZNlGe-7Rpkw4ZIzckE2NSVEDvyW0,7208
52
+ corekit/etl/schemas.py,sha256=L0FN-jfRuWKgWllKL3trgo3IDJzu7HgN3VfRzbOYh8M,395
53
+ corekit/etl/extract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
+ corekit/etl/extract/extractor.py,sha256=042fg1krmNcSx6mEF1oLN5-Fz010EUq6vEDOSQ_y2TQ,1616
55
+ corekit/etl/extract/schemas.py,sha256=Hqse-GUwaCuN7eAW7Cacuj4k7Vy1dEu-NagruabiYWU,386
56
+ corekit/etl/load/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
+ corekit/etl/load/loader.py,sha256=Oh8UZq1gRXFaO1a0poDxMNtdcnIayPsh06XuBQM9KjI,1579
58
+ corekit/etl/load/schemas.py,sha256=c4hZrbjgN_zFKMBhDeNwaPARTiJdgnVFSIW8Lah4c3U,860
59
+ corekit/etl/transform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
+ corekit/etl/transform/schemas.py,sha256=_Kr8DAXQKZH6nIqYjXVpaeeA0shAsk1vjWOcEcAXly8,319
61
+ corekit/etl/transform/transformer.py,sha256=aC3QIlA7Ze6MEr-4586k1jDCxpc0FkLIjk0tSdPcdik,933
62
+ corekit/events/__init__.py,sha256=BrNQKcLnSs5zT1vjDJZ4UX_CD0g87dA5resmzKs0nQQ,1065
63
+ corekit/events/enum.py,sha256=K7ziTjKDjZEZlDO8whjDph8X-bTT_ze2guTrYDGozYc,1342
64
+ corekit/events/frames.py,sha256=ICuF9EKWQ1NqcSfkL0JFHvMB6zrikzkwoujP-292Q8k,1480
65
+ corekit/events/models.py,sha256=faE-Z2qwggOaSBrzte-mBWXuomrzWMIl0gOEkjvPqLo,470
66
+ corekit/events/publisher.py,sha256=3CcmFm1WuZfgsLVMkT8IchTvwpuTEzGCT33arfWKhsg,2559
67
+ corekit/events/reader.py,sha256=ZKLEiQIcrv2jhQlUqRWJr4DgA4_Gh9E2S90xKF_zK4A,4449
68
+ corekit/events/sse.py,sha256=xEU7weQZQe3vq5s6M-T1iLeWROjpse0qfC2qKPD__i4,3868
69
+ corekit/events/websocket.py,sha256=EVz844LkULzRJ3L1K74qjne8uwLmaClJjPZrK3H0dxs,3217
70
+ corekit/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
+ corekit/exceptions/base.py,sha256=C7wLXmo3O0kIz2cUQgmfQL3vZkppk9hEQ7acI8VAxNU,1194
72
+ corekit/exceptions/types.py,sha256=ossPkVSUdq8MOnCKLO_p7KsYA07cum-Yq94Zdfh_uEY,691
73
+ corekit/exceptions/custom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
+ corekit/exceptions/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
+ corekit/exceptions/http/exceptions.py,sha256=RtfQshPUQsE9ajzvQuQluNtvJdxPyGsoAliC48fsGBQ,1205
76
+ corekit/files/__init__.py,sha256=YwivxegqFoU9DlZfZBm9Cnt3DP1fqUvNAOeloXWSpzw,688
77
+ corekit/files/base.py,sha256=ipjCASz1ylIHdatVnYMx7iG6iswatLg2Q8kcae3AZdU,3649
78
+ corekit/files/enum.py,sha256=L-_KPW8ilNnSIxNBGgvYgS7H686peNbEyIxOyKBTUPU,688
79
+ corekit/files/json.py,sha256=oLrDbGFtMogsIz26yBpSxFGxHDuwmVDLi3POxyPjdPc,324
80
+ corekit/files/pickle.py,sha256=qzVWJgtEdz8SgTDc5aBjXuktlCkktR1umQaCBbZmCNg,321
81
+ corekit/files/toml.py,sha256=YFRwghMapbCRSl7srqcI7IhqaTQDyO56XRasGMu_Aug,1273
82
+ corekit/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
+ corekit/http/client.py,sha256=cmBQCUN3GR3EpFYjeXtmVkKgPCBUOBOufnktGvQCjA8,5925
84
+ corekit/http/exponential_backoff.py,sha256=5jjGTspX_BC9aBKPr5Y5dNhJqXesmljsjJoQ6i2d4_I,3180
85
+ corekit/http/response.py,sha256=A_DOsAW9BLwGJJV3ea_9RutEUpqSaoX9Trbp4mPVWZE,423
86
+ corekit/log_monitor/__init__.py,sha256=uuXTQdI8EMeFbdt69G-2K8dKh5Nt-h9SzBEsOGcu0lo,371
87
+ corekit/log_monitor/constants.py,sha256=8nzIzU3Qit9h7_stZLWW1Q_0DTnYglw_j5bQUsF586g,230
88
+ corekit/log_monitor/models.py,sha256=GwGEhIHh7pVVEqtqhc5VsmbpX3D-cWqPGaDlwOuyb4A,4803
89
+ corekit/log_monitor/service.py,sha256=ey7Tjyfw-Pem_9D3lZavbbWS6wLA0mQaRwgh0u_JZ9w,17544
90
+ corekit/notifications/__init__.py,sha256=dLSyNnW7pOCgUNFsHRqTNzMRGYTItpwTWR3-wL7WxAI,255
91
+ corekit/notifications/base.py,sha256=G0QDPdvGpnsYPWnwm2cYnZkojdL1qskUXisgGE6YDYw,1457
92
+ corekit/notifications/models.py,sha256=cCAxNFCYCgzSclkUWHQL9O-uWdE058KUUaMewY5PL6M,646
93
+ corekit/observability/__init__.py,sha256=Hpmtty__RRKwkUXPji-jS7zxIeYzB5yYiS5G4jCHQVg,701
94
+ corekit/observability/benchmarkable.py,sha256=6grDn_4ug7koFPn7I5IqkarP1LIbWSz3jmNj0zA16cs,394
95
+ corekit/observability/loggable.py,sha256=UebpEwHaLNVUI5CO4ftuHD-gep7sAIddhcpD7KvkBrg,919
96
+ corekit/observability/timing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
+ corekit/observability/timing/constants.py,sha256=Cv6h98NHzJiTJJufVQBVMBpdI0_fNMTnxAZF0j0vrsg,22
98
+ corekit/observability/timing/split.py,sha256=qoITQnLLKfXe6NdCRvpL4ch8C9f_9G6a-P4bSQFYGYw,528
99
+ corekit/observability/timing/timer.py,sha256=IWG2UcNyWZnOZGLVNbd6DxZ7GESWBx-1ma_exvWJVg8,914
100
+ corekit/registry/__init__.py,sha256=uSehk6Mi3THRihOGeHHPAyKnxNpWw2l0GJ5uQv47epM,422
101
+ corekit/registry/registry.py,sha256=_rOKhEp7cmoOdjDWnXhfUK7xMIdmmNRSnxk_Fplt6dc,4202
102
+ corekit/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
+ corekit/schemas/enum.py,sha256=r7grhcxqUVDOJoOOwsr1tLbDnmsz8e1vZG7N9PaZyiE,1409
104
+ corekit/schemas/types.py,sha256=dgtjHPmmBXM-OXiREwvyoOjOPBSY3hZxnaH-2ks8JVc,1159
105
+ corekit/schemas/dataclasses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
+ corekit/schemas/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
+ corekit/schemas/models/arbitrary.py,sha256=m2TCrc3nIsgSSGwZ8ziPIMn9MNaUgMqYhqowPsjMJmw,407
108
+ corekit/schemas/models/date_models.py,sha256=UOlSLVsWqgr_jnHfxVRgSl4nVVdfbe6zPBYveOgRcIg,399
109
+ corekit/schemas/pydantic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
+ corekit/schemas/pydantic/fields.py,sha256=MLCmVKDnqWQN1w5FpeEbAbQE7ml3xp6vcw4WcJvR-hA,1210
111
+ corekit/serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
112
+ corekit/serialization/enum.py,sha256=pzHuAIXeonEF7-jyN1uYddJk-xvmQGuymDf1U6RWR5w,566
113
+ corekit/serialization/serializable.py,sha256=07JuTDHrefkSeTtdLgku43oZYd9jnPyEC0I7XYZfr5M,1271
114
+ corekit/serialization/serializer.py,sha256=oIvBqVENfDNLhL_4ENTV1cbG-nI2UlYy-gOAaPz5FaU,6203
115
+ corekit/utils/__init__.py,sha256=mCULFy5sjSjN3mHkRjhpcpLpGYyLWe1Uu4bdVS-o2t4,190
116
+ corekit/utils/ids.py,sha256=OST_qafowMk4kl8Z5P9AoAg3Og9XSRRc6_hrL6HU0Gc,76
117
+ corekit/utils/raise_exc.py,sha256=pcG5rHUd96S4dG1IzoiDn9QD3pWEDbVv5ra_vha_aoA,183
118
+ corekit/utils/time.py,sha256=070yexi-nMqsMyx6yaTNPm5pvonsWUvJj8mheFwUR1c,536
119
+ corekit/utils/validators.py,sha256=EBRSIpVhuw_B-hJ9UVQU7W-nzPP49EkUE3mXCLhH2YE,355
120
+ corekit/utils/void.py,sha256=qMyZ2jRsaH7C8M9eGXqmIdRmSFeontnsH-T7rBiT2BY,186
121
+ python_corekit-0.1.0.dist-info/licenses/LICENSE,sha256=357LYxbxAQZ95q5cV-8JK6j-KNgiE6AteRodjEsm-kE,1072
122
+ python_corekit-0.1.0.dist-info/METADATA,sha256=w9mKWBP5THi7Fnvlhc6pGRxd12UkHCPlA3PTqsuTgww,12097
123
+ python_corekit-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
124
+ python_corekit-0.1.0.dist-info/top_level.txt,sha256=SDK4o8BoaI47E9tPbiNCbbWoc2y6N_wCjwnIEfr-GWI,8
125
+ python_corekit-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Steven Jacobsen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ corekit