python-corekit 0.1.1__py3-none-any.whl → 0.2.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 (62) hide show
  1. corekit/api/__init__.py +18 -3
  2. corekit/api/application.py +237 -0
  3. corekit/api/lifespan.py +210 -0
  4. corekit/api/middleware.py +93 -0
  5. corekit/api/routers.py +109 -1
  6. corekit/concurrency/worker.py +65 -65
  7. corekit/connections/sql/__init__.py +31 -3
  8. corekit/connections/sql/connection.py +19 -0
  9. corekit/connections/sql/migration/__init__.py +5 -5
  10. corekit/connections/sql/migration/base.py +3 -3
  11. corekit/connections/sql/migration/operations.py +66 -42
  12. corekit/connections/sql/migration/registry.py +2 -2
  13. corekit/connections/sql/operations/__init__.py +24 -0
  14. corekit/connections/sql/operations/base.py +102 -0
  15. corekit/connections/sql/operations/statements.py +150 -0
  16. corekit/connections/sql/query.py +4 -62
  17. corekit/connections/sql/table.py +30 -4
  18. corekit/constants.py +45 -45
  19. corekit/data/__init__.py +8 -0
  20. corekit/data/expressions/__init__.py +10 -2
  21. corekit/data/expressions/comparison.py +184 -104
  22. corekit/data/expressions/expression.py +103 -98
  23. corekit/data/expressions/operator.py +54 -0
  24. corekit/data/expressions/target.py +21 -0
  25. corekit/data/record.py +147 -147
  26. corekit/data/stats.py +159 -157
  27. corekit/decorators/__init__.py +2 -2
  28. corekit/decorators/exception_handling.py +2 -1
  29. corekit/etl/connection.py +44 -44
  30. corekit/events/websocket.py +3 -2
  31. corekit/exceptions/__init__.py +18 -0
  32. corekit/http/__init__.py +13 -0
  33. corekit/jobs/__init__.py +26 -0
  34. corekit/jobs/registry.py +87 -0
  35. corekit/jobs/runner.py +69 -0
  36. corekit/jobs/task.py +152 -0
  37. corekit/observability/__init__.py +5 -3
  38. corekit/observability/request_context.py +135 -0
  39. corekit/registry/__init__.py +11 -6
  40. corekit/registry/ordered.py +86 -0
  41. corekit/schemas/__init__.py +10 -0
  42. corekit/schemas/enum.py +49 -49
  43. corekit/schemas/models/arbitrary.py +11 -11
  44. corekit/schemas/pydantic/fields.py +35 -35
  45. corekit/schemas/types.py +40 -40
  46. corekit/serialization/__init__.py +22 -0
  47. corekit/serialization/serializer.py +1 -1
  48. corekit/utils/__init__.py +59 -5
  49. corekit/utils/coercion.py +118 -0
  50. corekit/utils/collections.py +115 -0
  51. corekit/utils/ids.py +61 -5
  52. corekit/utils/payload.py +100 -0
  53. corekit/utils/raise_exc.py +8 -8
  54. corekit/utils/text.py +56 -0
  55. corekit/utils/time.py +74 -21
  56. corekit/utils/validators.py +15 -15
  57. corekit/utils/void.py +8 -8
  58. {python_corekit-0.1.1.dist-info → python_corekit-0.2.0.dist-info}/METADATA +98 -92
  59. {python_corekit-0.1.1.dist-info → python_corekit-0.2.0.dist-info}/RECORD +62 -44
  60. {python_corekit-0.1.1.dist-info → python_corekit-0.2.0.dist-info}/WHEEL +0 -0
  61. {python_corekit-0.1.1.dist-info → python_corekit-0.2.0.dist-info}/licenses/LICENSE +0 -0
  62. {python_corekit-0.1.1.dist-info → python_corekit-0.2.0.dist-info}/top_level.txt +0 -0
