gocache 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,8 @@
1
+ cmd/server/cache.rdb
2
+ cmd/server/cache.aof
3
+ internal/persistence/bench_test_*
4
+ cmd/data/*
5
+ server.exe
6
+ cache.aof
7
+ cache.rdb
8
+ pkg/client/python/__pycache__
gocache-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,328 @@
1
+ Metadata-Version: 2.4
2
+ Name: gocache
3
+ Version: 0.1.0
4
+ Summary: Python client library for GoCache — a Redis-compatible in-memory cache server
5
+ Project-URL: Homepage, https://github.com/erickim73/gocache
6
+ Project-URL: Repository, https://github.com/erickim73/gocache
7
+ Project-URL: Issues, https://github.com/erickim73/gocache/issues
8
+ Author-email: Eric Kim <seyoon2006@gmail.com>
9
+ License: MIT
10
+ Keywords: cache,client,gocache,redis,resp
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Database
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+
23
+ # GoCache Python Client
24
+
25
+ A pure-Python client library for [GoCache](../../../README.md) — a Redis-compatible in-memory cache server. No external dependencies. Python 3.10+ only.
26
+
27
+ ---
28
+
29
+ ## Installation
30
+
31
+ No package manager needed. Copy `client.py` into your project and import from it directly.
32
+
33
+ ```bash
34
+ # Clone the repo
35
+ git clone https://github.com/erickim73/gocache.git
36
+ cd gocache
37
+
38
+ # Verify Python version (3.10+ required)
39
+ python --version
40
+ ```
41
+
42
+ That's it. `client.py` uses only the Python standard library (`socket`, `time`).
43
+
44
+ ---
45
+
46
+ ## Running the Server
47
+
48
+ Before using the client, start the GoCache server:
49
+
50
+ ```bash
51
+ # From the repo root
52
+ go run cmd/server/main.go
53
+
54
+ # Or if you've built the binary
55
+ ./bin/gocache
56
+ ```
57
+
58
+ The server listens on `localhost:6379` by default.
59
+
60
+ ---
61
+
62
+ ## Quick Start
63
+
64
+ ```python
65
+ from client import GoCacheClient
66
+
67
+ with GoCacheClient("localhost", 6379) as cache:
68
+ # Health check
69
+ cache.ping() # → 'PONG'
70
+
71
+ # Store and retrieve a value
72
+ cache.set("user:1000", "Eric") # → 'OK'
73
+ cache.get("user:1000") # → 'Eric'
74
+
75
+ # Missing keys return None, not an error
76
+ cache.get("user:9999") # → None
77
+
78
+ # Set a key that expires after 60 seconds
79
+ cache.set("session:token", "abc123", ex=60)
80
+
81
+ # Delete one or more keys
82
+ cache.delete("user:1000") # → 1 (number of keys removed)
83
+ cache.delete("k1", "k2", "k3") # → 3
84
+ ```
85
+
86
+ The `with` statement guarantees the TCP connection is closed when the block exits, even if an exception occurs. For long-lived processes, you can also manage the lifecycle manually:
87
+
88
+ ```python
89
+ cache = GoCacheClient("localhost", 6379)
90
+ cache.set("key", "value")
91
+ cache.close()
92
+ ```
93
+
94
+ ---
95
+
96
+ ## API Reference
97
+
98
+ ### `GoCacheClient(host, port)`
99
+
100
+ Opens a TCP connection to the GoCache server. Raises `GoCacheConnectionError` if the server is not reachable.
101
+
102
+ | Parameter | Type | Default | Description |
103
+ |-----------|------|---------|-------------|
104
+ | `host` | `str` | `"localhost"` | Server hostname or IP address |
105
+ | `port` | `int` | `6379` | Server port |
106
+
107
+ ```python
108
+ client = GoCacheClient("localhost", 6379)
109
+ client = GoCacheClient("10.0.0.5", 6380) # custom host and port
110
+ ```
111
+
112
+ ---
113
+
114
+ ### `ping() → str`
115
+
116
+ Sends a PING to the server. Returns `'PONG'` on a healthy connection. Use this to verify the server is reachable before issuing commands.
117
+
118
+ ```python
119
+ response = client.ping() # → 'PONG'
120
+ ```
121
+
122
+ ---
123
+
124
+ ### `get(key) → str | None`
125
+
126
+ Retrieves the value stored at `key`. Returns `None` if the key does not exist — it does not raise an exception.
127
+
128
+ | Parameter | Type | Description |
129
+ |-----------|------|-------------|
130
+ | `key` | `str` | The key to look up |
131
+
132
+ **Returns:** `str` if the key exists, `None` if it does not.
133
+
134
+ ```python
135
+ client.set("color", "blue")
136
+
137
+ client.get("color") # → 'blue'
138
+ client.get("missing") # → None
139
+ ```
140
+
141
+ ---
142
+
143
+ ### `set(key, value, ex=None) → str`
144
+
145
+ Stores `value` at `key`. Overwrites any existing value. Returns `'OK'` on success.
146
+
147
+ | Parameter | Type | Default | Description |
148
+ |-----------|------|---------|-------------|
149
+ | `key` | `str` | — | The key to write |
150
+ | `value` | `str` | — | The value to store |
151
+ | `ex` | `int \| None` | `None` | Optional TTL in seconds. The key is deleted automatically after this many seconds. |
152
+
153
+ **Returns:** `'OK'`
154
+
155
+ ```python
156
+ client.set("name", "Eric") # → 'OK' (persists until deleted)
157
+ client.set("session", "xyz", ex=3600) # → 'OK' (expires after 1 hour)
158
+ ```
159
+
160
+ ---
161
+
162
+ ### `delete(*keys) → int`
163
+
164
+ Deletes one or more keys. Keys that do not exist are silently ignored and do not affect the count.
165
+
166
+ | Parameter | Type | Description |
167
+ |-----------|------|-------------|
168
+ | `*keys` | `str` | One or more keys to delete |
169
+
170
+ **Returns:** `int` — the number of keys that were actually deleted (keys that did not exist count as 0).
171
+
172
+ ```python
173
+ client.set("a", "1")
174
+ client.set("b", "2")
175
+
176
+ client.delete("a") # → 1
177
+ client.delete("b", "c", "d") # → 1 ("c" and "d" didn't exist)
178
+ client.delete("already_gone") # → 0
179
+ ```
180
+
181
+ ---
182
+
183
+ ### `close() → None`
184
+
185
+ Closes the TCP connection and releases the socket. After calling this, any further method calls on the client will raise an `OSError`.
186
+
187
+ If you use the client as a context manager (`with GoCacheClient(...) as c:`), `close()` is called automatically when the block exits. You only need to call it manually if you're managing the lifecycle yourself.
188
+
189
+ ```python
190
+ client = GoCacheClient("localhost", 6379)
191
+ client.set("key", "value")
192
+ client.close()
193
+ ```
194
+
195
+ ---
196
+
197
+ ### Context Manager Support
198
+
199
+ `GoCacheClient` implements `__enter__` and `__exit__`, so it can be used as a context manager. This is the recommended usage pattern — it guarantees the socket is closed regardless of whether the block exits normally or via an exception.
200
+
201
+ ```python
202
+ with GoCacheClient("localhost", 6379) as c:
203
+ c.set("key", "value")
204
+ value = c.get("key")
205
+ # Socket is closed here automatically
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Error Handling
211
+
212
+ The client defines three exception types, all in `client.py`:
213
+
214
+ ### `GoCacheError`
215
+
216
+ Base class for all GoCache-specific errors. Catch this if you want to handle any GoCache error in one place.
217
+
218
+ ```python
219
+ from client import GoCacheError
220
+
221
+ try:
222
+ client.set("key", "value")
223
+ except GoCacheError as e:
224
+ print(f"Something went wrong: {e}")
225
+ ```
226
+
227
+ ---
228
+
229
+ ### `GoCacheConnectionError(GoCacheError)`
230
+
231
+ Raised when the client cannot connect to the server, or when the connection is lost mid-command.
232
+
233
+ ```python
234
+ from client import GoCacheConnectionError
235
+
236
+ try:
237
+ client = GoCacheClient("localhost", 19999) # nothing listening here
238
+ except GoCacheConnectionError as e:
239
+ print(e)
240
+ # → Could not connect to GoCache at localhost:19999. Is the server running?
241
+ ```
242
+
243
+ ---
244
+
245
+ ### `GoCacheCommandError(GoCacheError)`
246
+
247
+ Raised when the server returns a RESP error response (a `-` type message). This indicates the server understood the command but rejected it — for example, an unknown command name or wrong number of arguments.
248
+
249
+ ```python
250
+ from client import GoCacheCommandError
251
+
252
+ try:
253
+ client._send("NOT_A_COMMAND")
254
+ client._read_response()
255
+ except GoCacheCommandError as e:
256
+ print(e) # → ERR unknown command 'NOT_A_COMMAND'
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Running the Examples
262
+
263
+ ```bash
264
+ cd pkg/client/python
265
+
266
+ # Make sure GoCache is running first
267
+ go run ../../../cmd/server/main.go &
268
+
269
+ python examples.py
270
+ ```
271
+
272
+ Expected output:
273
+
274
+ ```
275
+ ──────────────────────────────────────────────────
276
+ PING — health check
277
+ ──────────────────────────────────────────────────
278
+ ping() → 'PONG'
279
+ ✓ server is reachable
280
+
281
+ ──────────────────────────────────────────────────
282
+ SET / GET — basic read-write
283
+ ──────────────────────────────────────────────────
284
+ set('user:1000:name', 'Eric') → 'OK'
285
+ get('user:1000:name') → 'Eric'
286
+ ...
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Running the Tests
292
+
293
+ ```bash
294
+ cd pkg/client/python
295
+
296
+ # Unit tests only — no server required
297
+ python -m unittest test_client.TestRespEncoder test_client.TestRespParser -v
298
+
299
+ # All tests — integration tests run if GoCache is reachable, skip otherwise
300
+ python -m unittest test_client -v
301
+ ```
302
+
303
+ The test suite has two layers:
304
+
305
+ - **Unit tests** (`TestRespEncoder`, `TestRespParser`) — 24 tests covering the RESP encoder and parser in isolation. No server needed.
306
+ - **Integration tests** (`TestGoCacheClient`) — 21 tests exercising every method against a live server. Skipped automatically with a clear message if the server is not running.
307
+
308
+ ---
309
+
310
+ ## File Structure
311
+
312
+ ```
313
+ pkg/client/python/
314
+ ├── client.py # Client library — all code lives here
315
+ ├── examples.py # End-to-end demo script
316
+ ├── test_client.py # Unit and integration tests
317
+ └── README.md # This file
318
+ ```
319
+
320
+ ---
321
+
322
+ ## Compatibility
323
+
324
+ | | Requirement |
325
+ |-|-------------|
326
+ | Python | 3.10+ |
327
+ | GoCache server | any version supporting RESP protocol |
328
+ | Dependencies | none (standard library only) |
@@ -0,0 +1,306 @@
1
+ # GoCache Python Client
2
+
3
+ A pure-Python client library for [GoCache](../../../README.md) — a Redis-compatible in-memory cache server. No external dependencies. Python 3.10+ only.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ No package manager needed. Copy `client.py` into your project and import from it directly.
10
+
11
+ ```bash
12
+ # Clone the repo
13
+ git clone https://github.com/erickim73/gocache.git
14
+ cd gocache
15
+
16
+ # Verify Python version (3.10+ required)
17
+ python --version
18
+ ```
19
+
20
+ That's it. `client.py` uses only the Python standard library (`socket`, `time`).
21
+
22
+ ---
23
+
24
+ ## Running the Server
25
+
26
+ Before using the client, start the GoCache server:
27
+
28
+ ```bash
29
+ # From the repo root
30
+ go run cmd/server/main.go
31
+
32
+ # Or if you've built the binary
33
+ ./bin/gocache
34
+ ```
35
+
36
+ The server listens on `localhost:6379` by default.
37
+
38
+ ---
39
+
40
+ ## Quick Start
41
+
42
+ ```python
43
+ from client import GoCacheClient
44
+
45
+ with GoCacheClient("localhost", 6379) as cache:
46
+ # Health check
47
+ cache.ping() # → 'PONG'
48
+
49
+ # Store and retrieve a value
50
+ cache.set("user:1000", "Eric") # → 'OK'
51
+ cache.get("user:1000") # → 'Eric'
52
+
53
+ # Missing keys return None, not an error
54
+ cache.get("user:9999") # → None
55
+
56
+ # Set a key that expires after 60 seconds
57
+ cache.set("session:token", "abc123", ex=60)
58
+
59
+ # Delete one or more keys
60
+ cache.delete("user:1000") # → 1 (number of keys removed)
61
+ cache.delete("k1", "k2", "k3") # → 3
62
+ ```
63
+
64
+ The `with` statement guarantees the TCP connection is closed when the block exits, even if an exception occurs. For long-lived processes, you can also manage the lifecycle manually:
65
+
66
+ ```python
67
+ cache = GoCacheClient("localhost", 6379)
68
+ cache.set("key", "value")
69
+ cache.close()
70
+ ```
71
+
72
+ ---
73
+
74
+ ## API Reference
75
+
76
+ ### `GoCacheClient(host, port)`
77
+
78
+ Opens a TCP connection to the GoCache server. Raises `GoCacheConnectionError` if the server is not reachable.
79
+
80
+ | Parameter | Type | Default | Description |
81
+ |-----------|------|---------|-------------|
82
+ | `host` | `str` | `"localhost"` | Server hostname or IP address |
83
+ | `port` | `int` | `6379` | Server port |
84
+
85
+ ```python
86
+ client = GoCacheClient("localhost", 6379)
87
+ client = GoCacheClient("10.0.0.5", 6380) # custom host and port
88
+ ```
89
+
90
+ ---
91
+
92
+ ### `ping() → str`
93
+
94
+ Sends a PING to the server. Returns `'PONG'` on a healthy connection. Use this to verify the server is reachable before issuing commands.
95
+
96
+ ```python
97
+ response = client.ping() # → 'PONG'
98
+ ```
99
+
100
+ ---
101
+
102
+ ### `get(key) → str | None`
103
+
104
+ Retrieves the value stored at `key`. Returns `None` if the key does not exist — it does not raise an exception.
105
+
106
+ | Parameter | Type | Description |
107
+ |-----------|------|-------------|
108
+ | `key` | `str` | The key to look up |
109
+
110
+ **Returns:** `str` if the key exists, `None` if it does not.
111
+
112
+ ```python
113
+ client.set("color", "blue")
114
+
115
+ client.get("color") # → 'blue'
116
+ client.get("missing") # → None
117
+ ```
118
+
119
+ ---
120
+
121
+ ### `set(key, value, ex=None) → str`
122
+
123
+ Stores `value` at `key`. Overwrites any existing value. Returns `'OK'` on success.
124
+
125
+ | Parameter | Type | Default | Description |
126
+ |-----------|------|---------|-------------|
127
+ | `key` | `str` | — | The key to write |
128
+ | `value` | `str` | — | The value to store |
129
+ | `ex` | `int \| None` | `None` | Optional TTL in seconds. The key is deleted automatically after this many seconds. |
130
+
131
+ **Returns:** `'OK'`
132
+
133
+ ```python
134
+ client.set("name", "Eric") # → 'OK' (persists until deleted)
135
+ client.set("session", "xyz", ex=3600) # → 'OK' (expires after 1 hour)
136
+ ```
137
+
138
+ ---
139
+
140
+ ### `delete(*keys) → int`
141
+
142
+ Deletes one or more keys. Keys that do not exist are silently ignored and do not affect the count.
143
+
144
+ | Parameter | Type | Description |
145
+ |-----------|------|-------------|
146
+ | `*keys` | `str` | One or more keys to delete |
147
+
148
+ **Returns:** `int` — the number of keys that were actually deleted (keys that did not exist count as 0).
149
+
150
+ ```python
151
+ client.set("a", "1")
152
+ client.set("b", "2")
153
+
154
+ client.delete("a") # → 1
155
+ client.delete("b", "c", "d") # → 1 ("c" and "d" didn't exist)
156
+ client.delete("already_gone") # → 0
157
+ ```
158
+
159
+ ---
160
+
161
+ ### `close() → None`
162
+
163
+ Closes the TCP connection and releases the socket. After calling this, any further method calls on the client will raise an `OSError`.
164
+
165
+ If you use the client as a context manager (`with GoCacheClient(...) as c:`), `close()` is called automatically when the block exits. You only need to call it manually if you're managing the lifecycle yourself.
166
+
167
+ ```python
168
+ client = GoCacheClient("localhost", 6379)
169
+ client.set("key", "value")
170
+ client.close()
171
+ ```
172
+
173
+ ---
174
+
175
+ ### Context Manager Support
176
+
177
+ `GoCacheClient` implements `__enter__` and `__exit__`, so it can be used as a context manager. This is the recommended usage pattern — it guarantees the socket is closed regardless of whether the block exits normally or via an exception.
178
+
179
+ ```python
180
+ with GoCacheClient("localhost", 6379) as c:
181
+ c.set("key", "value")
182
+ value = c.get("key")
183
+ # Socket is closed here automatically
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Error Handling
189
+
190
+ The client defines three exception types, all in `client.py`:
191
+
192
+ ### `GoCacheError`
193
+
194
+ Base class for all GoCache-specific errors. Catch this if you want to handle any GoCache error in one place.
195
+
196
+ ```python
197
+ from client import GoCacheError
198
+
199
+ try:
200
+ client.set("key", "value")
201
+ except GoCacheError as e:
202
+ print(f"Something went wrong: {e}")
203
+ ```
204
+
205
+ ---
206
+
207
+ ### `GoCacheConnectionError(GoCacheError)`
208
+
209
+ Raised when the client cannot connect to the server, or when the connection is lost mid-command.
210
+
211
+ ```python
212
+ from client import GoCacheConnectionError
213
+
214
+ try:
215
+ client = GoCacheClient("localhost", 19999) # nothing listening here
216
+ except GoCacheConnectionError as e:
217
+ print(e)
218
+ # → Could not connect to GoCache at localhost:19999. Is the server running?
219
+ ```
220
+
221
+ ---
222
+
223
+ ### `GoCacheCommandError(GoCacheError)`
224
+
225
+ Raised when the server returns a RESP error response (a `-` type message). This indicates the server understood the command but rejected it — for example, an unknown command name or wrong number of arguments.
226
+
227
+ ```python
228
+ from client import GoCacheCommandError
229
+
230
+ try:
231
+ client._send("NOT_A_COMMAND")
232
+ client._read_response()
233
+ except GoCacheCommandError as e:
234
+ print(e) # → ERR unknown command 'NOT_A_COMMAND'
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Running the Examples
240
+
241
+ ```bash
242
+ cd pkg/client/python
243
+
244
+ # Make sure GoCache is running first
245
+ go run ../../../cmd/server/main.go &
246
+
247
+ python examples.py
248
+ ```
249
+
250
+ Expected output:
251
+
252
+ ```
253
+ ──────────────────────────────────────────────────
254
+ PING — health check
255
+ ──────────────────────────────────────────────────
256
+ ping() → 'PONG'
257
+ ✓ server is reachable
258
+
259
+ ──────────────────────────────────────────────────
260
+ SET / GET — basic read-write
261
+ ──────────────────────────────────────────────────
262
+ set('user:1000:name', 'Eric') → 'OK'
263
+ get('user:1000:name') → 'Eric'
264
+ ...
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Running the Tests
270
+
271
+ ```bash
272
+ cd pkg/client/python
273
+
274
+ # Unit tests only — no server required
275
+ python -m unittest test_client.TestRespEncoder test_client.TestRespParser -v
276
+
277
+ # All tests — integration tests run if GoCache is reachable, skip otherwise
278
+ python -m unittest test_client -v
279
+ ```
280
+
281
+ The test suite has two layers:
282
+
283
+ - **Unit tests** (`TestRespEncoder`, `TestRespParser`) — 24 tests covering the RESP encoder and parser in isolation. No server needed.
284
+ - **Integration tests** (`TestGoCacheClient`) — 21 tests exercising every method against a live server. Skipped automatically with a clear message if the server is not running.
285
+
286
+ ---
287
+
288
+ ## File Structure
289
+
290
+ ```
291
+ pkg/client/python/
292
+ ├── client.py # Client library — all code lives here
293
+ ├── examples.py # End-to-end demo script
294
+ ├── test_client.py # Unit and integration tests
295
+ └── README.md # This file
296
+ ```
297
+
298
+ ---
299
+
300
+ ## Compatibility
301
+
302
+ | | Requirement |
303
+ |-|-------------|
304
+ | Python | 3.10+ |
305
+ | GoCache server | any version supporting RESP protocol |
306
+ | Dependencies | none (standard library only) |