muscles-data 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,321 @@
1
+ Metadata-Version: 2.4
2
+ Name: muscles-data
3
+ Version: 0.1.0
4
+ Summary: Framework-level named data resources and typed ports for Muscles
5
+ Author-email: "Denis B." <denis@butko.info>
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: muscles<2.0.0,>=1.0.0rc1
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest<9.0,>=8.0; extra == "dev"
12
+
13
+ # muscles-data
14
+
15
+ Framework-level named data resources and typed ports for the Muscles ecosystem.
16
+
17
+ ## Purpose
18
+
19
+ `muscles-data` gives projects one small runtime for declaring, inspecting and
20
+ resolving data backends through narrow typed ports:
21
+
22
+ ```text
23
+ config -> named resource -> capability check -> lazy adapter -> typed port
24
+ ```
25
+
26
+ It does not replace SQL libraries, search clients, document databases, object
27
+ storage SDKs or project repositories. Framework packages should depend on ports,
28
+ not vendor clients.
29
+
30
+ ## Scope
31
+
32
+ The core package owns:
33
+
34
+ - `DataResourceConfig` and `DataCapability`;
35
+ - `DataAdapterCatalog`;
36
+ - lazy `DataRuntime`;
37
+ - `DataResourceHandle`;
38
+ - typed ports:
39
+ - `VectorSearchPort`;
40
+ - `SearchIndexPort`;
41
+ - `ObjectStorePort`;
42
+ - `KeyValuePort`;
43
+ - `LockPort`;
44
+ - `StreamPort`;
45
+ - `EventPublisherPort`;
46
+ - `EventConsumerPort`;
47
+ - `EventStorePort`;
48
+ - `DocumentStorePort`;
49
+ - `SqlResourcePort`;
50
+ - safe `data.resources.list`, `data.resource.inspect`, `data.doctor` actions;
51
+ - in-memory adapters for tests and examples;
52
+ - the `type: sql` bridge to a `muscles-sql` compatible registry.
53
+
54
+ It intentionally does not own:
55
+
56
+ - project business schemas;
57
+ - universal CRUD/query API;
58
+ - ORM models, repositories, Unit of Work or migrations;
59
+ - vendor clients such as SQLAlchemy, Redis, Elasticsearch, OpenSearch, Qdrant,
60
+ PyMongo or boto3;
61
+ - RAG, document parsing, embeddings, prompts or LLM calls;
62
+ - protocol routes;
63
+ - distributed transactions across backends.
64
+
65
+ ## Event-driven data contract
66
+
67
+ `DataEventEnvelope` and the publish/read/ack request and result models are core
68
+ `muscles.Model` schemas. They are visible through `inspect_application` and can
69
+ be reused by protocol projections without a second DTO or schema system.
70
+
71
+ The MVP includes `EventPublisherPort`, `EventConsumerPort` and `EventStorePort`,
72
+ plus the `memory_event` adapter for tests and local examples. Event schemas
73
+ describe semantic facts; `StreamPort` remains a lower-level transport
74
+ primitive. Production transports, outbox/inbox workers and distributed
75
+ transactions remain separate extensions.
76
+
77
+ Diagnostics expose only safe counts and resource metadata; event payloads are
78
+ never included in `inspect` or `doctor` output.
79
+
80
+ ## Configuration
81
+
82
+ Core resources work without external database packages:
83
+
84
+ ```yaml
85
+ data:
86
+ resources:
87
+ vector.docs:
88
+ type: memory_vector
89
+
90
+ search.docs:
91
+ type: memory_search
92
+
93
+ cache.default:
94
+ type: memory_kv
95
+
96
+ objects.docs:
97
+ type: memory_object
98
+
99
+ documents.local:
100
+ type: memory_document
101
+
102
+ sql.main:
103
+ type: sql
104
+ connection: main
105
+ role: read_write
106
+ ```
107
+
108
+ External adapter packages add real backend resource types. Their config still
109
+ lives in the project, but the factory comes from the adapter package:
110
+
111
+ ```yaml
112
+ data:
113
+ resources:
114
+ search.elastic:
115
+ type: elasticsearch
116
+ url_env: ELASTICSEARCH_URL
117
+ api_key: ${ELASTICSEARCH_API_KEY}
118
+ index: docs
119
+
120
+ search.public:
121
+ type: opensearch
122
+ url: ${OPENSEARCH_URL}
123
+ username: ${OPENSEARCH_USER}
124
+ password: ${OPENSEARCH_PASSWORD}
125
+ index: docs
126
+
127
+ cache.redis:
128
+ type: redis
129
+ url_env: REDIS_URL
130
+ namespace: app
131
+ stream_group: workers
132
+ consumer: index-worker
133
+
134
+ vector.qdrant:
135
+ type: qdrant
136
+ url_env: QDRANT_URL
137
+ api_key: ${QDRANT_API_KEY}
138
+ collection: docs
139
+ vector_size: 1536
140
+ distance: cosine
141
+
142
+ mongo.content:
143
+ type: mongodb
144
+ url: ${MONGO_URL}
145
+ database: content
146
+
147
+ objects.docs:
148
+ type: s3
149
+ endpoint_url: ${S3_ENDPOINT}
150
+ bucket: documents
151
+ prefix: raw
152
+
153
+ sql.local:
154
+ type: sqlalchemy
155
+ url: sqlite:///:memory:
156
+ name: local_sqlite
157
+ ```
158
+
159
+ ## External Adapter Packages
160
+
161
+ The adapter packages are separate repositories and dependencies:
162
+
163
+ | Resource type | Package | Port | Example |
164
+ | --- | --- | --- | --- |
165
+ | `elasticsearch` | [`muscles-data-elasticsearch`](https://github.com/butkoden/muscles-data-elasticsearch) | `SearchIndexPort` | [`example_data_elasticsearch_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_elasticsearch_1) |
166
+ | `opensearch` | [`muscles-data-opensearch`](https://github.com/butkoden/muscles-data-opensearch) | `SearchIndexPort` | [`example_data_opensearch_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_opensearch_1) |
167
+ | `redis` | [`muscles-data-redis`](https://github.com/butkoden/muscles-data-redis) | `KeyValuePort`, `LockPort`, `StreamPort` | [`example_data_redis_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_redis_1) |
168
+ | `qdrant` | [`muscles-data-qdrant`](https://github.com/butkoden/muscles-data-qdrant) | `VectorSearchPort` | [`example_data_qdrant_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_qdrant_1) |
169
+ | `mongodb` | [`muscles-data-mongodb`](https://github.com/butkoden/muscles-data-mongodb) | `DocumentStorePort` | [`example_data_mongodb_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_mongodb_1) |
170
+ | `s3` | [`muscles-data-s3`](https://github.com/butkoden/muscles-data-s3) | `ObjectStorePort` | [`example_data_s3_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_s3_1) |
171
+ | `sqlalchemy` | [`muscles-data-sqlalchemy`](https://github.com/butkoden/muscles-data-sqlalchemy) | `SqlResourcePort` | [`example_data_sqlalchemy_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_sqlalchemy_1) |
172
+
173
+ Register external factories in the project composition root:
174
+
175
+ ```python
176
+ from muscles_data.catalog import DataAdapterCatalog
177
+ from muscles_data_elasticsearch import ElasticsearchSearchFactory
178
+ from muscles_data_qdrant import QdrantVectorFactory
179
+ from muscles_data_redis import RedisDataFactory
180
+
181
+ catalog = DataAdapterCatalog.with_defaults()
182
+ catalog.register(ElasticsearchSearchFactory())
183
+ catalog.register(QdrantVectorFactory())
184
+ catalog.register(RedisDataFactory())
185
+ ```
186
+
187
+ `muscles-data` core does not import these packages automatically. This keeps
188
+ framework startup small and avoids pulling database SDKs into projects that do
189
+ not need them. Registration is deliberately manual: projects install and
190
+ register only the adapter packages they need.
191
+
192
+ ## Runtime API
193
+
194
+ ```python
195
+ from muscles_data import DataRuntime
196
+ from muscles_data.config import DataConfig
197
+ from muscles_data.catalog import DataAdapterCatalog
198
+ from muscles_data.ports import KeyValuePort, VectorSearchPort
199
+
200
+ runtime = DataRuntime(
201
+ config=DataConfig.from_raw({
202
+ "data": {
203
+ "resources": {
204
+ "vector.docs": {"type": "memory_vector"},
205
+ "cache.default": {"type": "memory_kv"},
206
+ }
207
+ }
208
+ }),
209
+ catalog=DataAdapterCatalog.with_defaults(),
210
+ )
211
+
212
+ vector = runtime.require_port("vector.docs", VectorSearchPort)
213
+ cache = runtime.require_port("cache.default", KeyValuePort)
214
+ ```
215
+
216
+ Adapters are initialized lazily: package init and resource listing do not open
217
+ connections. Concrete adapters are created by:
218
+
219
+ - `runtime.require_port(name, PortType)`;
220
+ - `runtime.require_resource(name, capability)`;
221
+ - `runtime.doctor()` when health checks are enabled.
222
+
223
+ ## SQL Resources
224
+
225
+ `type: sql` is the only real backend bridge built into core. It delegates to a
226
+ named registry owned by `muscles-sql` or by the project:
227
+
228
+ ```yaml
229
+ data:
230
+ resources:
231
+ sql.documents:
232
+ type: sql
233
+ connection: documents_metadata
234
+ role: read_write
235
+ ```
236
+
237
+ ```python
238
+ from muscles_data.ports import SqlResourcePort
239
+
240
+ sql = runtime.require_port("sql.documents", SqlResourcePort)
241
+ with sql.session() as session:
242
+ ...
243
+ ```
244
+
245
+ `muscles-data` does not create SQL engines, repositories, Unit of Work objects
246
+ or migrations. `session()`, `session_factory()`, `inspect()` and `doctor()`
247
+ delegate to the supplied registry.
248
+
249
+ Use `muscles-data-sqlalchemy` when a project wants direct SQLAlchemy sessions
250
+ through the same `SqlResourcePort` without using `muscles-sql`.
251
+
252
+ ## Native Escape Hatch
253
+
254
+ The preferred path is always a typed port. A project may explicitly request a
255
+ native/internal backend handle only when the resource declares the
256
+ `native_client` capability:
257
+
258
+ ```python
259
+ from muscles_data import DataCapability
260
+
261
+ handle = runtime.require_resource("cache.default", DataCapability.NATIVE_CLIENT)
262
+ native = handle.native_client()
263
+ ```
264
+
265
+ This is an advanced escape hatch for project-specific operations. Framework
266
+ packages should not build their primary logic on native clients. Native handles,
267
+ credentials and raw payloads are never included in inspect/doctor output.
268
+
269
+ ## Actions
270
+
271
+ - `data.resources.list` — list configured resources, capabilities and lazy init
272
+ state without health checks.
273
+ - `data.resource.inspect` — inspect one resource with redacted options.
274
+ - `data.doctor` — validate factories and run safe health checks with partial
275
+ failure reporting.
276
+
277
+ All actions are normal Muscles actions registered through the core action
278
+ contract. Protocol packages see them through `ActionDispatcher`; there is no
279
+ package-specific protocol routing.
280
+
281
+ ## Telemetry
282
+
283
+ `muscles-data` resolves telemetry through the neutral Muscles provider. It does
284
+ not import `muscles-otel` directly.
285
+
286
+ Safe attributes are resource name/type, capability, operation, status and safe
287
+ counts. Do not add DSNs, tokens, passwords, raw query payloads, document text,
288
+ object content, vector payloads or native clients to spans.
289
+
290
+ ## Examples
291
+
292
+ Run the core smoke examples:
293
+
294
+ ```bash
295
+ PYTHONPATH=../muscles/src:src python3 examples/run_data_runtime.py
296
+ PYTHONPATH=../muscles/src:src python3 examples/run_sql_resource_port.py
297
+ ```
298
+
299
+ Real backend examples live in
300
+ [`muscular-example`](https://github.com/butkoden/muscular-example) as
301
+ `example_data_[adapter]_1` packages.
302
+
303
+ Run tests:
304
+
305
+ ```bash
306
+ PYTHONPATH=../muscles/src:src python3 -m pytest -q
307
+ ```
308
+
309
+ Run the real-backend integration suite locally (Docker and the optional SDKs
310
+ are required):
311
+
312
+ ```bash
313
+ make data-integration-test
314
+ ```
315
+
316
+ The command starts isolated Elasticsearch, OpenSearch, Qdrant, Redis,
317
+ MongoDB, MinIO and PostgreSQL containers, runs every adapter's contract and
318
+ backend lifecycle tests, and removes the containers on exit. Set
319
+ `MUSCLES_DATA_PYTHON` to use a different Python environment. The compose file
320
+ and port map are documented in
321
+ [`infra/docker-compose.integration.yml`](infra/docker-compose.integration.yml).
@@ -0,0 +1,309 @@
1
+ # muscles-data
2
+
3
+ Framework-level named data resources and typed ports for the Muscles ecosystem.
4
+
5
+ ## Purpose
6
+
7
+ `muscles-data` gives projects one small runtime for declaring, inspecting and
8
+ resolving data backends through narrow typed ports:
9
+
10
+ ```text
11
+ config -> named resource -> capability check -> lazy adapter -> typed port
12
+ ```
13
+
14
+ It does not replace SQL libraries, search clients, document databases, object
15
+ storage SDKs or project repositories. Framework packages should depend on ports,
16
+ not vendor clients.
17
+
18
+ ## Scope
19
+
20
+ The core package owns:
21
+
22
+ - `DataResourceConfig` and `DataCapability`;
23
+ - `DataAdapterCatalog`;
24
+ - lazy `DataRuntime`;
25
+ - `DataResourceHandle`;
26
+ - typed ports:
27
+ - `VectorSearchPort`;
28
+ - `SearchIndexPort`;
29
+ - `ObjectStorePort`;
30
+ - `KeyValuePort`;
31
+ - `LockPort`;
32
+ - `StreamPort`;
33
+ - `EventPublisherPort`;
34
+ - `EventConsumerPort`;
35
+ - `EventStorePort`;
36
+ - `DocumentStorePort`;
37
+ - `SqlResourcePort`;
38
+ - safe `data.resources.list`, `data.resource.inspect`, `data.doctor` actions;
39
+ - in-memory adapters for tests and examples;
40
+ - the `type: sql` bridge to a `muscles-sql` compatible registry.
41
+
42
+ It intentionally does not own:
43
+
44
+ - project business schemas;
45
+ - universal CRUD/query API;
46
+ - ORM models, repositories, Unit of Work or migrations;
47
+ - vendor clients such as SQLAlchemy, Redis, Elasticsearch, OpenSearch, Qdrant,
48
+ PyMongo or boto3;
49
+ - RAG, document parsing, embeddings, prompts or LLM calls;
50
+ - protocol routes;
51
+ - distributed transactions across backends.
52
+
53
+ ## Event-driven data contract
54
+
55
+ `DataEventEnvelope` and the publish/read/ack request and result models are core
56
+ `muscles.Model` schemas. They are visible through `inspect_application` and can
57
+ be reused by protocol projections without a second DTO or schema system.
58
+
59
+ The MVP includes `EventPublisherPort`, `EventConsumerPort` and `EventStorePort`,
60
+ plus the `memory_event` adapter for tests and local examples. Event schemas
61
+ describe semantic facts; `StreamPort` remains a lower-level transport
62
+ primitive. Production transports, outbox/inbox workers and distributed
63
+ transactions remain separate extensions.
64
+
65
+ Diagnostics expose only safe counts and resource metadata; event payloads are
66
+ never included in `inspect` or `doctor` output.
67
+
68
+ ## Configuration
69
+
70
+ Core resources work without external database packages:
71
+
72
+ ```yaml
73
+ data:
74
+ resources:
75
+ vector.docs:
76
+ type: memory_vector
77
+
78
+ search.docs:
79
+ type: memory_search
80
+
81
+ cache.default:
82
+ type: memory_kv
83
+
84
+ objects.docs:
85
+ type: memory_object
86
+
87
+ documents.local:
88
+ type: memory_document
89
+
90
+ sql.main:
91
+ type: sql
92
+ connection: main
93
+ role: read_write
94
+ ```
95
+
96
+ External adapter packages add real backend resource types. Their config still
97
+ lives in the project, but the factory comes from the adapter package:
98
+
99
+ ```yaml
100
+ data:
101
+ resources:
102
+ search.elastic:
103
+ type: elasticsearch
104
+ url_env: ELASTICSEARCH_URL
105
+ api_key: ${ELASTICSEARCH_API_KEY}
106
+ index: docs
107
+
108
+ search.public:
109
+ type: opensearch
110
+ url: ${OPENSEARCH_URL}
111
+ username: ${OPENSEARCH_USER}
112
+ password: ${OPENSEARCH_PASSWORD}
113
+ index: docs
114
+
115
+ cache.redis:
116
+ type: redis
117
+ url_env: REDIS_URL
118
+ namespace: app
119
+ stream_group: workers
120
+ consumer: index-worker
121
+
122
+ vector.qdrant:
123
+ type: qdrant
124
+ url_env: QDRANT_URL
125
+ api_key: ${QDRANT_API_KEY}
126
+ collection: docs
127
+ vector_size: 1536
128
+ distance: cosine
129
+
130
+ mongo.content:
131
+ type: mongodb
132
+ url: ${MONGO_URL}
133
+ database: content
134
+
135
+ objects.docs:
136
+ type: s3
137
+ endpoint_url: ${S3_ENDPOINT}
138
+ bucket: documents
139
+ prefix: raw
140
+
141
+ sql.local:
142
+ type: sqlalchemy
143
+ url: sqlite:///:memory:
144
+ name: local_sqlite
145
+ ```
146
+
147
+ ## External Adapter Packages
148
+
149
+ The adapter packages are separate repositories and dependencies:
150
+
151
+ | Resource type | Package | Port | Example |
152
+ | --- | --- | --- | --- |
153
+ | `elasticsearch` | [`muscles-data-elasticsearch`](https://github.com/butkoden/muscles-data-elasticsearch) | `SearchIndexPort` | [`example_data_elasticsearch_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_elasticsearch_1) |
154
+ | `opensearch` | [`muscles-data-opensearch`](https://github.com/butkoden/muscles-data-opensearch) | `SearchIndexPort` | [`example_data_opensearch_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_opensearch_1) |
155
+ | `redis` | [`muscles-data-redis`](https://github.com/butkoden/muscles-data-redis) | `KeyValuePort`, `LockPort`, `StreamPort` | [`example_data_redis_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_redis_1) |
156
+ | `qdrant` | [`muscles-data-qdrant`](https://github.com/butkoden/muscles-data-qdrant) | `VectorSearchPort` | [`example_data_qdrant_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_qdrant_1) |
157
+ | `mongodb` | [`muscles-data-mongodb`](https://github.com/butkoden/muscles-data-mongodb) | `DocumentStorePort` | [`example_data_mongodb_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_mongodb_1) |
158
+ | `s3` | [`muscles-data-s3`](https://github.com/butkoden/muscles-data-s3) | `ObjectStorePort` | [`example_data_s3_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_s3_1) |
159
+ | `sqlalchemy` | [`muscles-data-sqlalchemy`](https://github.com/butkoden/muscles-data-sqlalchemy) | `SqlResourcePort` | [`example_data_sqlalchemy_1`](https://github.com/butkoden/muscular-example/tree/master/example_data_sqlalchemy_1) |
160
+
161
+ Register external factories in the project composition root:
162
+
163
+ ```python
164
+ from muscles_data.catalog import DataAdapterCatalog
165
+ from muscles_data_elasticsearch import ElasticsearchSearchFactory
166
+ from muscles_data_qdrant import QdrantVectorFactory
167
+ from muscles_data_redis import RedisDataFactory
168
+
169
+ catalog = DataAdapterCatalog.with_defaults()
170
+ catalog.register(ElasticsearchSearchFactory())
171
+ catalog.register(QdrantVectorFactory())
172
+ catalog.register(RedisDataFactory())
173
+ ```
174
+
175
+ `muscles-data` core does not import these packages automatically. This keeps
176
+ framework startup small and avoids pulling database SDKs into projects that do
177
+ not need them. Registration is deliberately manual: projects install and
178
+ register only the adapter packages they need.
179
+
180
+ ## Runtime API
181
+
182
+ ```python
183
+ from muscles_data import DataRuntime
184
+ from muscles_data.config import DataConfig
185
+ from muscles_data.catalog import DataAdapterCatalog
186
+ from muscles_data.ports import KeyValuePort, VectorSearchPort
187
+
188
+ runtime = DataRuntime(
189
+ config=DataConfig.from_raw({
190
+ "data": {
191
+ "resources": {
192
+ "vector.docs": {"type": "memory_vector"},
193
+ "cache.default": {"type": "memory_kv"},
194
+ }
195
+ }
196
+ }),
197
+ catalog=DataAdapterCatalog.with_defaults(),
198
+ )
199
+
200
+ vector = runtime.require_port("vector.docs", VectorSearchPort)
201
+ cache = runtime.require_port("cache.default", KeyValuePort)
202
+ ```
203
+
204
+ Adapters are initialized lazily: package init and resource listing do not open
205
+ connections. Concrete adapters are created by:
206
+
207
+ - `runtime.require_port(name, PortType)`;
208
+ - `runtime.require_resource(name, capability)`;
209
+ - `runtime.doctor()` when health checks are enabled.
210
+
211
+ ## SQL Resources
212
+
213
+ `type: sql` is the only real backend bridge built into core. It delegates to a
214
+ named registry owned by `muscles-sql` or by the project:
215
+
216
+ ```yaml
217
+ data:
218
+ resources:
219
+ sql.documents:
220
+ type: sql
221
+ connection: documents_metadata
222
+ role: read_write
223
+ ```
224
+
225
+ ```python
226
+ from muscles_data.ports import SqlResourcePort
227
+
228
+ sql = runtime.require_port("sql.documents", SqlResourcePort)
229
+ with sql.session() as session:
230
+ ...
231
+ ```
232
+
233
+ `muscles-data` does not create SQL engines, repositories, Unit of Work objects
234
+ or migrations. `session()`, `session_factory()`, `inspect()` and `doctor()`
235
+ delegate to the supplied registry.
236
+
237
+ Use `muscles-data-sqlalchemy` when a project wants direct SQLAlchemy sessions
238
+ through the same `SqlResourcePort` without using `muscles-sql`.
239
+
240
+ ## Native Escape Hatch
241
+
242
+ The preferred path is always a typed port. A project may explicitly request a
243
+ native/internal backend handle only when the resource declares the
244
+ `native_client` capability:
245
+
246
+ ```python
247
+ from muscles_data import DataCapability
248
+
249
+ handle = runtime.require_resource("cache.default", DataCapability.NATIVE_CLIENT)
250
+ native = handle.native_client()
251
+ ```
252
+
253
+ This is an advanced escape hatch for project-specific operations. Framework
254
+ packages should not build their primary logic on native clients. Native handles,
255
+ credentials and raw payloads are never included in inspect/doctor output.
256
+
257
+ ## Actions
258
+
259
+ - `data.resources.list` — list configured resources, capabilities and lazy init
260
+ state without health checks.
261
+ - `data.resource.inspect` — inspect one resource with redacted options.
262
+ - `data.doctor` — validate factories and run safe health checks with partial
263
+ failure reporting.
264
+
265
+ All actions are normal Muscles actions registered through the core action
266
+ contract. Protocol packages see them through `ActionDispatcher`; there is no
267
+ package-specific protocol routing.
268
+
269
+ ## Telemetry
270
+
271
+ `muscles-data` resolves telemetry through the neutral Muscles provider. It does
272
+ not import `muscles-otel` directly.
273
+
274
+ Safe attributes are resource name/type, capability, operation, status and safe
275
+ counts. Do not add DSNs, tokens, passwords, raw query payloads, document text,
276
+ object content, vector payloads or native clients to spans.
277
+
278
+ ## Examples
279
+
280
+ Run the core smoke examples:
281
+
282
+ ```bash
283
+ PYTHONPATH=../muscles/src:src python3 examples/run_data_runtime.py
284
+ PYTHONPATH=../muscles/src:src python3 examples/run_sql_resource_port.py
285
+ ```
286
+
287
+ Real backend examples live in
288
+ [`muscular-example`](https://github.com/butkoden/muscular-example) as
289
+ `example_data_[adapter]_1` packages.
290
+
291
+ Run tests:
292
+
293
+ ```bash
294
+ PYTHONPATH=../muscles/src:src python3 -m pytest -q
295
+ ```
296
+
297
+ Run the real-backend integration suite locally (Docker and the optional SDKs
298
+ are required):
299
+
300
+ ```bash
301
+ make data-integration-test
302
+ ```
303
+
304
+ The command starts isolated Elasticsearch, OpenSearch, Qdrant, Redis,
305
+ MongoDB, MinIO and PostgreSQL containers, runs every adapter's contract and
306
+ backend lifecycle tests, and removes the containers on exit. Set
307
+ `MUSCLES_DATA_PYTHON` to use a different Python environment. The compose file
308
+ and port map are documented in
309
+ [`infra/docker-compose.integration.yml`](infra/docker-compose.integration.yml).
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "muscles-data"
7
+ version = "0.1.0"
8
+ description = "Framework-level named data resources and typed ports for Muscles"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Denis B.", email = "denis@butko.info" }]
13
+ dependencies = [
14
+ "muscles>=1.0.0rc1,<2.0.0",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "pytest>=8.0,<9.0",
20
+ ]
21
+
22
+ [tool.pytest.ini_options]
23
+ testpaths = ["tests"]
24
+
25
+ [tool.setuptools]
26
+ package-dir = {"" = "src"}
27
+
28
+ [tool.setuptools.packages.find]
29
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+