haystack-py 0.1.3__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 (49) hide show
  1. haystack_py-0.1.3.dist-info/METADATA +446 -0
  2. haystack_py-0.1.3.dist-info/RECORD +49 -0
  3. haystack_py-0.1.3.dist-info/WHEEL +4 -0
  4. haystack_py-0.1.3.dist-info/licenses/LICENSE +21 -0
  5. hs_py/__init__.py +160 -0
  6. hs_py/__main__.py +119 -0
  7. hs_py/_scram_core.py +309 -0
  8. hs_py/auth.py +406 -0
  9. hs_py/auth_types.py +107 -0
  10. hs_py/client.py +581 -0
  11. hs_py/content_negotiation.py +144 -0
  12. hs_py/convert.py +83 -0
  13. hs_py/encoding/__init__.py +37 -0
  14. hs_py/encoding/csv.py +103 -0
  15. hs_py/encoding/json.py +667 -0
  16. hs_py/encoding/scanner.py +638 -0
  17. hs_py/encoding/trio.py +292 -0
  18. hs_py/encoding/zinc.py +337 -0
  19. hs_py/errors.py +57 -0
  20. hs_py/fastapi_server.py +477 -0
  21. hs_py/filter/__init__.py +34 -0
  22. hs_py/filter/ast.py +121 -0
  23. hs_py/filter/eval.py +178 -0
  24. hs_py/filter/lexer.py +316 -0
  25. hs_py/filter/parser.py +168 -0
  26. hs_py/grid.py +212 -0
  27. hs_py/kinds.py +302 -0
  28. hs_py/metrics.py +68 -0
  29. hs_py/ontology/__init__.py +62 -0
  30. hs_py/ontology/defs.py +138 -0
  31. hs_py/ontology/namespace.py +261 -0
  32. hs_py/ontology/normalize.py +153 -0
  33. hs_py/ontology/rdf.py +131 -0
  34. hs_py/ontology/reflect.py +109 -0
  35. hs_py/ontology/taxonomy.py +114 -0
  36. hs_py/ops.py +423 -0
  37. hs_py/py.typed +0 -0
  38. hs_py/redis_ops.py +127 -0
  39. hs_py/storage/__init__.py +34 -0
  40. hs_py/storage/memory.py +338 -0
  41. hs_py/storage/protocol.py +178 -0
  42. hs_py/storage/redis.py +789 -0
  43. hs_py/storage/timescale.py +867 -0
  44. hs_py/tls.py +311 -0
  45. hs_py/watch.py +191 -0
  46. hs_py/ws.py +399 -0
  47. hs_py/ws_client.py +1432 -0
  48. hs_py/ws_codec.py +147 -0
  49. hs_py/ws_server.py +477 -0
