nippy-decoder 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 @@
1
+ *
Binary file
Binary file
Binary file
File without changes
@@ -0,0 +1,16 @@
1
+ {"time":"2026-02-26T21:41:18.667496+05:30","level":"WARN","source":{"function":"github.com/charmbracelet/crush/internal/config.Load","file":"github.com/charmbracelet/crush/internal/config/load.go","line":58},"msg":"No git repository detected in working directory, will limit file walk operations","depth":2,"items":100}
2
+ {"time":"2026-02-26T21:41:18.669777+05:30","level":"WARN","source":{"function":"github.com/charmbracelet/crush/internal/config.Load","file":"github.com/charmbracelet/crush/internal/config/load.go","line":66},"msg":"Detected Apple Terminal, enabling transparent mode"}
3
+ {"time":"2026-02-26T21:41:18.675466+05:30","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":55},"msg":"Fetching providers from Catwalk"}
4
+ {"time":"2026-02-26T21:41:19.065506+05:30","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":63},"msg":"Catwalk providers not modified"}
5
+ {"time":"2026-02-26T21:41:19.087574+05:30","level":"INFO","msg":"OK 20250424200609_initial.sql (1.16ms)"}
6
+ {"time":"2026-02-26T21:41:19.087836+05:30","level":"INFO","msg":"OK 20250515105448_add_summary_message_id.sql (191.29µs)"}
7
+ {"time":"2026-02-26T21:41:19.08802+05:30","level":"INFO","msg":"OK 20250624000000_add_created_at_indexes.sql (170.54µs)"}
8
+ {"time":"2026-02-26T21:41:19.088202+05:30","level":"INFO","msg":"OK 20250627000000_add_provider_to_messages.sql (172.38µs)"}
9
+ {"time":"2026-02-26T21:41:19.088547+05:30","level":"INFO","msg":"OK 20250810000000_add_is_summary_message.sql (174.96µs)"}
10
+ {"time":"2026-02-26T21:41:19.088777+05:30","level":"INFO","msg":"OK 20250812000000_add_todos_to_sessions.sql (210.38µs)"}
11
+ {"time":"2026-02-26T21:41:19.089194+05:30","level":"INFO","msg":"OK 20260127000000_add_read_files_table.sql (395.96µs)"}
12
+ {"time":"2026-02-26T21:41:19.0892+05:30","level":"INFO","msg":"goose: successfully migrated database to version: 20260127000000"}
13
+ {"time":"2026-02-26T21:41:19.091067+05:30","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent/tools/mcp.Initialize","file":"github.com/charmbracelet/crush/internal/agent/tools/mcp/init.go","line":153},"msg":"Initializing MCP clients"}
14
+ {"time":"2026-02-26T21:41:26.184106+05:30","level":"ERROR","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":812},"msg":"Error generating title with small model; trying big model","err":"Post \"https://bedrock-runtime..amazonaws.com/model/us.anthropic.claude-haiku-4-5-20251001-v1%3A0/invoke-with-response-stream\": dial tcp: lookup bedrock-runtime..amazonaws.com: no such host"}
15
+ {"time":"2026-02-27T09:16:51.594865+05:30","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*Config).RefreshOAuthToken","file":"github.com/charmbracelet/crush/internal/config/config.go","line":564},"msg":"Successfully refreshed OAuth token","provider":"copilot"}
16
+ {"time":"2026-02-27T09:47:01.79407+05:30","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*Config).RefreshOAuthToken","file":"github.com/charmbracelet/crush/internal/config/config.go","line":564},"msg":"Successfully refreshed OAuth token","provider":"copilot"}
@@ -0,0 +1,43 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ venv/
25
+ env/
26
+ ENV/
27
+
28
+ # Testing
29
+ .pytest_cache/
30
+ .coverage
31
+ htmlcov/
32
+ .tox/
33
+
34
+ # IDE
35
+ .vscode/
36
+ .idea/
37
+ *.swp
38
+ *.swo
39
+ *~
40
+
41
+ # OS
42
+ .DS_Store
43
+ Thumbs.db
@@ -0,0 +1,363 @@
1
+ # AGENTS.md
2
+
3
+ ## Project Overview
4
+
5
+ **nippy-decoder** is a pure Python decoder for Clojure Nippy-encoded data. This is a library project focused on decoding serialized data from Clojure/Java services that use the Nippy format.
6
+
7
+ - **Language**: Python 3.8+
8
+ - **Dependencies**: Zero external dependencies (stdlib only)
9
+ - **Purpose**: Decode Nippy-encoded bytes from databases (Postgres, SQLite), APIs, Kafka, Redis, etc.
10
+ - **Status**: Beta (v0.1.0)
11
+ - **License**: MIT
12
+
13
+ ## Essential Commands
14
+
15
+ ### Installation & Setup
16
+
17
+ ```bash
18
+ # Install in development mode
19
+ pip install -e .
20
+
21
+ # Install for use
22
+ pip install nippy-decoder
23
+ ```
24
+
25
+ ### Running Examples
26
+
27
+ ```bash
28
+ # Run examples (use PYTHONPATH to add src/)
29
+ PYTHONPATH=src python examples/basic_usage.py
30
+ PYTHONPATH=src python examples/database_example.py
31
+
32
+ # Or after installing in dev mode
33
+ python examples/basic_usage.py
34
+ python examples/database_example.py
35
+ ```
36
+
37
+ ### Testing
38
+
39
+ ```bash
40
+ # Install test dependency
41
+ pip install pytest
42
+
43
+ # Run all tests with verbose output
44
+ pytest tests/ -v
45
+
46
+ # Run specific test file
47
+ pytest tests/test_primitives.py -v
48
+ pytest tests/test_collections.py -v
49
+ pytest tests/test_uuid.py -v
50
+
51
+ # Run with coverage (if coverage installed)
52
+ pip install pytest-cov
53
+ pytest tests/ --cov=nippy_decoder --cov-report=html
54
+ ```
55
+
56
+ ### Building & Publishing
57
+
58
+ ```bash
59
+ # Install build tools
60
+ pip install build twine
61
+
62
+ # Build package
63
+ python -m build
64
+
65
+ # Upload to test PyPI
66
+ twine upload --repository testpypi dist/*
67
+
68
+ # Upload to production PyPI
69
+ twine upload dist/*
70
+ ```
71
+
72
+ ## Code Organization
73
+
74
+ ```
75
+ nippy-decoder/
76
+ ├── src/nippy_decoder/ # Main package
77
+ │ ├── __init__.py # Package exports: NippyDecoder, __version__
78
+ │ └── decoder.py # Core decoder logic (~200 lines)
79
+ ├── tests/ # pytest test suite
80
+ │ ├── test_primitives.py # Tests for basic types (null, bool, int, float, string, keyword)
81
+ │ ├── test_collections.py # Tests for vectors, maps, sets, lists
82
+ │ └── test_uuid.py # Tests for UUID decoding
83
+ ├── examples/ # Usage examples
84
+ │ ├── basic_usage.py # Simple decoding examples
85
+ │ └── database_example.py # Database integration examples
86
+ ├── pyproject.toml # Package metadata & build config (hatchling)
87
+ ├── README.md # User-facing documentation
88
+ ├── SETUP.md # Developer setup guide
89
+ ├── CHANGELOG.md # Version history
90
+ ├── LICENSE # MIT license
91
+ └── .gitignore # Standard Python gitignore
92
+ ```
93
+
94
+ ### Key Files
95
+
96
+ - **`src/nippy_decoder/decoder.py`**: The entire decoder implementation (~200 lines)
97
+ - `NippyDecoder` class with `decode()` method
98
+ - Private methods for reading each type (`_read()`, `_read_str()`, `_read_vec()`, `_read_map()`, etc.)
99
+ - No external dependencies - uses only `struct`, `uuid`, `json`, `io` from stdlib
100
+
101
+ - **`src/nippy_decoder/__init__.py`**: Simple package export
102
+ - Exports `NippyDecoder` class
103
+ - Defines `__version__` = "0.1.0"
104
+
105
+ ## Code Patterns & Conventions
106
+
107
+ ### Code Style
108
+
109
+ - **Simple, readable code**: No complex abstractions
110
+ - **Type hints**: Used for public API (`decode()` method signature)
111
+ - **Docstrings**: Present for public methods and module
112
+ - **No formatters/linters configured**: Use standard Python conventions
113
+ - **Line length**: Reasonable (~80-100 chars, not strictly enforced)
114
+
115
+ ### Naming Conventions
116
+
117
+ - **Classes**: PascalCase (`NippyDecoder`)
118
+ - **Methods**: snake_case (`decode()`, `_read_str()`)
119
+ - **Private methods**: Prefix with underscore (`_read()`, `_read_vec()`)
120
+ - **Constants**: UPPER_SNAKE_CASE (`MAGIC_HEADER`, `VERSION`)
121
+ - **Variables**: snake_case (`type_code`, `result`)
122
+
123
+ ### Error Handling
124
+
125
+ - **Raises `ValueError`** for all decode errors:
126
+ - Invalid header: `"invalid nippy header"`
127
+ - Unsupported version: `"unsupported nippy version: X"`
128
+ - Unsupported type: `"unsupported type: X"`
129
+ - Unexpected EOF: `"unexpected end of stream"`
130
+
131
+ - **Return `None`** for empty/missing data (not an error)
132
+
133
+ ### Data Type Conversions
134
+
135
+ **Clojure → Python mappings:**
136
+ - `nil` → `None`
137
+ - `true`/`false` → `True`/`False`
138
+ - integers → `int`
139
+ - floats/doubles → `float`
140
+ - strings → `str`
141
+ - **keywords** (`:status`) → `str` (`"status"` without colon)
142
+ - vectors → `list`
143
+ - maps → `dict`
144
+ - sets → `set`
145
+ - byte arrays → `bytes` (or `dict`/`list` if auto-parsed as JSON)
146
+ - UUID → `str` (UUID string representation)
147
+
148
+ **Important**: Clojure keywords lose their `:` prefix when decoded to Python strings.
149
+
150
+ ### Key Implementation Details
151
+
152
+ 1. **Big-endian encoding**: All multi-byte integers use big-endian (`>` in struct format)
153
+
154
+ 2. **Type codes**: Each value is prefixed with a type byte (0-127)
155
+ - 0-3: Primitives (zero, true, false, nil)
156
+ - 4-8, 40-43: Integers (various sizes)
157
+ - 9-10: Floats (f32, f64)
158
+ - 11-14, 105: Strings
159
+ - 15-18: Byte arrays
160
+ - 19-23: Vectors
161
+ - 24-26, 112: Maps
162
+ - 27-29: Sets
163
+ - 30-32: Lists
164
+ - 33-35, 106: Keywords
165
+ - 36: UUID
166
+ - 37: Metadata (skip and read next)
167
+ - 44-127: Legacy collection types (treated as vectors)
168
+
169
+ 3. **Length prefixes**: Collections and strings use 1, 2, or 4-byte length prefixes
170
+ - Type determines prefix size
171
+ - Lengths are unsigned big-endian integers
172
+
173
+ 4. **UUID encoding**: Stored as two signed 64-bit longs (MSB, LSB)
174
+ - Conversion handles signed→unsigned for Python UUID construction
175
+
176
+ 5. **Map keys**: Unhashable keys (list, dict, set) converted to strings
177
+
178
+ 6. **Byte array special case**: If starts with `{` or `[`, attempts JSON parse
179
+
180
+ 7. **Metadata handling**: Type 37 skips metadata and returns the actual value
181
+
182
+ ## Testing Approach
183
+
184
+ ### Test Structure
185
+
186
+ - **pytest** framework
187
+ - Tests organized by type category:
188
+ - `test_primitives.py`: Basic types
189
+ - `test_collections.py`: Vectors, maps, sets
190
+ - `test_uuid.py`: UUID handling
191
+ - Each test manually constructs Nippy bytes and validates decoded result
192
+
193
+ ### Test Patterns
194
+
195
+ ```python
196
+ # Standard test pattern
197
+ def test_something():
198
+ decoder = NippyDecoder()
199
+
200
+ # Manually construct Nippy bytes
201
+ data = b'NPY\x00' + [type byte] + [payload]
202
+
203
+ # Decode and assert
204
+ result = decoder.decode(data)
205
+ assert result == expected_value
206
+ ```
207
+
208
+ ### Key Test Scenarios Covered
209
+
210
+ - All primitive types (null, bool, int, float)
211
+ - String encoding (empty, short, medium)
212
+ - Keywords (converted to strings)
213
+ - Collections (vectors, maps, sets, lists)
214
+ - Empty collections
215
+ - Nested structures
216
+ - Mixed-type collections
217
+ - UUID encoding/decoding
218
+ - Error cases (invalid header, unsupported version)
219
+ - Edge cases (unhashable map keys)
220
+
221
+ ### Running Tests
222
+
223
+ Always run tests after changes to decoder logic:
224
+ ```bash
225
+ pytest tests/ -v
226
+ ```
227
+
228
+ ## Development Workflow
229
+
230
+ ### Making Changes
231
+
232
+ 1. **Read first**: Always read `decoder.py` before making changes
233
+ 2. **Understand context**: This decoder is validated against official Clojure Nippy - don't break compatibility
234
+ 3. **Test immediately**: Run tests after every change
235
+ 4. **Keep it simple**: The whole decoder is ~200 lines - maintain this simplicity
236
+
237
+ ### Adding New Type Support
238
+
239
+ If adding support for a new Nippy type code:
240
+
241
+ 1. Find the type code in Nippy spec/source
242
+ 2. Add handler in `_read()` method (follow existing pattern)
243
+ 3. Create helper method if needed (e.g., `_read_new_type()`)
244
+ 4. Add test case in appropriate test file
245
+ 5. Update README type mapping table
246
+ 6. Update docstring in `decoder.py`
247
+
248
+ ### Common Tasks
249
+
250
+ **Add a new type handler:**
251
+ ```python
252
+ # In decoder.py _read() method
253
+ if type_code == XX: return self._read_new_type(stream)
254
+ ```
255
+
256
+ **Add a helper method:**
257
+ ```python
258
+ def _read_new_type(self, stream: BytesIO) -> SomeType:
259
+ """Read new type with description."""
260
+ # Implementation
261
+ return result
262
+ ```
263
+
264
+ **Update version:**
265
+ 1. Edit `pyproject.toml` version
266
+ 2. Edit `src/nippy_decoder/__init__.py` __version__
267
+ 3. Add entry to `CHANGELOG.md`
268
+
269
+ ## Important Gotchas
270
+
271
+ ### 1. Nippy Version Lock
272
+
273
+ **Only Nippy v0 is supported** (the standard format since 2014). The decoder explicitly checks version byte and rejects anything other than 0.
274
+
275
+ ### 2. Keyword Transformation
276
+
277
+ Clojure keywords like `:status` become Python strings `"status"`. The colon is not preserved. This is by design but can surprise users.
278
+
279
+ ### 3. JSON Auto-parsing in Byte Arrays
280
+
281
+ Byte arrays that start with `{` or `[` are automatically parsed as JSON and returned as dict/list. This can fail silently (returns raw bytes on JSON parse error). This is intentional but worth knowing.
282
+
283
+ ### 4. Map Key Conversion
284
+
285
+ Unhashable Python objects (list, dict, set) used as map keys are automatically converted to strings via `str()`. This prevents crashes but loses type information.
286
+
287
+ ### 5. Zero Dependencies Constraint
288
+
289
+ **Never add external dependencies.** This is a core feature of the library. Use only Python stdlib.
290
+
291
+ ### 6. PYTHONPATH for Examples
292
+
293
+ Examples must be run with `PYTHONPATH=src` unless package is installed in dev mode (`pip install -e .`).
294
+
295
+ ### 7. Struct Format Strings
296
+
297
+ All struct unpacking uses big-endian (`>`):
298
+ - `'>h'`: signed 16-bit
299
+ - `'>i'`: signed 32-bit
300
+ - `'>q'`: signed 64-bit
301
+ - `'>H'`: unsigned 16-bit
302
+ - `'>I'`: unsigned 32-bit
303
+ - `'>f'`: 32-bit float
304
+ - `'>d'`: 64-bit double
305
+
306
+ ### 8. BytesIO Position
307
+
308
+ The decoder uses `BytesIO.read()` which advances position automatically. Be careful when adding new read methods.
309
+
310
+ ### 9. No Encoding Support
311
+
312
+ This is a **decoder-only** library. Encoding is not supported and should not be added. Users needing encoding should use official Clojure Nippy.
313
+
314
+ ## Project-Specific Context
315
+
316
+ ### Purpose & Use Cases
317
+
318
+ This library solves a specific problem: **Python services need to read data serialized by Clojure services using Nippy format.**
319
+
320
+ Common scenarios:
321
+ - Reading Nippy-encoded columns from Postgres/SQLite databases
322
+ - Consuming Nippy-encoded messages from Kafka
323
+ - Reading Nippy values from Redis
324
+ - Processing API responses with Nippy payloads
325
+
326
+ ### Design Philosophy
327
+
328
+ 1. **Simplicity**: ~200 lines total, easy to understand and debug
329
+ 2. **Zero dependencies**: No external deps, easy to install anywhere
330
+ 3. **Decode-only**: Focused on one problem, does it well
331
+ 4. **Stdlib only**: Uses only Python built-in modules
332
+ 5. **Type fidelity**: Maps Clojure types to natural Python equivalents
333
+
334
+ ### Not Supported (by design)
335
+
336
+ - **Encoding**: Use official Clojure Nippy for encoding
337
+ - **Nippy v1+**: Only v0 (the standard) is supported
338
+ - **Custom extensions**: Custom Nippy types not implemented
339
+ - **Types > 127**: Reserved/future types not implemented
340
+ - **Streaming**: Decodes complete byte arrays, not streaming
341
+
342
+ ### Validation
343
+
344
+ The decoder has been validated against official `taoensso/nippy` Clojure implementation. When making changes, ensure compatibility is maintained.
345
+
346
+ ## References
347
+
348
+ - **Official Nippy**: https://github.com/taoensso/nippy
349
+ - **Type codes**: See `decoder.py` comments or README.md type mapping table
350
+ - **Build backend**: Uses `hatchling` (specified in `pyproject.toml`)
351
+
352
+ ## Tips for Agents
353
+
354
+ 1. **Always test after changes**: `pytest tests/ -v`
355
+ 2. **Keep decoder simple**: Resist urge to refactor into complex abstractions
356
+ 3. **Preserve zero dependencies**: Don't add external packages
357
+ 4. **Match existing patterns**: Follow the type handler pattern in `_read()`
358
+ 5. **Update docs together**: If changing decoder, update README type table
359
+ 6. **Test with real Nippy data**: If possible, validate against Clojure-generated bytes
360
+ 7. **Use exact whitespace**: Code has 4-space indentation consistently
361
+ 8. **Preserve type conversion logic**: Keyword→string, unhashable keys→string, etc.
362
+ 9. **Don't break backward compatibility**: This is a library, breaking changes affect users
363
+ 10. **Read SETUP.md and README.md**: They contain additional context and user docs
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.0] - 2025-02-26
6
+
7
+ ### Added
8
+ - Initial release
9
+ - Pure Python Nippy decoder implementation
10
+ - Support for all standard Nippy types:
11
+ - Primitives (null, bool, int, float)
12
+ - Strings and keywords
13
+ - Collections (vector, map, set, list)
14
+ - UUIDs
15
+ - Byte arrays with auto-JSON parsing
16
+ - Zero external dependencies
17
+ - Python 3.8+ support
18
+ - Comprehensive test suite
19
+ - Usage examples
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 nippy-decoder contributors
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.