corekit/utils/text.py ADDED
@@ -0,0 +1,56 @@
1
+ """
2
+ String assembly helpers.
3
+
4
+ Small formatters for turning collections into text a human will read -- log
5
+ lines, notification bodies, error messages -- plus filename sanitization.
6
+ """
7
+
8
+ import re
9
+ from typing import Any, Literal
10
+
11
+ __all__ = ["join_lines", "list_to_english", "sanitize_filename"]
12
+
13
+ FALLBACK_FILENAME = "unnamed"
14
+ _UNSAFE_CHARS = re.compile(r"[^a-z0-9\-]")
15
+ _REPEATED_HYPHENS = re.compile(r"-{2,}")
16
+
17
+
18
+ def join_lines(*items: Any, prefix: str = "") -> str:
19
+ """
20
+ Join items with newlines, optionally prefixing each one.
21
+ """
22
+ return "\n".join(f"{prefix}{item}" for item in items)
23
+
24
+
25
+ def list_to_english(items: list[str], ending: Literal["and", "or"] | None = None) -> str:
26
+ """
27
+ Join items into a readable list: ``"a, b, and c"``.
28
+
29
+ Without ``ending`` the final item is joined with a plain comma, which suits
30
+ a bare enumeration; pass ``"and"`` or ``"or"`` for prose.
31
+ """
32
+ if not items:
33
+ return ""
34
+
35
+ if len(items) == 1:
36
+ return items[0]
37
+
38
+ buffer = ", ".join(items[:-1])
39
+ last = items[-1]
40
+
41
+ if ending is None:
42
+ return f"{buffer}, {last}"
43
+ return f"{buffer}, {ending} {last}"
44
+
45
+
46
+ def sanitize_filename(raw: str, fallback: str = FALLBACK_FILENAME) -> str:
47
+ """
48
+ Reduce a string to lowercase kebab-case safe for use as a filename stem.
49
+
50
+ Everything outside ``a-z0-9-`` becomes a hyphen, runs collapse, and the
51
+ result is trimmed. A string with nothing usable in it returns ``fallback``
52
+ rather than the empty string, which would produce a nameless file.
53
+ """
54
+ cleaned = _UNSAFE_CHARS.sub("-", raw.strip().lower())
55
+ cleaned = _REPEATED_HYPHENS.sub("-", cleaned).strip("-")
56
+ return cleaned or fallback
corekit/utils/time.py CHANGED
@@ -1,21 +1,74 @@
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
1
+ """
2
+ Timezone-aware time helpers.
3
+
4
+ Every function returns UTC. Naive datetimes are the usual source of "works on
5
+ my machine" time bugs, so nothing here produces one.
6
+ """
7
+
8
+ import re
9
+ from datetime import UTC, datetime, timedelta
10
+ from typing import Any
11
+
12
+ __all__ = ["isoformat_now", "parse_docker_timestamp", "time_now", "timedelta_now", "timestamp_now"]
13
+
14
+ # Docker reports nanosecond precision; fromisoformat accepts at most microseconds.
15
+ _DOCKER_TS_PATTERN = re.compile(r"(\.\d{6})\d*")
16
+
17
+ # Docker's zero value for "this never happened".
18
+ _DOCKER_ZERO_TIME = "0001-01-01"
19
+
20
+
21
+ def time_now() -> datetime:
22
+ """
23
+ Return the current UTC time, timezone-aware.
24
+ """
25
+ return datetime.now(UTC)
26
+
27
+
28
+ def isoformat_now() -> str:
29
+ """
30
+ Return the current UTC time as an ISO 8601 string.
31
+ """
32
+ return time_now().isoformat()
33
+
34
+
35
+ def timestamp_now() -> float:
36
+ """
37
+ Return the current UTC time as a Unix timestamp.
38
+ """
39
+ return time_now().timestamp()
40
+
41
+
42
+ def timedelta_now(delta_seconds: int | None = None, add_time: bool = False, **kwargs: Any) -> datetime:
43
+ """
44
+ Return a time offset from now, in the past by default.
45
+
46
+ Pass ``delta_seconds`` for the common case, or any ``timedelta`` keyword
47
+ (``days``, ``hours``, ...) for the rest. ``add_time`` moves forward instead
48
+ of back.
49
+ """
50
+ if delta_seconds is not None:
51
+ kwargs = {"seconds": delta_seconds}
52
+
53
+ delta = timedelta(**kwargs)
54
+ if add_time:
55
+ return time_now() + delta
56
+ return time_now() - delta
57
+
58
+
59
+ def parse_docker_timestamp(value: str | None) -> float | None:
60
+ """
61
+ Parse a docker API timestamp into a Unix timestamp, or ``None``.
62
+
63
+ Docker emits nanosecond-precision ISO 8601 (a container's ``State.StartedAt``,
64
+ for instance), which ``fromisoformat`` rejects. Returns ``None`` for empty
65
+ input, docker's zero timestamp, and anything unparseable, so a caller can
66
+ treat "no such time" and "bad time" the same way.
67
+ """
68
+ if not value or value.startswith(_DOCKER_ZERO_TIME):
69
+ return None
70
+ try:
71
+ trimmed = _DOCKER_TS_PATTERN.sub(r"\1", value.replace("Z", "+00:00"))
72
+ return datetime.fromisoformat(trimmed).timestamp()
73
+ except ValueError:
74
+ return None
@@ -1,15 +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
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 CHANGED
@@ -1,8 +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
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
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-corekit
3
- Version: 0.1.1
4
- Summary: Shared foundations for Python projects: logging, benchmarking, registries, FastAPI routers/handlers, data stores, and ETL
3
+ Version: 0.2.0
4
+ Summary: Shared foundations for Python projects: logging, benchmarking, registries, FastAPI application and routers, SQL statements and migrations, background tasks, and ETL
5
5
  Author: Steven Jacobsen
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/stevejaker/corekit
8
8
  Project-URL: Issues, https://github.com/stevejaker/corekit/issues
9
- Keywords: fastapi,etl,homelab,logging,benchmarking
9
+ Keywords: fastapi,etl,logging,benchmarking,sqlmodel,migrations
10
10
  Classifier: Development Status :: 3 - Alpha
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: Programming Language :: Python :: 3.11
@@ -36,8 +36,8 @@ Dynamic: license-file
36
36
  # corekit
37
37
 
38
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.
39
+ registries, a FastAPI application with routers and handlers, SQL statements and
40
+ migrations, background tasks, an in-memory record store, and ETL scaffolding.
41
41
 