@@ -0,0 +1,446 @@
1
+ Metadata-Version: 2.4
2
+ Name: haystack-py
3
+ Version: 0.1.3
4
+ Summary: Async Project Haystack client and ontology library for Python
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.13
8
+ Requires-Dist: aiohttp>=3.10
9
+ Requires-Dist: cryptography>=42.0
10
+ Requires-Dist: orjson>=3.10
11
+ Requires-Dist: rdflib>=7.0
12
+ Requires-Dist: websockets>=14.0
13
+ Provides-Extra: all
14
+ Requires-Dist: asyncpg>=0.30; extra == 'all'
15
+ Requires-Dist: fastapi>=0.115; extra == 'all'
16
+ Requires-Dist: redis[hiredis]>=5.2; extra == 'all'
17
+ Requires-Dist: uvicorn[standard]>=0.32; extra == 'all'
18
+ Provides-Extra: server
19
+ Requires-Dist: fastapi>=0.115; extra == 'server'
20
+ Requires-Dist: redis[hiredis]>=5.2; extra == 'server'
21
+ Requires-Dist: uvicorn[standard]>=0.32; extra == 'server'
22
+ Provides-Extra: timescale
23
+ Requires-Dist: asyncpg>=0.30; extra == 'timescale'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # haystack-py
27
+
28
+ [![PyPI](https://img.shields.io/pypi/v/haystack-py)](https://pypi.org/project/haystack-py/)
29
+ [![Python](https://img.shields.io/pypi/pyversions/haystack-py)](https://pypi.org/project/haystack-py/)
30
+ [![License](https://img.shields.io/github/license/jscott3201/hs-py)](LICENSE)
31
+ [![CI](https://github.com/jscott3201/hs-py/actions/workflows/ci.yml/badge.svg)](https://github.com/jscott3201/hs-py/actions/workflows/ci.yml)
32
+
33
+ Asynchronous [Project Haystack](https://project-haystack.org/) client and server library for Python 3.13+. HTTP and WebSocket transports, four wire formats, SCRAM-SHA-256 and mTLS authentication, pluggable storage backends (Redis, TimescaleDB), and full ontology support. Built on native `asyncio`.
34
+
35
+ [Documentation](https://jscott3201.github.io/hs-py/) | [Getting Started](https://jscott3201.github.io/hs-py/getting-started.html) | [API Reference](https://jscott3201.github.io/hs-py/api/index.html) | [Changelog](CHANGELOG.md)
36
+
37
+ ```python
38
+ from hs_py import Client
39
+
40
+ async with Client("http://server/api", "admin", "secret") as c:
41
+ about = await c.about()
42
+ points = await c.read("point and temp and sensor")
43
+ ```
44
+
45
+ ## Table of Contents
46
+
47
+ - [Features](#features)
48
+ - [Installation](#installation)
49
+ - [Quick Start](#quick-start)
50
+ - [Storage Backends](#storage-backends)
51
+ - [Architecture](#architecture)
52
+ - [Configuration](#configuration)
53
+ - [Testing](#testing)
54
+ - [Requirements](#requirements)
55
+ - [License](#license)
56
+
57
+ ## Features
58
+
59
+ | Category | Highlights |
60
+ |----------|-----------|
61
+ | **Transports** | HTTP with aiohttp, WebSocket with `websockets` sans-I/O, persistent connections, per-message deflate compression |
62
+ | **Client** | `Client` (HTTP) and `WebSocketClient` with all 13 standard ops, batch requests, watch subscriptions, auto-auth |
63
+ | **Server** | FastAPI application factory, SCRAM-SHA-256 middleware, content negotiation (JSON/Zinc/Trio/CSV), standalone `WebSocketServer` |
64
+ | **Wire Formats** | JSON v3/v4 (`orjson`), Zinc (text), Trio (tagged records), CSV (export-only) |
65
+ | **Authentication** | SCRAM-SHA-256 over HTTP and WebSocket, PLAINTEXT fallback, token-based WebSocket auth, mTLS with `CertAuthenticator` |
66
+ | **TLS** | TLS 1.3 enforced, mutual authentication, test certificate generation (EC P-256), `TLSConfig` dataclass |
67
+ | **Storage** | Pluggable `StorageAdapter` protocol with Redis (RediSearch + RedisTimeSeries), TimescaleDB (asyncpg + JSONB), and in-memory backends |
68
+ | **Data Model** | All Haystack value types as frozen dataclasses, `Grid` / `GridBuilder` as universal message format |
69
+ | **Filters** | Recursive descent parser, AST representation, evaluation against dicts and grids, SQL pushdown for JSONB/RediSearch |
70
+ | **Ontology** | Def/Lib/Namespace model, taxonomy queries, tag normalization, dict-to-def reflection |
71
+ | **WebSocket Extras** | `ReconnectingWebSocketClient` with backoff, `WebSocketPool` / `ChannelClient` multiplexing, binary frame codec |
72
+ | **Observability** | `MetricsHooks` for connection, message, request, and error callbacks |
73
+ | **Watch** | Server-side `WatchState` delta encoding, client-side `WatchAccumulator` delta merging |
74
+ | **Quality** | 1,200+ tests, 69 end-to-end integration tests, 122 TimescaleDB tests, mypy strict, ruff linting, frozen dataclasses throughout |
75
+
76
+ ## Installation
77
+
78
+ ```bash
79
+ pip install haystack-py
80
+ ```
81
+
82
+ Optional extras:
83
+
84
+ ```bash
85
+ pip install haystack-py[server] # FastAPI + Redis backend (server-side)
86
+ pip install haystack-py[timescale] # TimescaleDB/PostgreSQL backend
87
+ pip install haystack-py[rdf] # RDF ontology import (rdflib)
88
+ pip install haystack-py[all] # All optional dependencies
89
+ ```
90
+
91
+ ### Development
92
+
93
+ ```bash
94
+ git clone https://github.com/jscott3201/hs-py.git
95
+ cd hs-py
96
+ uv sync --group dev
97
+ ```
98
+
99
+ ## Quick Start
100
+
101
+ ### HTTP Client
102
+
103
+ ```python
104
+ import asyncio
105
+ from hs_py import Client, Ref
106
+
107
+
108
+ async def main():
109
+ async with Client("http://server/api", "admin", "secret") as c:
110
+ # Server info
111
+ about = await c.about()
112
+ print(about[0]["serverName"])
113
+
114
+ # Filter-based read
115
+ sites = await c.read("site")
116
+ for row in sites:
117
+ print(row.get("dis"), row.get("id"))
118
+
119
+ # ID-based read
120
+ entities = await c.read_by_ids([Ref("site-1"), Ref("equip-2")])
121
+
122
+ # Navigation
123
+ nav = await c.nav() # root sites
124
+ children = await c.nav(Ref("site-1")) # site's equips
125
+
126
+ # History
127
+ history = await c.his_read(Ref("point-1"), "yesterday")
128
+ for row in history:
129
+ print(row["ts"], row["val"])
130
+
131
+ # Write a point value
132
+ await c.point_write(Ref("point-1"), level=8, val=72.5)
133
+
134
+
135
+ asyncio.run(main())
136
+ ```
137
+
138
+ ### WebSocket Client
139
+
140
+ ```python
141
+ import asyncio
142
+ from hs_py import WebSocketClient, Grid, Ref
143
+
144
+
145
+ async def main():
146
+ async with WebSocketClient("ws://server/api/ws", auth_token="token") as ws:
147
+ about = await ws.about()
148
+
149
+ # Batch: multiple ops in one round-trip
150
+ results = await ws.batch(
151
+ ("about", Grid.make_empty()),
152
+ ("read", Grid.make_rows([{"filter": "site"}])),
153
+ ("read", Grid.make_rows([{"filter": "point and temp"}])),
154
+ )
155
+
156
+ # Watch: subscribe to entity changes
157
+ watch = await ws.watch_sub([Ref("p-1"), Ref("p-2")], "my-watch")
158
+ watch_id = watch.meta["watchId"]
159
+ poll = await ws.watch_poll(watch_id)
160
+ await ws.watch_close(watch_id)
161
+
162
+
163
+ asyncio.run(main())
164
+ ```
165
+
166
+ ### Server
167
+
168
+ ```python
169
+ import asyncio
170
+ from hs_py.ops import HaystackOps
171
+ from hs_py.storage.memory import MemoryAdapter
172
+ from hs_py.auth_types import SimpleAuthenticator
173
+ from hs_py.fastapi_server import create_app
174
+ import uvicorn
175
+
176
+
177
+ async def main():
178
+ storage = MemoryAdapter()
179
+ await storage.start()
180
+ await storage.load_entities([
181
+ {"id": Ref("s1"), "site": MARKER, "dis": "My Building"},
182
+ ])
183
+
184
+ ops = HaystackOps(storage=storage)
185
+ auth = SimpleAuthenticator({"admin": "secret"})
186
+ app = create_app(ops, authenticator=auth)
187
+ config = uvicorn.Config(app, host="0.0.0.0", port=8080)
188
+ server = uvicorn.Server(config)
189
+ await server.serve()
190
+
191
+
192
+ asyncio.run(main())
193
+ ```
194
+
195
+ ### Wire Formats
196
+
197
+ ```python
198
+ from hs_py.encoding import json, zinc, trio, csv
199
+ from hs_py.encoding.json import JsonVersion
200
+
201
+ # JSON v4
202
+ grid = json.decode_grid(data, version=JsonVersion.V4)
203
+ json_bytes = json.encode_grid(grid, version=JsonVersion.V4)
204
+
205
+ # Zinc
206
+ zinc_text = zinc.encode_grid(grid)
207
+ grid = zinc.decode_grid(zinc_text)
208
+
209
+ # Trio records
210
+ records = trio.parse_trio(trio_text)
211
+ trio_text = trio.encode_trio(records)
212
+
213
+ # CSV (encode-only)
214
+ csv_text = csv.encode_grid(grid)
215
+ ```
216
+
217
+ ### Filters
218
+
219
+ ```python
220
+ from hs_py import MARKER, parse, evaluate, evaluate_grid
221
+
222
+ # Parse filter to AST
223
+ ast = parse("point and temp and sensor")
224
+
225
+ # Evaluate against a dict
226
+ entity = {"point": MARKER, "temp": MARKER, "sensor": MARKER}
227
+ assert evaluate(ast, entity) is True
228
+
229
+ # Filter a grid
230
+ matching = evaluate_grid(ast, grid)
231
+ ```
232
+
233
+ ### Ontology
234
+
235
+ ```python
236
+ from hs_py.ontology.namespace import Namespace, load_lib_from_trio
237
+ from hs_py.ontology.reflect import reflect
238
+
239
+ lib = load_lib_from_trio(trio_text)
240
+ ns = Namespace([lib])
241
+
242
+ # Taxonomy queries
243
+ assert ns.is_subtype("ahu", "equip")
244
+ subtypes = ns.subtypes("equip") # [ahu, vav, ...]
245
+
246
+ # Reflect entities against definitions
247
+ defs = reflect(ns, entity_dict)
248
+ ```
249
+
250
+ ## Storage Backends
251
+
252
+ haystack-py defines a `StorageAdapter` protocol that decouples server operations from data storage. Three implementations are provided:
253
+
254
+ | Backend | Module | Best For |
255
+ |---------|--------|----------|
256
+ | **Memory** | `storage.memory` | Testing, prototyping, small datasets |
257
+ | **Redis** | `storage.redis` | Production with RediSearch full-text and RedisTimeSeries |
258
+ | **TimescaleDB** | `storage.timescale` | Production with PostgreSQL JSONB and time-series hypertables |
259
+
260
+ ### Redis
261
+
262
+ ```python
263
+ from hs_py.storage.redis import RedisAdapter, create_redis_client
264
+
265
+ r = await create_redis_client("redis://localhost:6379")
266
+ adapter = RedisAdapter(r)
267
+ await adapter.start()
268
+ ```
269
+
270
+ Features: RediSearch indexes for filter queries, RedisTimeSeries for history, pub/sub for watch notifications.
271
+
272
+ ### TimescaleDB
273
+
274
+ ```python
275
+ from hs_py.storage.timescale import TimescaleAdapter, create_timescale_pool
276
+
277
+ pool = await create_timescale_pool("postgresql://localhost/haystack")
278
+ adapter = TimescaleAdapter(pool)
279
+ await adapter.start() # Creates schema + hypertable
280
+ ```
281
+
282
+ Features: JSONB entity storage with GIN indexes, filter AST → SQL pushdown, hypertable time-series, COPY-based bulk loading.
283
+
284
+ ## Architecture
285
+
286
+ ```
287
+ src/hs_py/
288
+ kinds.py Haystack value types (Marker, Number, Ref, Coord, etc.)
289
+ grid.py Grid, Col, GridBuilder -- universal message format
290
+ errors.py Exception hierarchy (HaystackError, CallError, AuthError)
291
+ auth.py SCRAM-SHA-256 / PLAINTEXT client auth handshake
292
+ auth_types.py Authenticator protocol, SimpleAuthenticator, CertAuthenticator
293
+ client.py Async HTTP client with all standard ops
294
+ ops.py HaystackOps base class with storage-backed op dispatch
295
+ fastapi_server.py FastAPI application factory, SCRAM middleware, WebSocket endpoint
296
+ metrics.py MetricsHooks for transport-level observability
297
+ tls.py TLSConfig, SSL context builders, certificate generation
298
+ security.py Security hardening utilities
299
+ watch.py WatchState (server delta), WatchAccumulator (client merge)
300
+ ws.py Sans-I/O WebSocket wrapper (websockets library)
301
+ ws_client.py WebSocketClient, ReconnectingWebSocketClient, WebSocketPool
302
+ ws_server.py Standalone WebSocket server with SCRAM auth and batch dispatch
303
+ ws_codec.py Binary frame codec (4-byte header + JSON payload)
304
+ encoding/
305
+ json.py JSON v3/v4 encode/decode via orjson
306
+ zinc.py Zinc text format encode/decode
307
+ trio.py Trio tagged record format
308
+ csv.py CSV export (encode-only, lossy)
309
+ scanner.py Shared Zinc value scanning
310
+ filter/
311
+ ast.py Filter AST nodes (Has, Missing, Cmp, And, Or, Path)
312
+ lexer.py Filter expression tokenizer
313
+ parser.py Recursive descent parser
314
+ eval.py Filter evaluation against dicts/grids
315
+ storage/
316
+ protocol.py StorageAdapter protocol (11 async methods)
317
+ memory.py In-memory adapter for testing
318
+ redis.py Redis + RediSearch + RedisTimeSeries adapter
319
+ timescale.py PostgreSQL/TimescaleDB adapter via asyncpg
320
+ ontology/
321
+ defs.py Def and Lib frozen dataclasses
322
+ namespace.py Namespace container, symbol resolution
323
+ taxonomy.py Subtype tree, tag inheritance
324
+ normalize.py Normalization pipeline
325
+ reflect.py Dict-to-def reflection engine
326
+ ```
327
+
328
+ ### Key Classes
329
+
330
+ | Class | Module | Purpose |
331
+ |-------|--------|---------|
332
+ | `Client` | `client` | Async HTTP client with SCRAM auth and all Haystack ops |
333
+ | `WebSocketClient` | `ws_client` | Persistent WebSocket client with batch and watch support |
334
+ | `ReconnectingWebSocketClient` | `ws_client` | Auto-reconnecting WebSocket client with exponential backoff |
335
+ | `WebSocketPool` | `ws_client` | Multiplexed channels over a single WebSocket connection |
336
+ | `HaystackOps` | `ops` | Storage-backed server operation handler for all 13 ops |
337
+ | `WebSocketServer` | `ws_server` | Standalone WebSocket server with SCRAM auth and push |
338
+ | `Grid` | `grid` | Universal Haystack message format (immutable) |
339
+ | `GridBuilder` | `grid` | Fluent builder for constructing grids |
340
+ | `StorageAdapter` | `storage.protocol` | Protocol for pluggable storage backends |
341
+ | `TLSConfig` | `tls` | TLS certificate configuration |
342
+ | `MetricsHooks` | `metrics` | Optional observability callbacks |
343
+ | `WatchState` | `watch` | Server-side watch delta computation |
344
+ | `WatchAccumulator` | `watch` | Client-side watch delta merging |
345
+ | `Namespace` | `ontology.namespace` | Resolved ontology with taxonomy queries |
346
+
347
+ ### Error Handling
348
+
349
+ All client methods raise from a common exception hierarchy:
350
+
351
+ ```python
352
+ from hs_py import HaystackError, CallError, AuthError, NetworkError
353
+
354
+ # HaystackError Base for all haystack-py errors
355
+ # CallError Server returned an error grid
356
+ # AuthError Authentication failure
357
+ # NetworkError Transport-level failure
358
+ ```
359
+
360
+ ## Configuration
361
+
362
+ ### Docker Compose
363
+
364
+ The included `docker/docker-compose.yml` provides a complete development stack:
365
+
366
+ ```bash
367
+ docker compose -f docker/docker-compose.yml up -d
368
+ ```
369
+
370
+ Services:
371
+
372
+ | Service | Port | Description |
373
+ |---------|------|-------------|
374
+ | `server` | 8080 | FastAPI Haystack server with SCRAM auth |
375
+ | `redis` | 6379 | Redis with RediSearch and RedisTimeSeries |
376
+ | `timescaledb` | 5432 | TimescaleDB (PostgreSQL 16) |
377
+ | `redis-tls` | 6380 | Redis with mTLS for TLS integration tests |
378
+
379
+ Environment variables for the server:
380
+
381
+ | Variable | Default | Description |
382
+ |----------|---------|-------------|
383
+ | `REDIS_URL` | `redis://redis:6379` | Redis connection URL |
384
+ | `HAYSTACK_USER` | `admin` | SCRAM username |
385
+ | `HAYSTACK_PASS` | `secret` | SCRAM password |
386
+
387
+ ### Seed Data
388
+
389
+ The `_data/` directory contains Project Haystack example building datasets in JSON v4 format:
390
+
391
+ - **Alpha** — 2,032 entities (1 site, 184 equips, 1,846 points)
392
+ - **Bravo** — 1,077 entities (1 site, 149 equips, 918 points)
393
+
394
+ These are loaded automatically by the Docker server on startup.
395
+
396
+ ## Testing
397
+
398
+ ```bash
399
+ make test # 1,200+ unit tests
400
+ make lint # ruff check + format verification
401
+ make typecheck # mypy strict
402
+ make check # lint + typecheck + test (all of the above)
403
+ make coverage # tests with coverage report
404
+ make fix # auto-fix lint/format issues
405
+ make docs # sphinx-build
406
+ ```
407
+
408
+ ### Docker Integration Tests
409
+
410
+ End-to-end testing against real services with full SCRAM authentication:
411
+
412
+ ```bash
413
+ make docker-server # Start Redis + FastAPI server stack
414
+ make docker-test-e2e # 69 end-to-end tests (HTTP, WebSocket, auth, history, watch)
415
+ make docker-server-clean # Tear down server stack
416
+ ```
417
+
418
+ ### Storage Backend Tests
419
+
420
+ ```bash
421
+ make docker-test # Redis adapter integration tests
422
+ make docker-test-tls # Redis mTLS integration tests
423
+ make docker-test-timescale # 122 TimescaleDB integration tests
424
+ ```
425
+
426
+ ### Cleanup
427
+
428
+ ```bash
429
+ make docker-clean # Remove Redis containers
430
+ make docker-clean-timescale # Remove TimescaleDB containers
431
+ ```
432
+
433
+ ## Requirements
434
+
435
+ - Python >= 3.13
436
+ - [aiohttp](https://docs.aiohttp.org/) >= 3.10
437
+ - [orjson](https://github.com/ijl/orjson) >= 3.10
438
+ - [cryptography](https://cryptography.io/) >= 42.0
439
+ - [websockets](https://websockets.readthedocs.io/) >= 14.0
440
+ - Optional: [FastAPI](https://fastapi.tiangolo.com/) + [Redis](https://redis.io/) for server mode
441
+ - Optional: [asyncpg](https://magicstack.github.io/asyncpg/) for TimescaleDB backend
442
+ - Docker and Docker Compose for integration tests
443
+
444
+ ## License
445
+
446
+ MIT
@@ -0,0 +1,49 @@
1
+ hs_py/__init__.py,sha256=1a1NH5CbNGKTeDbwx4SdqobDoR_-eh-0rRmf0d-AgXw,3782
2
+ hs_py/__main__.py,sha256=MQtHV1A3od6WtqwGPYd3Ya9m_KJrnPVBks0TpAvjY6o,3793
3
+ hs_py/_scram_core.py,sha256=UquYTt96USmSn85fuwwq-5YY4PTD-tygkDqFpiFc65Q,9845
4
+ hs_py/auth.py,sha256=3aVD_tnjsu3nar4yTUB9M9w4lDA7YCXFbxlsQ4tsiak,13776
5
+ hs_py/auth_types.py,sha256=fBTJFokvodz4ar2645SeYHOa07_47biJUyrOzRYNwkc,3660
6
+ hs_py/client.py,sha256=Fr9wrAQBW3lQ1hHykl7sjNs62dW4-3xoi5n37FNhz0w,22654
7
+ hs_py/content_negotiation.py,sha256=UGULngWqCcHxL2YLO5iQN5XNFRhjd6jZ30_jLPQVMlE,4577
8
+ hs_py/convert.py,sha256=RTkINlzsKF8rHYgbip78MvbGH3zJcd9hmVBmeazUP64,2911
9
+ hs_py/errors.py,sha256=hKW_rWe_RK8_JdR5Qe7gbmEO8WLGv1GDHX9Dp22HoYU,1330
10
+ hs_py/fastapi_server.py,sha256=aRysIRNUhu3ZCBIRuUFQU6F2DIEIazjeNAftqjIcDtA,18273
11
+ hs_py/grid.py,sha256=VDHqB43uA1kcZJzqbFyMoNDgZ9SRjeMllW8j7ZV41ME,6153
12
+ hs_py/kinds.py,sha256=sbI5ziUKakktb39W9xSA15npaK7RHE0sF9hku1jtcZQ,7451
13
+ hs_py/metrics.py,sha256=_tHSrAqbYUls_D2AOHvLR78IfLqmDiV5FDlnspRhjFw,2518
14
+ hs_py/ops.py,sha256=cb7JtvECcbbf5q9OLeJEbrEWXLm7MHaO6shvnx9Rlsw,15300
15
+ hs_py/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ hs_py/redis_ops.py,sha256=XUJCo3gUeuesT4qoba-iJHJAYl0S6VTH9SyWs-Wk2Y0,4469
17
+ hs_py/tls.py,sha256=Yk6-UR1jPigNBq4mKDSstSu_CjflyAc-tvzbsENjO10,11056
18
+ hs_py/watch.py,sha256=8n0qVxtv5GMpuHcKEiZbzyH29oJ7P9FgUi00n-gJ7SI,6501
19
+ hs_py/ws.py,sha256=CswvUPClez3h72Fuy8OzP0x0Al3B_VslHmeEi0MiKSo,14107
20
+ hs_py/ws_client.py,sha256=N7QeU0BonL-YJuMU9J-P8YO1RPcPvT4s4PUauhZyN70,54310
21
+ hs_py/ws_codec.py,sha256=rSQ5fODumoP2m3B5pxuFVbX7X5m5oIDcetY6WBsqUpE,4281
22
+ hs_py/ws_server.py,sha256=hMNGNETIsojYapDZEguR2iTXdDhEJyMzYgne5B3CpXo,19670
23
+ hs_py/encoding/__init__.py,sha256=xd40h674PlWQ-86_VDwrnYyOvDGkBfIvZJp0Vkx1tLk,1046
24
+ hs_py/encoding/csv.py,sha256=8_JNj7-s8EFTEO-z3tdUXvGfuQFoN0poJQGJO2dbczU,2929
25
+ hs_py/encoding/json.py,sha256=Y0qu52AQKvfrQpbmUTSIsj3gECws8KQFqgNkjE-I2YM,20421
26
+ hs_py/encoding/scanner.py,sha256=Qy7n6V7M7T26qOa0e7KlD8-ttBeVFiexCHnkm-o0BOA,18771
27
+ hs_py/encoding/trio.py,sha256=J2Z_C3jo3Q_FcL1a6ogWFK-e3iBF4CRDyhV-J4LdDwU,9062
28
+ hs_py/encoding/zinc.py,sha256=WkZsm4lvkjLXRNHExSAeIXVWjEkzP_50P-hXWb4ogJ8,8606
29
+ hs_py/filter/__init__.py,sha256=hEfsw8y67NDeBVwBBXU7N-vl6WDUWhvEMRXgIg5A1po,738
30
+ hs_py/filter/ast.py,sha256=TQv17hDvMNELiiG942qB8mL6DjGqrOsMaQcqzyroU9I,2339
31
+ hs_py/filter/eval.py,sha256=HJ0OktQEmUxGBV2EcwCqumzVsSbF2etEq3n0n-OZ-Ro,5662
32
+ hs_py/filter/lexer.py,sha256=FclM5FWJUdCz1-usDSVLDMJOUOo-btL7xFz8yi0bh-8,10045
33
+ hs_py/filter/parser.py,sha256=5tUABoOGL-rdKSoCet8Io0ZUYivvBrnYJHOlLlTcyRo,4889
34
+ hs_py/ontology/__init__.py,sha256=iDU8t6wTj8lbyXyoSepLHngW8sw79GDHyVt9nwnw6qQ,1514
35
+ hs_py/ontology/defs.py,sha256=VPbo1F6VYmbUkmycFJj2BrMA7bwFaCuT7vWKLXhP8aQ,4214
36
+ hs_py/ontology/namespace.py,sha256=VwMdUB8YbYBHyn1ORSTp7iNZjoJUZPi_Cc70POcvleQ,8673
37
+ hs_py/ontology/normalize.py,sha256=ks3y60UAHbjxpMYD32Llz6LexyrM8Biz5TTFovwI0nM,4803
38
+ hs_py/ontology/rdf.py,sha256=3Ii3ABSF5T6p0SZwLM3HYBlU9wB7Gor11C_rWDWt4DU,3975
39
+ hs_py/ontology/reflect.py,sha256=n20mtULp8hrq4nMEnGY5GFkm47-VcvJ7niGgNAF6wuE,3424
40
+ hs_py/ontology/taxonomy.py,sha256=jLlFy6ELKZNg4AVFxqXPvvmjdEYk7k37Ms9uopEbI40,3307
41
+ hs_py/storage/__init__.py,sha256=4h4lVreYnOND_YyyDQHR1OEqITzBAj9qrMUCgzlRIiA,942
42
+ hs_py/storage/memory.py,sha256=3DLOaQaXDCMgNxca_iZrXTQZ_pfVlgNri2Ch5G_Iq6k,11937
43
+ hs_py/storage/protocol.py,sha256=axFXSoAqjvEOOOqYB2XApNdfIilwqVmVXJ3avRBMnMU,5874
44
+ hs_py/storage/redis.py,sha256=hERp5OI6dPq6_eKqkxdl5QGs77GdmWqx8Y8vYF6AqPg,28289
45
+ hs_py/storage/timescale.py,sha256=EZx_XvOZkMkHiUCaps6yoXLgTm1BBJmK8ZYpWAnMePk,29827
46
+ haystack_py-0.1.3.dist-info/METADATA,sha256=YkMiw1tRnounuxeNUx8Efb8FRKhZ90-EcXEt0GkICr0,15792
47
+ haystack_py-0.1.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
48
+ haystack_py-0.1.3.dist-info/licenses/LICENSE,sha256=3GO_-MGsYcCdBLOP5qFVRUCgfAKrPLNEG7zN8JaauXc,1069
49
+ haystack_py-0.1.3.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Justin Scott
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.
hs_py/__init__.py ADDED
@@ -0,0 +1,160 @@
1
+ """hs-py — Async Project Haystack client library for Python."""
2
+
3
+ __version__ = "0.1.3"
4
+
5
+ from hs_py.auth_types import (
6
+ Authenticator,
7
+ CertAuthenticator,
8
+ ScramCredentials,
9
+ SimpleAuthenticator,
10
+ )
11
+ from hs_py.client import Client
12
+ from hs_py.convert import grid_to_pythonic
13
+ from hs_py.encoding import JsonVersion
14
+ from hs_py.errors import AuthError, CallError, HaystackError, NetworkError
15
+ from hs_py.filter import ParseError, evaluate, evaluate_grid, parse
16
+ from hs_py.grid import Col, Grid, GridBuilder
17
+ from hs_py.kinds import (
18
+ MARKER,
19
+ NA,
20
+ REMOVE,
21
+ Coord,
22
+ Marker,
23
+ Na,
24
+ Number,
25
+ Ref,
26
+ Remove,
27
+ Symbol,
28
+ Uri,
29
+ XStr,
30
+ )
31
+ from hs_py.metrics import MetricsHooks
32
+ from hs_py.ontology.rdf import export_jsonld, export_turtle
33
+ from hs_py.ops import HaystackOps
34
+ from hs_py.tls import (
35
+ TLSConfig,
36
+ build_client_ssl_context,
37
+ build_server_ssl_context,
38
+ extract_peer_cn,
39
+ extract_peer_sans,
40
+ generate_test_certificates,
41
+ )
42
+ from hs_py.watch import WatchAccumulator, WatchState
43
+ from hs_py.ws import HaystackWebSocket
44
+ from hs_py.ws_client import (
45
+ ChannelClient,
46
+ ReconnectingWebSocketClient,
47
+ WebSocketClient,
48
+ WebSocketPool,
49
+ )
50
+ from hs_py.ws_codec import (
51
+ OP_CODES,
52
+ decode_binary_frame,
53
+ encode_binary_push,
54
+ encode_binary_request,
55
+ encode_binary_response,
56
+ )
57
+ from hs_py.ws_server import WebSocketServer
58
+
59
+
60
+ def __getattr__(name: str) -> object:
61
+ if name == "RedisOps":
62
+ from hs_py.redis_ops import RedisOps
63
+
64
+ return RedisOps
65
+ if name == "RedisAdapter":
66
+ from hs_py.storage.redis import RedisAdapter
67
+
68
+ return RedisAdapter
69
+ if name == "create_redis_client":
70
+ from hs_py.redis_ops import create_redis_client
71
+
72
+ return create_redis_client
73
+ if name == "create_fastapi_app":
74
+ from hs_py.fastapi_server import create_fastapi_app
75
+
76
+ return create_fastapi_app
77
+ if name == "StorageAdapter":
78
+ from hs_py.storage.protocol import StorageAdapter
79
+
80
+ return StorageAdapter
81
+ if name == "InMemoryAdapter":
82
+ from hs_py.storage.memory import InMemoryAdapter
83
+
84
+ return InMemoryAdapter
85
+ if name == "TimescaleAdapter":
86
+ from hs_py.storage.timescale import TimescaleAdapter
87
+
88
+ return TimescaleAdapter
89
+ if name == "create_timescale_pool":
90
+ from hs_py.storage.timescale import create_timescale_pool
91
+
92
+ return create_timescale_pool
93
+ msg = f"module {__name__!r} has no attribute {name!r}"
94
+ raise AttributeError(msg)
95
+
96
+
97
+ __all__ = [
98
+ "MARKER",
99
+ "NA",
100
+ "OP_CODES",
101
+ "REMOVE",
102
+ "AuthError",
103
+ "Authenticator",
104
+ "CallError",
105
+ "CertAuthenticator",
106
+ "ChannelClient",
107
+ "Client",
108
+ "Col",
109
+ "Coord",
110
+ "Grid",
111
+ "GridBuilder",
112
+ "HaystackError",
113
+ "HaystackOps",
114
+ "HaystackWebSocket",
115
+ "InMemoryAdapter",
116
+ "JsonVersion",
117
+ "Marker",
118
+ "MetricsHooks",
119
+ "Na",
120
+ "NetworkError",
121
+ "Number",
122
+ "ParseError",
123
+ "ReconnectingWebSocketClient",
124
+ "RedisAdapter",
125
+ "RedisOps",
126
+ "Ref",
127
+ "Remove",
128
+ "ScramCredentials",
129
+ "SimpleAuthenticator",
130
+ "StorageAdapter",
131
+ "Symbol",
132
+ "TLSConfig",
133
+ "TimescaleAdapter",
134
+ "Uri",
135
+ "WatchAccumulator",
136
+ "WatchState",
137
+ "WebSocketClient",
138
+ "WebSocketPool",
139
+ "WebSocketServer",
140
+ "XStr",
141
+ "__version__",
142
+ "build_client_ssl_context",
143
+ "build_server_ssl_context",
144
+ "create_fastapi_app",
145
+ "create_redis_client",
146
+ "create_timescale_pool",
147
+ "decode_binary_frame",
148
+ "encode_binary_push",
149
+ "encode_binary_request",
150
+ "encode_binary_response",
151
+ "evaluate",
152
+ "evaluate_grid",
153
+ "export_jsonld",
154
+ "export_turtle",
155
+ "extract_peer_cn",
156
+ "extract_peer_sans",
157
+ "generate_test_certificates",
158
+ "grid_to_pythonic",
159
+ "parse",
160
+ ]