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.
- nippy_decoder-0.1.0/.crush/.gitignore +1 -0
- nippy_decoder-0.1.0/.crush/crush.db +0 -0
- nippy_decoder-0.1.0/.crush/crush.db-shm +0 -0
- nippy_decoder-0.1.0/.crush/crush.db-wal +0 -0
- nippy_decoder-0.1.0/.crush/init +0 -0
- nippy_decoder-0.1.0/.crush/logs/crush.log +16 -0
- nippy_decoder-0.1.0/.gitignore +43 -0
- nippy_decoder-0.1.0/AGENTS.md +363 -0
- nippy_decoder-0.1.0/CHANGELOG.md +19 -0
- nippy_decoder-0.1.0/LICENSE +21 -0
- nippy_decoder-0.1.0/PKG-INFO +298 -0
- nippy_decoder-0.1.0/README.md +274 -0
- nippy_decoder-0.1.0/SETUP.md +74 -0
- nippy_decoder-0.1.0/examples/basic_usage.py +180 -0
- nippy_decoder-0.1.0/pyproject.toml +35 -0
- nippy_decoder-0.1.0/src/nippy_decoder/__init__.py +4 -0
- nippy_decoder-0.1.0/src/nippy_decoder/decoder.py +203 -0
- nippy_decoder-0.1.0/tests/test_collections.py +96 -0
- nippy_decoder-0.1.0/tests/test_primitives.py +99 -0
- nippy_decoder-0.1.0/tests/test_uuid.py +54 -0
|
@@ -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.
|