42
42
  Requires Python 3.11+.
43
43
 
@@ -55,7 +55,7 @@ extras to remember, and no import that fails because something was left out.
55
55
  Pin a compatible release rather than tracking whatever is newest:
56
56
 
57
57
  ```
58
- python-corekit~=0.1.0
58
+ python-corekit~=0.2.0
59
59
  ```
60
60
 
61
61
  Before 1.0, the minor version carries breaking changes.
@@ -88,6 +88,57 @@ class Report(Benchmarkable):
88
88
  self.timing("queried") # logs the time since the previous split
89
89
  ```
90
90
 
91
+ ## Assembling an application
92
+
93
+ `Application` is a plain `FastAPI` subclass — every constructor argument,
94
+ including `lifespan`, passes straight through. What it adds is a short set of
95
+ assembly steps, each of which logs what it did.
96
+
97
+ ```python
98
+ from corekit.api import Application, Lifespan
99
+ from app.backend import routers
100
+
101
+ lifespan = Lifespan()
102
+ lifespan.add("cache", startup=cache.connect, shutdown=cache.disconnect)
103
+
104
+ app = Application(lifespan=lifespan)
105
+ app.discover_routers(routers)
106
+ ```
107
+
108
+ `discover_routers` imports every module under the package so each router can
109
+ register itself. It raises if it finds none: passing a package is a statement
110
+ that routers live there, and an app that silently serves nothing is worse than
111
+ one that refuses to start.
112
+
113
+ Lifespan steps start in the order added and shut down in reverse, the way
114
+ nested `with` blocks unwind. If a step fails on the way up, the steps that
115
+ already started are still torn down. A shutdown hook runs only if its own
116
+ startup completed, so it may assume the state that startup builds — and a hook
117
+ that raises cannot stop the unwind.
118
+
119
+ ### Middleware
120
+
121
+ Middleware order is a security property: a host check that reads a client
122
+ address before the proxy-header layer has rewritten it is checking the proxy,
123
+ not the client. So middleware is never auto-discovered, and corekit installs
124
+ none by default. A stack is declared in one place, outermost first — the order
125
+ a request actually meets the layers.
126
+
127
+ ```python
128
+ from corekit.api import MiddlewareStack
129
+
130
+ stack = (
131
+ MiddlewareStack()
132
+ .add(ProxyHeadersMiddleware, trusted_hosts="*")
133
+ .add(TrustedHostMiddleware, allowed_hosts=HOSTS)
134
+ .add(CORSMiddleware, allow_origins=ORIGINS, allow_credentials=True)
135
+ )
136
+ app.add_middleware_stack(stack)
137
+ ```
138
+
139
+ Reading top to bottom gives the order a request travels, which is the property
140
+ you need when reviewing it.
141
+
91
142
  ## Routers and handlers
92
143
 
93
144
  A router and the handler holding its business logic travel together. Declare the
@@ -147,113 +198,67 @@ people.get_record("Ada").age # O(1) lookup by id
147
198
 
148
199
  Stores pickle cleanly, including their dynamically generated record class.
149
200
 
150
- ## Homelab pieces
201
+ ## SQL statements
151
202
 
152
- ### Container control
203
+ Select, insert, update and delete are objects you build and then execute, so a
204
+ statement can be assembled in pieces and passed around before it runs.
153
205
 
154
206
  ```python
155
- from corekit.docker import Watchdog
207
+ from corekit.connections.sql import SQLConnection, Select, Insert, Update, Delete
208
+ from corekit.data import Field
156
209
 
157
- watchdog = Watchdog(enforce_label=True)
158
- watchdog.restart_container_by_name("minecraft")
159
- watchdog.find_and_stop(label="app", value="staging")
160
- ```
210
+ conn = SQLConnection("sqlite:///app.db")
161
211
 
162
- `enforce_label` limits the blast radius: with it on, only containers carrying
163
- the `watchdog=true` label can be started, stopped or paused, so a mistyped name
164
- cannot take down something unrelated. Leave it on unless the watchdog is meant
165
- to control everything on the host.
166
-
167
- ### Reacting to logs
168
-
169
- Describe what to watch for and what to do about it:
170
-
171
- ```yaml
172
- # config.yaml
173
- containers:
174
- - name: "minecraft-.*"
175
- rules:
176
- - name: "out of memory"
177
- pattern: "java.lang.OutOfMemoryError"
178
- severity: critical
179
- send_notification: true
180
- actions:
181
- - type: restart_container
182
- max_restarts: 3
183
- restart_window: 3600
184
- advanced:
185
- ignore_patterns:
186
- - "healthcheck"
187
- rate_limits:
188
- restart_container:
189
- count: 5
190
- period: hour
191
- ```
212
+ conn.execute(Insert(table=User, rows=[{"name": "Ada", "age": 36}]).add(name="Bob", age=17))
192
213
 
193
- ```python
194
- from corekit.log_monitor import LogMonitor
214
+ adults = conn.fetch(Select(table=User).where(User.age >= 18).order_by(User.name).limit(10))
195
215
 
