flagrant-client 0.1.0__tar.gz → 0.2.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.
Files changed (25) hide show
  1. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/.agent-shell/transcripts/2026-09-11-21-40-01.md +98 -0
  2. flagrant_client-0.2.0/.agent-shell/transcripts/2026-10-01-22-10-28.md +264 -0
  3. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/PKG-INFO +2 -2
  4. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/README.md +1 -1
  5. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/pyproject.toml +1 -1
  6. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/__init__.py +2 -2
  7. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/cache.py +2 -0
  8. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/client.py +2 -2
  9. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/uv.lock +263 -263
  10. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/.agent-shell/transcripts/2026-08-11-10-19-43.md +0 -0
  11. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/.agent-shell/transcripts/2026-08-18-10-59-01.md +0 -0
  12. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/.agent-shell/transcripts/2026-08-19-00-02-53.md +0 -0
  13. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/.agent-shell/transcripts/2026-08-26-23-27-24.md +0 -0
  14. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/.gitignore +0 -0
  15. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/.python-version +0 -0
  16. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/LICENSE +0 -0
  17. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/proto/features.proto +0 -0
  18. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/grpc_transport.py +0 -0
  19. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/http_transport.py +0 -0
  20. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/models.py +0 -0
  21. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/proto/__init__.py +0 -0
  22. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/proto/features_pb2.py +0 -0
  23. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/proto/features_pb2.pyi +0 -0
  24. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/proto/features_pb2_grpc.py +0 -0
  25. {flagrant_client-0.1.0 → flagrant_client-0.2.0}/src/flagrant/transport.py +0 -0
@@ -1287,3 +1287,101 @@ SOFTWARE.
1287
1287
 
1288
1288
  I should check whether the hatchling version being used supports the PEP 639 SPDX license string format, since older versions might not handle `license = "MIT"` correctly. Let me just confirm the build works with this setup.
1289
1289
 