196
- LogMonitor.run("config.yaml")
216
+ conn.execute(Update(table=User).where(User.name == "Bob").set(age=18))
217
+ conn.execute(Delete(table=User).where(User.age < 13))
197
218
  ```
198
219
 
199
- Restarts are capped per container, so a crash loop cannot become a restart loop.
200
-
201
- ### Notifications
220
+ `where` accepts a SQLAlchemy expression or a `corekit.data` one, so the same
221
+ predicate language that filters a `Dataset` also filters a table:
202
222
 
203
223
  ```python
204
- from corekit.notifications import BaseNotificationService, Notification, NotificationType
205
-
206
- class DiscordNotifier(BaseNotificationService):
207
- """
208
- Sends notifications to a Discord channel.
209
- """
210
-
211
- def _send(self, message: str) -> None:
212
- discord.post(message)
213
-
214
- notifier.notify(Notification(message="disk full", type=NotificationType.ERROR))
224
+ Select(table=User).where(Field("age") >= 18)
215
225
  ```
216
226
 
217
- Override `_send`, not `send`. `notify()` formats the message and calls `_send`,
218
- so an override with any other name is silently ignored.
227
+ Update and delete compile to a single statement rather than fetching rows and
228
+ looping, which matters most over a network, where fetch-and-loop pays a round
229
+ trip per row.
219
230
 
220
- ### Real-time updates
231
+ A `Delete` or `Update` with no condition raises rather than running:
221
232
 
222
- Publish from wherever the work happens:
223
-
224
- ```python
225
- from corekit.events import EventPublisher
226
-
227
- publisher = EventPublisher.for_resource("minecraft", "server", "survival")
228
- publisher.publish("backup_finished", {"size": "4.2GB"})
233
+ ```
234
+ Delete on User needs a condition; use truncate to empty a table
229
235
  ```
230
236
 
231
- Stream it to the browser:
237
+ ## Background tasks
232
238
 
233
- ```python
234
- from corekit.api import SSEResponse
235
- from corekit.events import SSEStream
239
+ Tasks describe work and register themselves by name. Nothing here imports a
240
+ queue library, so the same task runs under RQ, Celery, a cron entry, or a test
241
+ with no queue at all.
236
242
 
237
- @router.get("/events")
238
- async def events(channel: str) -> SSEResponse:
239
- return SSEResponse(SSEStream(channel, keepalive_interval=15))
240
- ```
243
+ ```python
244
+ from corekit.jobs import Task, run_task
245
+ from corekit.utils import encode_payload
241
246
 
242
- The browser side is three lines, and reconnects on its own:
247
+ class SendDigest(Task):
248
+ def task_function(self, user: str) -> None:
249
+ ...
243
250
 
244
- ```javascript
245
- const source = new EventSource("/events?channel=minecraft:server:survival");
246
- source.addEventListener("backup_finished", e => console.log(JSON.parse(e.data)));
251
+ # a worker, holding only a name and a JSON string
252
+ run_task("SendDigest", encode_payload(["ada"]))
247
253
  ```
248
254
 
249
- `SSEStream` sends a `connected` frame on subscribe, an optional `initial_state`
250
- so a client arriving late renders immediately, and a comment frame every
251
- `keepalive_interval` seconds so proxies do not close an idle connection. For
252
- WebSockets, `WebSocketBridge` relays the same channel and stops on a terminal
253
- status.
255
+ Arguments cross the queue as JSON, never as a serialized object. `pickle` and
256
+ `dill` execute code while loading, so a queue holding objects turns write
257
+ access to the queue into code execution in a worker. `encode_payload` and
258
+ `decode_payload` live in `corekit.utils`, since crossing a process boundary as
259
+ data is not specific to queues.
254
260
 
255
- Publishing never raises: an event that cannot be delivered should not take down
256
- the operation that produced it. `publish` returns whether it worked.
261
+ `ScheduledTask` adds an `interval` for tasks a scheduler should repeat.
257
262
 
258
263
  ## Parallel work
259
264
 
@@ -375,18 +380,19 @@ corekit/
375
380
  exceptions/ error types
376
381
 
377
382
  observability/ Loggable, Benchmarkable, Timer
378
- registry/ schemas/ utils/ SmartRegistry, enums and fields, helpers
383
+ registry/ schemas/ utils/ registries, enums and fields, helpers
379
384
  data/ Dataset and its filter expressions
385
+ jobs/ queue-independent background tasks
380
386
  crypto/ files/ serialization/
381
387
  concurrency/ ThreadLocalRegistry, ThreadWorker
382
388
  decorators/
383
389
 
384
390
  connections/ the Connectable lifecycle and @connect
385
- sql/ SQLConnection, queries, migrations
391
+ sql/ SQLConnection, statements, migrations
386
392
  redis/ RedisConnection
387
393
  http/ BaseApiClient, retries, responses
388
394
 
389
- api/ handlers, routers, responses
395
+ api/ Application, lifespan, middleware, routers
390
396
  docker/ notifications/ etl/
391
397
 
392
398
  events/ log_monitor/ built on the capabilities above
@@ -1,14 +1,17 @@
1
1
  corekit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- corekit/constants.py,sha256=18_SbFpb5EaY_T32yLvYqz6WAzxzJsaykUL3hggPm8w,1098
2
+ corekit/constants.py,sha256=lVuBLu6JCZ8yYt2N36jlVLbkRPYHdTI06XdsXDABXjk,1053
3
3
  corekit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- corekit/api/__init__.py,sha256=02s3b1UKPDcFLIc6NgW7oalZb9cwClpUCONuASbpSEI,288
4
+ corekit/api/__init__.py,sha256=44gtrMRkllhAQdwpfIKrkMuSl3qg61xRxgaTDldzmjg,688
5
+ corekit/api/application.py,sha256=VIcohNNPW5VDseILWrUS3Uchz8i0KWsDNUF7XA7aRGI,10038
5
6
  corekit/api/handler.py,sha256=vYrao8_B5e6MZGI7GLm1Mr-e8NH8rYt8TNAcnoEuzRQ,2659
7
+ corekit/api/lifespan.py,sha256=_gFQwIMcrvYfsEfUg6PrwmAJYRU2dsFksrXhzuuj6Qg,7873
8
+ corekit/api/middleware.py,sha256=6M5VPjRZTTPEfX5Hm7DHLoDwwWWAgmvWNiTuSykUA98,3168
6
9
  corekit/api/responses.py,sha256=bvADoj7snxqCKHIWFiU5rH8Mg5dmvi-ldxVLJbtVEQs,1258
7
- corekit/api/routers.py,sha256=bT2mNIfwhRhU8EWHNMxUBsL36V4JftiWamJMPaLPIWs,3902
10
+ corekit/api/routers.py,sha256=j2DSkYTTIGd5qFEzYFlY5YM0qhdvhjUu8iFxWc9KM6o,7910
8
11
  corekit/concurrency/__init__.py,sha256=MjdyVTaBzhYqn_cVumM43oGidiUMhIjVQQKVrXZQvok,322
9
12
  corekit/concurrency/decorators.py,sha256=6Yn95QN3g06NPDP1Q4yZSF7uNnVQ91HCUa8GaL77UvQ,2656
10
13
  corekit/concurrency/thread_local.py,sha256=Kh_bWuHzUwJ2jUr0JUCxaUSdahpBt8HvOwpgg7qmpOA,3206
11
- corekit/concurrency/worker.py,sha256=QAwgucT9tadelFmb8io7J509zpQqlOvByHcVUVpveAM,1969
14
+ corekit/concurrency/worker.py,sha256=osdtvG1rjnGKTFYVKuceSDTvZaF6cclwvKoYd1ECpwo,1904
12
15
  corekit/config/__init__.py,sha256=dw2Y2HvpHw1MXwLSVxdARGPYse9j6IteVN3q70c3Kic,1227
13
16
  corekit/config/loader.py,sha256=KeEAgN0ZPcOQOwjzPQBFZXhhSI1tw9VAiFRWylFTtGY,5041
14
17
  corekit/config/settings.py,sha256=Z-GB5Cj-gl23AbYT_3HHBfeWFXMQL9XX11p9gqWvcqk,4822
@@ -19,35 +22,40 @@ corekit/connections/decorators.py,sha256=7xfmLuMr3kkceEkkxHxY_Pen2OakJEVukXtiOYA
19
22
  corekit/connections/registry.py,sha256=BR04dGthcGCtON2MxNsl-TlhRgbRlvGj0RLymDFBkGQ,3119
20
23
  corekit/connections/redis/__init__.py,sha256=BsWLZNqpKAqIoreHGLEtftnMqVw-ESK8jbexDAughag,174
21
24
  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
25
+ corekit/connections/sql/__init__.py,sha256=_o9AGqb9fvCIP5cLng8BKSRGLkklKPh9qPv3r4LxRR4,844
26
+ corekit/connections/sql/connection.py,sha256=OLEiFd-Ce2gZLdAKo6GPqG2kwBESZg3boA-T5Nqj8o4,11276
27
+ corekit/connections/sql/query.py,sha256=8s5atIssmN2l9FCL2IHnIQqwxOk0fA_mPNh36koLhoE,233
28
+ corekit/connections/sql/table.py,sha256=qsx_2LhcRP6eXIKo-B6CKGLYV9h4bRANIxYNMEHjxgk,3612
26
29
  corekit/connections/sql/fields/__init__.py,sha256=cqY_OdBkesDOAjx_mgrnXjPR0htXipX84EI8Njpc0eM,150
27
30
  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
31
+ corekit/connections/sql/migration/__init__.py,sha256=68ZYG4MgYHXW1kKpMSNXXEjtVEbNPME30nSfCVS5FaQ,1582
32
+ corekit/connections/sql/migration/base.py,sha256=b-3ISuCRgt_HuLl4lWNPq2QiNC2_3fiNrd1dEgDd7pE,1344
33
+ corekit/connections/sql/migration/operations.py,sha256=SxNpEGQw-fPgg9uW1YmSsvV2wNO47YfqSSeZ-2nsTU8,13107
34
+ corekit/connections/sql/migration/registry.py,sha256=M1iS7v7aKYfgyf_grvF-6Ogn4KfQ1EEWbA1MO02CiB0,6728
32
35
  corekit/connections/sql/migration/table.py,sha256=CpOkJl-4rcG2hTZYq-Yt7OvGTpVhVJ42IrubVO3BUvs,666
36
+ corekit/connections/sql/operations/__init__.py,sha256=f6fnAbD3k8ALXwcwJ31fIHQwkvud_WqmUod_apTErpY,480
37
+ corekit/connections/sql/operations/base.py,sha256=VU5LeK6rT4GUo7hd0gMSw2nM47ilBDDIZSFakDFlBKc,2907
38
+ corekit/connections/sql/operations/statements.py,sha256=H_VYBT7npttTabpb7v1-fxjsINmU8cOFJvEGDowVU6Q,4318
33
39
  corekit/crypto/__init__.py,sha256=dtu06iE3reJWRp99gOvV7QQ6iw5V8F7e4kU1CR8ppos,27
34
40
  corekit/crypto/constants.py,sha256=0rSSEpVKi9kBeza6Tq-5XK4LoWhEUyhrkKdcidZUEnc,299
35
41
  corekit/crypto/enum.py,sha256=0csj06cATCYDPY_QTNy66j_hB16WCqcHg8tJOO0lMNU,212
36
42
  corekit/crypto/hasher.py,sha256=qiJgeUsI2EeRLRPRf7YPY3ylZkRx3ZXfzsUk5Wg9uW4,2930
37
- corekit/data/__init__.py,sha256=y3VOJJwL4WKYxJcWIQooqCBjGV0xNAl_nKIDQYthN5w,1845
43
+ corekit/data/__init__.py,sha256=COGfw3-OBRp6KKWBSixid5vfaeasFVY5X4Wza0mB_kY,1977
38
44
  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
45
+ corekit/data/record.py,sha256=EczoOvcxl-lHO1M92AjUh_5E9UQJ2qwBsiu6Yo-34gs,5233
46
+ corekit/data/stats.py,sha256=N6Wn9JfrCBhwPWb56ByJ6HEwS6fB43WFkGg7vNOwqn0,4811
47
+ corekit/data/expressions/__init__.py,sha256=1H_HkgxDSF7Br8LaZ9Nrde_zDAJ2Pxeo4vVINGDov2o,1320
48
+ corekit/data/expressions/comparison.py,sha256=mG61TbjbC_lb3pKA-B-Nx1EEJi-SkPRuWGSQC_RntvQ,12408
49
+ corekit/data/expressions/expression.py,sha256=FdvbsGS4IhOHyzdjxK-hFu-3rlUfAngRX8jyXQGQU3U,3539
50
+ corekit/data/expressions/operator.py,sha256=0ViIdJs6kE5t9QLzQa6stcnL86EfVUtZRbHu2W9Vx4M,1415
51
+ corekit/data/expressions/target.py,sha256=QSl7sH1qFVUc-mL7gsasTsCahWKhHqdo0bMHt--t2m8,470
52
+ corekit/decorators/__init__.py,sha256=YrBcc_4wkSs7nEb3OjC7C7a8fNEvBk3RzOpD8QccHKM,83
53
+ corekit/decorators/exception_handling.py,sha256=VrdNs4HLiOVlXPTwgzsOBHZuVSWzAeK93W0CaaD1_ww,1589
46
54
  corekit/decorators/warnings.py,sha256=RS7AS6OS05VxiWaMjkj6kYky9FbpT4Z-Cb7TQoPOObk,1109
47
55
  corekit/docker/__init__.py,sha256=LtrmEvqorme1-YwL0jGNFLEASdgn69s0eYuh490anmg,104
48
56
  corekit/docker/watchdog.py,sha256=yreDLBQCMJYA-1IZzF2lHTmkQvvgF69zplPeS1L2NNA,7949
49
57
  corekit/etl/__init__.py,sha256=0agaPi5Lv8q8hTAC5JKj2Dm73vW52l9ZQRnb8JLu5oQ,1341
50
- corekit/etl/connection.py,sha256=DBFP_2mrB0wtauWnibTTCW4yqPrpCl-DbG0KqS74s84,1279
58
+ corekit/etl/connection.py,sha256=dhfeHMUijodEpfOGr54K0rJlWV8WhAAUmhTQ_8GSKlU,1235
51
59
  corekit/etl/orchestrator.py,sha256=4hPPHYQ-mzaBWLFZNlGe-7Rpkw4ZIzckE2NSVEDvyW0,7208
52
60
  corekit/etl/schemas.py,sha256=L0FN-jfRuWKgWllKL3trgo3IDJzu7HgN3VfRzbOYh8M,395
53
61
  corekit/etl/extract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -66,8 +74,8 @@ corekit/events/models.py,sha256=faE-Z2qwggOaSBrzte-mBWXuomrzWMIl0gOEkjvPqLo,470
66
74
  corekit/events/publisher.py,sha256=3CcmFm1WuZfgsLVMkT8IchTvwpuTEzGCT33arfWKhsg,2559
67
75
  corekit/events/reader.py,sha256=ZKLEiQIcrv2jhQlUqRWJr4DgA4_Gh9E2S90xKF_zK4A,4449
68
76
  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
77
+ corekit/events/websocket.py,sha256=9dwmQ2BXvYLJrwHtGpspraRG30ls95Sj0D5tv2oDvsg,3283
78
+ corekit/exceptions/__init__.py,sha256=EJqMvijcOKDs59-PsSnHaodwxo2wDyXzkyFUSCAaarU,587
71
79
  corekit/exceptions/base.py,sha256=C7wLXmo3O0kIz2cUQgmfQL3vZkppk9hEQ7acI8VAxNU,1194
72
80
  corekit/exceptions/types.py,sha256=ossPkVSUdq8MOnCKLO_p7KsYA07cum-Yq94Zdfh_uEY,691
73
81
  corekit/exceptions/custom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -79,10 +87,14 @@ corekit/files/enum.py,sha256=L-_KPW8ilNnSIxNBGgvYgS7H686peNbEyIxOyKBTUPU,688
79
87
  corekit/files/json.py,sha256=oLrDbGFtMogsIz26yBpSxFGxHDuwmVDLi3POxyPjdPc,324
80
88
  corekit/files/pickle.py,sha256=qzVWJgtEdz8SgTDc5aBjXuktlCkktR1umQaCBbZmCNg,321
81
89
  corekit/files/toml.py,sha256=YFRwghMapbCRSl7srqcI7IhqaTQDyO56XRasGMu_Aug,1273
82
- corekit/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
+ corekit/http/__init__.py,sha256=DLdi_Ul0ZagEYuGu7KbS_9kAaDLeiXdSbVaOqfdS4hs,488
83
91
  corekit/http/client.py,sha256=cmBQCUN3GR3EpFYjeXtmVkKgPCBUOBOufnktGvQCjA8,5925
84
92
  corekit/http/exponential_backoff.py,sha256=5jjGTspX_BC9aBKPr5Y5dNhJqXesmljsjJoQ6i2d4_I,3180
85
93
  corekit/http/response.py,sha256=A_DOsAW9BLwGJJV3ea_9RutEUpqSaoX9Trbp4mPVWZE,423
94
+ corekit/jobs/__init__.py,sha256=vsv8o23ApT6MlOwOq-AFYeQW2NT1kjyvu89A5CL2PsY,880
95
+ corekit/jobs/registry.py,sha256=cco6884eVuIYHEtSv6P62MICFjX-QhuSZIFkEJcQi_M,2557
96
+ corekit/jobs/runner.py,sha256=6RSjfDKKR82uvPcU5-7avHnPvo4OtZpV7m0OKjD6uYw,2215
97
+ corekit/jobs/task.py,sha256=kOqY0202HQ9b-DcS6AjLHQ7BuXm1CduEHQoEnx95vUU,5050
86
98
  corekit/log_monitor/__init__.py,sha256=uuXTQdI8EMeFbdt69G-2K8dKh5Nt-h9SzBEsOGcu0lo,371
87
99
  corekit/log_monitor/constants.py,sha256=8nzIzU3Qit9h7_stZLWW1Q_0DTnYglw_j5bQUsF586g,230
88
100
  corekit/log_monitor/models.py,sha256=GwGEhIHh7pVVEqtqhc5VsmbpX3D-cWqPGaDlwOuyb4A,4803
@@ -90,36 +102,42 @@ corekit/log_monitor/service.py,sha256=ey7Tjyfw-Pem_9D3lZavbbWS6wLA0mQaRwgh0u_JZ9
90
102
  corekit/notifications/__init__.py,sha256=dLSyNnW7pOCgUNFsHRqTNzMRGYTItpwTWR3-wL7WxAI,255
91
103
  corekit/notifications/base.py,sha256=G0QDPdvGpnsYPWnwm2cYnZkojdL1qskUXisgGE6YDYw,1457
92
104
  corekit/notifications/models.py,sha256=cCAxNFCYCgzSclkUWHQL9O-uWdE058KUUaMewY5PL6M,646
93
- corekit/observability/__init__.py,sha256=Hpmtty__RRKwkUXPji-jS7zxIeYzB5yYiS5G4jCHQVg,701
105
+ corekit/observability/__init__.py,sha256=bz36f-8NmOgssevTSegs0TRTQptHztNHx8ZSjQ6gsQE,895
94
106
  corekit/observability/benchmarkable.py,sha256=6grDn_4ug7koFPn7I5IqkarP1LIbWSz3jmNj0zA16cs,394
95
107
  corekit/observability/loggable.py,sha256=UebpEwHaLNVUI5CO4ftuHD-gep7sAIddhcpD7KvkBrg,919
108
+ corekit/observability/request_context.py,sha256=CaKs3JEGh7ZbMiuSemEHpXs-iAZJYOD9-pPp-nKlDKg,4591
96
109
  corekit/observability/timing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
110
  corekit/observability/timing/constants.py,sha256=Cv6h98NHzJiTJJufVQBVMBpdI0_fNMTnxAZF0j0vrsg,22
98
111
  corekit/observability/timing/split.py,sha256=qoITQnLLKfXe6NdCRvpL4ch8C9f_9G6a-P4bSQFYGYw,528
99
112
  corekit/observability/timing/timer.py,sha256=IWG2UcNyWZnOZGLVNbd6DxZ7GESWBx-1ma_exvWJVg8,914
100
- corekit/registry/__init__.py,sha256=uSehk6Mi3THRihOGeHHPAyKnxNpWw2l0GJ5uQv47epM,422
113
+ corekit/registry/__init__.py,sha256=lMIcmjnGDRfBfVSaxWv_EwqMktOIKNdsyuWrmnoBXGA,649
114
+ corekit/registry/ordered.py,sha256=_KS4mWzmLgFvODW2M0_ugxZIBW_dKhwwxrVrtNqLMfs,2506
101
115
  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
116
+ corekit/schemas/__init__.py,sha256=lpGVfqzXLpxpdP5ijS9uye_XfF1yNgjJ0Rt4m2EkwPE,298
117
+ corekit/schemas/enum.py,sha256=nlXOX6L6XN_YcskqUIE0nMDtpJRk_N9QTFwxsJg1vnM,1360
118
+ corekit/schemas/types.py,sha256=z6GxKd-rUcQ4ySyzgh7xpN2g7SR1fedFoCdOAyqHtrU,1119
105
119
  corekit/schemas/dataclasses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
120
  corekit/schemas/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
- corekit/schemas/models/arbitrary.py,sha256=m2TCrc3nIsgSSGwZ8ziPIMn9MNaUgMqYhqowPsjMJmw,407
121
+ corekit/schemas/models/arbitrary.py,sha256=L2Uqxo_twPAmzoly3Lyo5rzYhDzReSB_U-oAuRgWubg,396
108
122
  corekit/schemas/models/date_models.py,sha256=UOlSLVsWqgr_jnHfxVRgSl4nVVdfbe6zPBYveOgRcIg,399
109
123
  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
124
+ corekit/schemas/pydantic/fields.py,sha256=ScP2ZQCR2qTWaqwjKl7lGvkTGSIeQBJ0yeNs79E47AA,1175
125
+ corekit/serialization/__init__.py,sha256=qZ1zUOjJ7NqasJZ3jb_RQKJM7O7NZjX9wtdPTqZqFrc,741
112
126
  corekit/serialization/enum.py,sha256=pzHuAIXeonEF7-jyN1uYddJk-xvmQGuymDf1U6RWR5w,566
113
127
  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.1.dist-info/licenses/LICENSE,sha256=357LYxbxAQZ95q5cV-8JK6j-KNgiE6AteRodjEsm-kE,1072
122
- python_corekit-0.1.1.dist-info/METADATA,sha256=x6nMXzukrlJb_FTnRamfMiRtGa13ERzJJFG-38e0gzw,11972
123
- python_corekit-0.1.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
124
- python_corekit-0.1.1.dist-info/top_level.txt,sha256=SDK4o8BoaI47E9tPbiNCbbWoc2y6N_wCjwnIEfr-GWI,8
125
- python_corekit-0.1.1.dist-info/RECORD,,
128
+ corekit/serialization/serializer.py,sha256=F8rTw54ruDqWAOONhE1skRyqcbmq-YIAwrZrUUrzC7I,6204
129
+ corekit/utils/__init__.py,sha256=n3feLbH1WrGqWZACyg-hChxkTrqfADFlFkxm5ie2JGo,1769
130
+ corekit/utils/coercion.py,sha256=Aq63jrxQYg1ooWUFXHff2uepB_LoHm1izao8_3xkzEU,3650
131
+ corekit/utils/collections.py,sha256=IqCv_3CCC9T-KmDZCA6l5OHUqe6NOyl0F9Q5RAcMgRM,3617
132
+ corekit/utils/ids.py,sha256=BVniaYqewM-EosDxcJAT_S_7T9C1c5X8nsaVGs5up34,1731
133
+ corekit/utils/payload.py,sha256=DN2wyxnSDDByituR20rYHLBLM4qfFhwiMpIkcr5SFg0,3528
134
+ corekit/utils/raise_exc.py,sha256=cz7ljc6ZW90AGQussKuANN8PLsDLUhl-E8FZ0Ipsw-0,175
135
+ corekit/utils/text.py,sha256=54maLM4jsS8cjVXRQCNVFzVszGimuzgaNzudhZzZeio,1694
136
+ corekit/utils/time.py,sha256=-iPaETXcgDu7J39Kwe938JyZKhPVjl6a572BBqsqcDQ,2215
137
+ corekit/utils/validators.py,sha256=_HVKo3y264KGNaZ-1RyM1x46VPvu7XKcQkrHWDC9uxs,340
138
+ corekit/utils/void.py,sha256=eXDb2QynuguiSgHGJLdgE08MuRnxBdYebRdMD7s1m9o,178
139
+ python_corekit-0.2.0.dist-info/licenses/LICENSE,sha256=357LYxbxAQZ95q5cV-8JK6j-KNgiE6AteRodjEsm-kE,1072
140
+ python_corekit-0.2.0.dist-info/METADATA,sha256=4i3O12D1eLCzZdoX7kSR9r_v3EPxnTF2K8tl3CQDQ74,13155
141
+ python_corekit-0.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
142
+ python_corekit-0.2.0.dist-info/top_level.txt,sha256=SDK4o8BoaI47E9tPbiNCbbWoc2y6N_wCjwnIEfr-GWI,8
143
+ python_corekit-0.2.0.dist-info/RECORD,,