1290
+
1291
+
1292
+ ### Tool Call [completed]: uv build 2>&1 | tail -30
1293
+
1294
+ **Tool:** execute
1295
+ **Timestamp:** 2026-09-11 22:40:17
1296
+ **Command:** uv build 2>&1 | tail -30
1297
+
1298
+ ````
1299
+ ```console
1300
+ Building source distribution...
1301
+ Building wheel from source distribution...
1302
+ Successfully built dist/flagrant_client-0.1.0.tar.gz
1303
+ Successfully built dist/flagrant_client-0.1.0-py3-none-any.whl
1304
+ ```
1305
+ ````
1306
+
1307
+ ## Agent (2026-09-11 22:40:19)
1308
+
1309
+ Build succeeds. Updated `pyproject.toml` with description, `license = "MIT"`, GitHub homepage/repository URLs, and the two classifiers, and added a `LICENSE` (MIT, Michal Buczko) file. Verified with `uv build` — both sdist and wheel build cleanly.
1310
+
1311
+ You're set to publish with `uv publish` (needs a PyPI token via `UV_PUBLISH_TOKEN` or `--token`).
1312
+
1313
+ ## User (2026-09-11 22:56:12)
1314
+
1315
+ how to remove a history in fish shell? I'd like to remove the recent entry.
1316
+
1317
+ ## Agent's Thoughts (2026-09-11 22:56:18)
1318
+
1319
+ This is about fish shell history rather than the repo itself. Fish stores history in ~/.local/share/fish/fish_history, and I recall the `history delete` command can remove entries by exact match, so I need to find the right syntax to delete just the most recent one.
1320
+
1321
+ My plan: capture the most recent command with `history --max 1`, store it in a variable, then pass that to `history delete --exact --` to remove it precisely.
1322
+
1323
+
1324
+
1325
+ ### Tool Call [completed]: fish -c 'history --help' 2>&1 | head -40
1326
+
1327
+ **Tool:** execute
1328
+ **Timestamp:** 2026-09-11 22:56:27
1329
+ **Command:** fish -c 'history --help' 2>&1 | head -40
1330
+
1331
+ ````
1332
+ ```console
1333
+ HISTORY(1) fish-shell HISTORY(1)
1334
+
1335
+ NNAAMMEE
1336
+ history - show and manipulate command history
1337
+
1338
+ SSYYNNOOPPSSIISS
1339
+ hhiissttoorryy [sseeaarrcchh] [----sshhooww--ttiimmee] [----ccaassee--sseennssiittiivvee]
1340
+ [----eexxaacctt | ----pprreeffiixx | ----ccoonnttaaiinnss] [----mmaaxx _N] [----nnuullll] [----rreevveerrssee]
1341
+ [_S_E_A_R_C_H___S_T_R_I_N_G ...]
1342
+ hhiissttoorryy ddeelleettee [----ccaassee--sseennssiittiivvee]
1343
+ [----eexxaacctt | ----pprreeffiixx | ----ccoonnttaaiinnss] _S_E_A_R_C_H___S_T_R_I_N_G ...
1344
+ hhiissttoorryy mmeerrggee
1345
+ hhiissttoorryy ssaavvee
1346
+ hhiissttoorryy cclleeaarr
1347
+ hhiissttoorryy cclleeaarr--sseessssiioonn
1348
+ hhiissttoorryy aappppeenndd _C_O_M_M_A_N_D ...
1349
+
1350
+
1351
+ DDEESSCCRRIIPPTTIIOONN
1352
+ hhiissttoorryy is used to search, delete, and otherwise manipulate the history
1353
+ of interactive commands <#history-search>.
1354
+
1355
+ The following operations (sub-commands) are available:
1356
+
1357
+ sseeaarrcchh Returns history items matching the search string. If no search
1358
+ string is provided it returns all history items. This is the
1359
+ default operation if no other operation is specified. You only
1360
+ have to explicitly say hhiissttoorryy sseeaarrcchh if you wish to search for
1361
+ one of the subcommands. The ----ccoonnttaaiinnss search option will be
1362
+ used if you don't specify a different search option. Entries are
1363
+ ordered newest to oldest unless you use the ----rreevveerrssee flag. If
1364
+ stdout is attached to a tty the output will be piped through
1365
+ your pager by the history function. The history builtin writes
1366
+ the results to stdout.
1367
+
1368
+ ddeelleettee Deletes history items. The ----ccoonnttaaiinnss search option will be used
1369
+ if you don't specify a different search option. If you don't
1370
+ specify ----eexxaacctt a prompt will be displayed before any items are
1371
+ deleted asking you which entries are to be deleted. You can
1372
+ enter the word "all" to delete all matching entries. You can
1373
+ ```
1374
+ ````
1375
+
1376
+ ## Agent (2026-09-11 22:56:29)
1377
+
1378
+ To delete just the most recent history entry in fish:
1379
+
1380
+ ```fish
1381
+ history delete --exact --case-sensitive -- (history --max 1)
1382
+ ```
1383
+
1384
+ `history --max 1` grabs the last entry's exact text, and `history delete --exact` removes only that exact match (without `--exact` it does a `--contains` match and prompts you interactively). Run `history save` afterward if you want the deletion flushed to disk immediately (fish normally does this on shell exit anyway).
1385
+
1386
+ If you'd rather do it interactively: just run `history delete <part of the command>` — fish will show matching entries and let you type `all` or pick which to remove.
1387
+
@@ -0,0 +1,264 @@
1
+ # Agent Shell Transcript
2
+
3
+ **Agent:** Claude
4
+ **Started:** 2026-10-01 22:10:40
5
+ **Working Directory:** /Users/michal/workspace/python/flagrant-client/
6
+ **Session ID:** a52ab1c2-8ea5-4e3d-97db-b9e80551c806
7
+
8
+ ---
9
+
10
+ ## User (2026-10-01 22:10:40)
11
+
12
+ rename it to FlagrantOpts
13
+
14
+ src/flagrant/client.py:12-12
15
+
16
+ class FlagsmithOpts:
17
+
18
+ ## Agent's Thoughts (2026-10-01 22:10:42)
19
+
20
+ I need to rename FlagsmithOpts to FlagrantOpts in src/flagrant/client.py, so let me check that file first.
21
+
22
+
23
+
24
+ ### Tool Call [completed]: Read src/flagrant/client.py
25
+
26
+ **Tool:** read
27
+ **Timestamp:** 2026-10-01 22:10:42
28
+ **Parameters:**
29
+ file_path: /Users/michal/workspace/python/flagrant-client/src/flagrant/client.py
30
+
31
+ ````
32
+ ```
33
+ 1 from collections.abc import Callable
34
+ 2 from dataclasses import dataclass
35
+ 3 from typing import Any
36
+ 4
37
+ 5 from flagrant.cache import LRUCache
38
+ 6 from flagrant.models import Feature
39
+ 7 from flagrant.transport import Transport, TransientError
40
+ 8
41
+ 9 DefaultFlagHandler = Callable[[str, str], list[Feature] | None]
42
+ 10
43
+ 11 @dataclass(frozen=True)
44
+ 12 class FlagsmithOpts:
45
+ 13 cache_ttl: float | None = None
46
+ 14 cache_size: int | None = None
47
+ 15
48
+ 16
49
+ 17 class FlagrantClient:
50
+ 18 def __init__(
51
+ 19 self,
52
+ 20 project: str,
53
+ 21 transport: Transport,
54
+ 22 *,
55
+ 23 default_handler: DefaultFlagHandler | None = None,
56
+ 24 opts: FlagsmithOpts = FlagsmithOpts()
57
+ 25 ) -> None:
58
+ 26 self.project = project
59
+ 27 self.transport = transport
60
+ 28 self.opts = opts
61
+ 29 self.default_handler = default_handler
62
+ 30
63
+ 31 self._cache: LRUCache[tuple[str, str], list[Feature]] | None = (
64
+ 32 LRUCache(max_size=opts.cache_size, ttl=opts.cache_ttl)
65
+ 33 if opts.cache_size
66
+ 34 else None
67
+ 35 )
68
+ 36
69
+ 37 def get_features(self, environment: str, identity: str) -> list[Feature] | None:
70
+ 38 key = (environment, identity)
71
+ 39 if self._cache is not None:
72
+ 40 if cached := self._cache.get(key):
73
+ 41 return cached
74
+ 42
75
+ 43 try:
76
+ 44 features = self.transport.fetch(self.project, environment, identity)
77
+ 45 except TransientError:
78
+ 46 if self._cache is not None and (stale := self._cache.get_stale(key)) is not None:
79
+ 47 return stale
80
+ 48 if self.default_handler is None:
81
+ 49 raise
82
+ 50 return self.default_handler(environment, identity)
83
+ 51
84
+ 52 if self._cache is not None:
85
+ 53 self._cache.set(key, features)
86
+ 54
87
+ 55 return features
88
+ 56
89
+ 57 def close(self) -> None:
90
+ 58 self.transport.close()
91
+ 59
92
+ 60 def __enter__(self) -> "FlagrantClient":
93
+ 61 return self
94
+ 62
95
+ 63 def __exit__(self, *_args: Any) -> None:
96
+ 64 self.close()
97
+ ```
98
+ ````
99
+
100
+ ## Agent (2026-10-01 22:10:43)
101
+
102
+ Let me check for other usages of `FlagsmithOpts` across the repo before renaming.
103
+
104
+ ### Tool Call [completed]: grep -rn "FlagsmithOpts" /Users/michal/workspace/python/flagrant-client --include="*.py"
105
+
106
+ **Tool:** execute
107
+ **Timestamp:** 2026-10-01 22:10:44
108
+ **Description:** Search for other usages of FlagsmithOpts
109
+ **Command:** grep -rn "FlagsmithOpts" /Users/michal/workspace/python/flagrant-client --include="*.py"
110
+
111
+ ````
112
+ ```console
113
+ /Users/michal/workspace/python/flagrant-client/src/flagrant/client.py:12:class FlagsmithOpts:
114
+ /Users/michal/workspace/python/flagrant-client/src/flagrant/client.py:24: opts: FlagsmithOpts = FlagsmithOpts()
115
+ /Users/michal/workspace/python/flagrant-client/src/flagrant/__init__.py:1:from flagrant.client import FlagrantClient, FlagsmithOpts
116
+ /Users/michal/workspace/python/flagrant-client/src/flagrant/__init__.py:8: "FlagsmithOpts",
117
+ ```
118
+ ````
119
+
120
+ ## Agent (2026-10-01 22:10:45)
121
+
122
+ Only two files reference it. Renaming both.
123
+
124
+ ### Tool Call [completed]: Edit src/flagrant/client.py
125
+
126
+ **Tool:** edit
127
+ **Timestamp:** 2026-10-01 22:10:51
128
+ **Parameters:**
129
+ file_path: /Users/michal/workspace/python/flagrant-client/src/flagrant/client.py
130
+ old_string: class FlagsmithOpts:
131
+ cache_ttl: float | None = None
132
+ cache_size: int | None = None
133
+
134
+
135
+ class FlagrantClient:
136
+ def __init__(
137
+ self,
138
+ project: str,
139
+ transport: Transport,
140
+ *,
141
+ default_handler: DefaultFlagHandler | None = None,
142
+ opts: FlagsmithOpts = FlagsmithOpts()
143
+ new_string: class FlagrantOpts:
144
+ cache_ttl: float | None = None
145
+ cache_size: int | None = None
146
+
147
+
148
+ class FlagrantClient:
149
+ def __init__(
150
+ self,
151
+ project: str,
152
+ transport: Transport,
153
+ *,
154
+ default_handler: DefaultFlagHandler | None = None,
155
+ opts: FlagrantOpts = FlagrantOpts()
156
+
157
+ ```
158
+ ╭────────────────────────╮
159
+ │ src/flagrant/client.py │
160
+ ╰────────────────────────╯
161
+
162
+ @@ -1,6 +1,6 @@
163
+ DefaultFlagHandler = Callable[[str, str], list[Feature] | None]
164
+
165
+ @dataclass(frozen=True)
166
+ -class FlagsmithOpts:
167
+ +class FlagrantOpts:
168
+ cache_ttl: float | None = None
169
+ cache_size: int | None = None
170
+
171
+
172
+ ╭────────────────────────╮
173
+ │ src/flagrant/client.py │
174
+ ╰────────────────────────╯
175
+
176
+ @@ -1,7 +1,7 @@
177
+ transport: Transport,
178
+ *,
179
+ default_handler: DefaultFlagHandler | None = None,
180
+ - opts: FlagsmithOpts = FlagsmithOpts()
181
+ + opts: FlagrantOpts = FlagrantOpts()
182
+ ) -> None:
183
+ self.project = project
184
+ self.transport = transport
185
+
186
+ ```
187
+
188
+
189
+ ### Tool Call [completed]: Read src/flagrant/__init__.py
190
+
191
+ **Tool:** read
192
+ **Timestamp:** 2026-10-01 22:10:52
193
+ **Parameters:**
194
+ file_path: /Users/michal/workspace/python/flagrant-client/src/flagrant/__init__.py
195
+
196
+ ````
197
+ ```
198
+ 1 from flagrant.client import FlagrantClient, FlagsmithOpts
199
+ 2 from flagrant.grpc_transport import GrpcTransport
200
+ 3 from flagrant.http_transport import HttpTransport
201
+ 4 from flagrant.transport import PermanentError, Transport, TransientError
202
+ 5
203
+ 6 __all__ = [
204
+ 7 "FlagrantClient",
205
+ 8 "FlagsmithOpts",
206
+ 9 "GrpcTransport",
207
+ 10 "HttpTransport",
208
+ 11 "PermanentError",
209
+ 12 "Transport",
210
+ 13 "TransientError",
211
+ 14 ]
212
+ ```
213
+ ````
214
+
215
+
216
+ ### Tool Call [completed]: Edit src/flagrant/__init__.py
217
+
218
+ **Tool:** edit
219
+ **Timestamp:** 2026-10-01 22:10:59
220
+ **Parameters:**
221
+ file_path: /Users/michal/workspace/python/flagrant-client/src/flagrant/__init__.py
222
+ old_string: from flagrant.client import FlagrantClient, FlagsmithOpts
223
+ from flagrant.grpc_transport import GrpcTransport
224
+ from flagrant.http_transport import HttpTransport
225
+ from flagrant.transport import PermanentError, Transport, TransientError
226
+
227
+ __all__ = [
228
+ "FlagrantClient",
229
+ "FlagsmithOpts",
230
+ new_string: from flagrant.client import FlagrantClient, FlagrantOpts
231
+ from flagrant.grpc_transport import GrpcTransport
232
+ from flagrant.http_transport import HttpTransport
233
+ from flagrant.transport import PermanentError, Transport, TransientError
234
+
235
+ __all__ = [
236
+ "FlagrantClient",
237
+ "FlagrantOpts",
238
+
239
+ ```
240
+ ╭──────────────────────────╮
241
+ │ src/flagrant/__init__.py │
242
+ ╰──────────────────────────╯
243
+
244
+ @@ -1,11 +1,11 @@
245
+ -from flagrant.client import FlagrantClient, FlagsmithOpts
246
+ +from flagrant.client import FlagrantClient, FlagrantOpts
247
+ from flagrant.grpc_transport import GrpcTransport
248
+ from flagrant.http_transport import HttpTransport
249
+ from flagrant.transport import PermanentError, Transport, TransientError
250
+
251
+ __all__ = [
252
+ "FlagrantClient",
253
+ - "FlagsmithOpts",
254
+ + "FlagrantOpts",
255
+ "GrpcTransport",
256
+ "HttpTransport",
257
+ "PermanentError",
258
+
259
+ ```
260
+
261
+ ## Agent (2026-10-01 22:10:59)
262
+
263
+ Done — renamed `FlagsmithOpts` → `FlagrantOpts` in `client.py:12,24` and `__init__.py:1,8`.
264
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flagrant-client
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A python client for Flagrant feature-flagging API
5
5
  Project-URL: Homepage, https://github.com/mbuczko/flagrant-client-python
6
6
  Project-URL: Repository, https://github.com/mbuczko/flagrant-client-python
@@ -39,7 +39,7 @@ This split lets `FlagrantClient.get_features` react differently depending on whe
39
39
 
40
40
  ## Caching and fallback
41
41
 
42
- `FlagrantClient` optionally wraps fetches in an `LRUCache` (per `(environment, identity)` key), configured via `FlagsmithOpts(cache_size=..., cache_ttl=...)`. On a `TransientError`, the client tries a stale cache entry before falling back to a user-supplied `default_handler`, and only re-raises if neither is available.
42
+ `FlagrantClient` optionally wraps fetches in an `LRUCache` (per `(environment, identity)` key), configured via `FlagrantOpts(cache_size=..., cache_ttl=...)`. On a `TransientError`, the client tries a stale cache entry before falling back to a user-supplied `default_handler`, and only re-raises if neither is available.
43
43
 
44
44
  ## Testing in IPython
45
45
 
@@ -21,7 +21,7 @@ This split lets `FlagrantClient.get_features` react differently depending on whe
21
21
 
22
22
  ## Caching and fallback
23
23
 
24
- `FlagrantClient` optionally wraps fetches in an `LRUCache` (per `(environment, identity)` key), configured via `FlagsmithOpts(cache_size=..., cache_ttl=...)`. On a `TransientError`, the client tries a stale cache entry before falling back to a user-supplied `default_handler`, and only re-raises if neither is available.
24
+ `FlagrantClient` optionally wraps fetches in an `LRUCache` (per `(environment, identity)` key), configured via `FlagrantOpts(cache_size=..., cache_ttl=...)`. On a `TransientError`, the client tries a stale cache entry before falling back to a user-supplied `default_handler`, and only re-raises if neither is available.
25
25
 
26
26
  ## Testing in IPython
27
27
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "flagrant-client"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "A python client for Flagrant feature-flagging API"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,11 +1,11 @@
1
- from flagrant.client import FlagrantClient, FlagsmithOpts
1
+ from flagrant.client import FlagrantClient, FlagrantOpts
2
2
  from flagrant.grpc_transport import GrpcTransport
3
3
  from flagrant.http_transport import HttpTransport
4
4
  from flagrant.transport import PermanentError, Transport, TransientError
5
5
 
6
6
  __all__ = [
7
7
  "FlagrantClient",
8
- "FlagsmithOpts",
8
+ "FlagrantOpts",
9
9
  "GrpcTransport",
10
10
  "HttpTransport",
11
11
  "PermanentError",
@@ -28,8 +28,10 @@ class LRUCache(Generic[K, V]):
28
28
  return None
29
29
 
30
30
  timestamp, value = entry
31
+
31
32
  if self.ttl is not None and time.monotonic() - timestamp > self.ttl:
32
33
  return None
34
+
33
35
  self._data.move_to_end(key)
34
36
  return value
35
37
 
@@ -9,7 +9,7 @@ from flagrant.transport import Transport, TransientError
9
9
  DefaultFlagHandler = Callable[[str, str], list[Feature] | None]
10
10
 
11
11
  @dataclass(frozen=True)
12
- class FlagsmithOpts:
12
+ class FlagrantOpts:
13
13
  cache_ttl: float | None = None
14
14
  cache_size: int | None = None
15
15
 
@@ -21,7 +21,7 @@ class FlagrantClient:
21
21
  transport: Transport,
22
22
  *,
23
23
  default_handler: DefaultFlagHandler | None = None,
24
- opts: FlagsmithOpts = FlagsmithOpts()
24
+ opts: FlagrantOpts = FlagrantOpts()
25
25
  ) -> None:
26
26
  self.project = project
27
27
  self.transport = transport