flpit 0.2.0.dev2__tar.gz → 0.2.1__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.
@@ -1,3 +1,27 @@
1
+ Metadata-Version: 2.5
2
+ Name: flpit
3
+ Version: 0.2.1
4
+ Summary: Fluent LINQ for Python — flip your iterables
5
+ Project-URL: Homepage, https://github.com/riemar/flpit
6
+ Project-URL: Repository, https://github.com/riemar/flpit
7
+ Project-URL: Issues, https://github.com/riemar/flpit/issues
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: fluent,generators,iterable,lazy-evaluation,linq,pipeline,pyrefly,type-safe
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.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: 3.15
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.12
22
+ Requires-Dist: msgspec>=0.22.0
23
+ Description-Content-Type: text/markdown
24
+
1
25
  # FlpIt
2
26
 
3
27
  > **Fluent LINQ for Python — LINQ semantics, Python-native execution**
@@ -130,6 +154,52 @@ print(list(backend_engineers))
130
154
  # ['Alice']
131
155
  ```
132
156
 
157
+ ## JSON Serialization
158
+
159
+ FlpIt provides typed JSON serialization and deserialization through `flpit.json.JsonConvert`,
160
+ backed by [`msgspec`](https://github.com/jcrist/msgspec).
161
+
162
+ It supports dataclasses, nested models, generic containers, configurable property naming, and explicit field metadata.
163
+
164
+ ```python
165
+ from dataclasses import dataclass
166
+ from typing import Annotated
167
+
168
+ from flpit import FlpList
169
+ from flpit.json import JProp, JIgnore, JsonConvert
170
+
171
+ @dataclass
172
+ class Person:
173
+ first_name: str
174
+ internal_id: Annotated[str, JProp("id")]
175
+ password: Annotated[str, JIgnore] = ""
176
+
177
+
178
+ person = Person("Ada", "usr-42", "secret")
179
+ raw = JsonConvert.serialize(person, Person)
180
+ # {"firstName":"Ada","id":"usr-42"}
181
+
182
+ people_json = """
183
+ [
184
+ {"firstName": "Ada", "id": "usr-42"},
185
+ {"firstName": "Grace", "id": "usr-43"}
186
+ ]
187
+ """
188
+
189
+ people: FlpList[Person] = FlpList.from_json(people_json, Person)
190
+ ```
191
+
192
+ ### Mapping and conversion
193
+
194
+ - **Naming policies:** Configure automatic property naming with `JsonSerializerOptions`. The default is camel case.
195
+ - **Explicit metadata:** `JProp` assigns a specific JSON property name, `JIgnore` excludes a field, and `JInclude` explicitly includes otherwise excluded underscore-prefixed fields.
196
+ - **Serialization options:** Control default-value omission and whether unknown JSON fields are accepted or rejected.
197
+ - **Typed collections:** Pass the complete target type, such as `list[Person]`, so nested and generic values can be decoded against the intended schema.
198
+
199
+ For large outputs, `JsonConvert.serialize_to()` writes a JSON array to a file-like stream without first materializing the entire serialized document.
200
+
201
+ By default, deserialization returns instances of the requested model type. Setting `native=True` opts into the generated `msgspec.Struct` representation instead, trading the original model type for the native representation and its performance characteristics.
202
+
133
203
  ## 🔬 LINQ Semantics, Python Runtime
134
204
 
135
205
  `flpit` deliberately separates the two:
@@ -325,6 +395,21 @@ to_list ← terminal materialization
325
395
 
326
396
  Operations such as `order_by()` necessarily materialize their input internally to establish ordering.
327
397
 
398
+ ## ⚙️ Task Runtime
399
+
400
+ FlpIt includes a lightweight, `asyncio`-free task runtime for scheduling and coordinating concurrent work, with synchronous callables executed through a bounded worker pool.
401
+
402
+ Key features:
403
+
404
+ - **Task scheduling:** `Task.run()` schedules coroutines, awaitables, and synchronous callables.
405
+ - **Task coordination:** `Task.when_all()` waits for all tasks while preserving input order; `Task.when_any()` returns the first completed task; `Task.collect()` yields tasks as they complete.
406
+ - **Cancellation:** `CancellationToken` and `CancellationTokenSource` support cooperative cancellation, cancellation callbacks, timeouts, and linked cancellation tokens.
407
+ - **Resource management:** Configure worker capacity and pending background-work limits, handle queue saturation explicitly, and shut down the process-wide runtime with an observable shutdown report.
408
+
409
+ The runtime starts lazily and manages its own lifecycle. It is deliberately not an `asyncio` compatibility layer; cancellation of running synchronous functions is cooperative.
410
+
411
+ See the [Task Runtime examples](https://github.com/riemar/flpit/tree/main/examples/task) for usage examples.
412
+
328
413
  ## ⚙️ Installation
329
414
 
330
415
  ```bash
@@ -349,16 +434,18 @@ Distributed under the MIT License. See `LICENSE` for more information.
349
434
  | ├── test_all.py | 80 |
350
435
  | ├── test_any.py | 90 |
351
436
  | ├── test_average.py | 572 |
437
+ | ├── test_concat.py | 64 |
352
438
  | ├── test_count.py | 37 |
353
439
  | ├── test_generic_list.py | 95 |
354
440
  | ├── test_last.py | 29 |
355
441
  | ├── test_order_by.py | 100 |
356
442
  | ├── test_order_descending_by.py | 45 |
443
+ | ├── test_skip.py | 75 |
357
444
  | ├── test_sum.py | 134 |
358
445
  | ├── test_take.py | 39 |
359
446
  | ├── test_then_by.py | 40 |
360
447
  | └── test_then_descending_by.py | 34 |
361
448
  ----------------------------------------------------------------
362
- | GRAND TOTALS | 1295 |
449
+ | GRAND TOTALS | 1434 |
363
450
  ================================================================
364
451
  ```
@@ -1,23 +1,3 @@
1
- Metadata-Version: 2.5
2
- Name: flpit
3
- Version: 0.2.0.dev2
4
- Summary: Fluent LINQ for Python — flip your iterables
5
- License: MIT
6
- License-File: LICENSE
7
- Keywords: fluent,generators,iterable,lazy-evaluation,linq,pipeline,pyrefly,type-safe
8
- Classifier: Development Status :: 3 - Alpha
9
- Classifier: Intended Audience :: Developers
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.11
13
- Classifier: Programming Language :: Python :: 3.12
14
- Classifier: Programming Language :: Python :: 3.13
15
- Classifier: Programming Language :: Python :: 3.14
16
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
- Classifier: Typing :: Typed
18
- Requires-Python: >=3.11
19
- Description-Content-Type: text/markdown
20
-
21
1
  # FlpIt
22
2
 
23
3
  > **Fluent LINQ for Python — LINQ semantics, Python-native execution**
@@ -150,6 +130,52 @@ print(list(backend_engineers))
150
130
  # ['Alice']
151
131
  ```
152
132
 
133
+ ## JSON Serialization
134
+
135
+ FlpIt provides typed JSON serialization and deserialization through `flpit.json.JsonConvert`,
136
+ backed by [`msgspec`](https://github.com/jcrist/msgspec).
137
+
138
+ It supports dataclasses, nested models, generic containers, configurable property naming, and explicit field metadata.
139
+
140
+ ```python
141
+ from dataclasses import dataclass
142
+ from typing import Annotated
143
+
144
+ from flpit import FlpList
145
+ from flpit.json import JProp, JIgnore, JsonConvert
146
+
147
+ @dataclass
148
+ class Person:
149
+ first_name: str
150
+ internal_id: Annotated[str, JProp("id")]
151
+ password: Annotated[str, JIgnore] = ""
152
+
153
+
154
+ person = Person("Ada", "usr-42", "secret")
155
+ raw = JsonConvert.serialize(person, Person)
156
+ # {"firstName":"Ada","id":"usr-42"}
157
+
158
+ people_json = """
159
+ [
160
+ {"firstName": "Ada", "id": "usr-42"},
161
+ {"firstName": "Grace", "id": "usr-43"}
162
+ ]
163
+ """
164
+
165
+ people: FlpList[Person] = FlpList.from_json(people_json, Person)
166
+ ```
167
+
168
+ ### Mapping and conversion
169
+
170
+ - **Naming policies:** Configure automatic property naming with `JsonSerializerOptions`. The default is camel case.
171
+ - **Explicit metadata:** `JProp` assigns a specific JSON property name, `JIgnore` excludes a field, and `JInclude` explicitly includes otherwise excluded underscore-prefixed fields.
172
+ - **Serialization options:** Control default-value omission and whether unknown JSON fields are accepted or rejected.
173
+ - **Typed collections:** Pass the complete target type, such as `list[Person]`, so nested and generic values can be decoded against the intended schema.
174
+
175
+ For large outputs, `JsonConvert.serialize_to()` writes a JSON array to a file-like stream without first materializing the entire serialized document.
176
+
177
+ By default, deserialization returns instances of the requested model type. Setting `native=True` opts into the generated `msgspec.Struct` representation instead, trading the original model type for the native representation and its performance characteristics.
178
+
153
179
  ## 🔬 LINQ Semantics, Python Runtime
154
180
 
155
181
  `flpit` deliberately separates the two:
@@ -345,6 +371,21 @@ to_list ← terminal materialization
345
371
 
346
372
  Operations such as `order_by()` necessarily materialize their input internally to establish ordering.
347
373
 
374
+ ## ⚙️ Task Runtime
375
+
376
+ FlpIt includes a lightweight, `asyncio`-free task runtime for scheduling and coordinating concurrent work, with synchronous callables executed through a bounded worker pool.
377
+
378
+ Key features:
379
+
380
+ - **Task scheduling:** `Task.run()` schedules coroutines, awaitables, and synchronous callables.
381
+ - **Task coordination:** `Task.when_all()` waits for all tasks while preserving input order; `Task.when_any()` returns the first completed task; `Task.collect()` yields tasks as they complete.
382
+ - **Cancellation:** `CancellationToken` and `CancellationTokenSource` support cooperative cancellation, cancellation callbacks, timeouts, and linked cancellation tokens.
383
+ - **Resource management:** Configure worker capacity and pending background-work limits, handle queue saturation explicitly, and shut down the process-wide runtime with an observable shutdown report.
384
+
385
+ The runtime starts lazily and manages its own lifecycle. It is deliberately not an `asyncio` compatibility layer; cancellation of running synchronous functions is cooperative.
386
+
387
+ See the [Task Runtime examples](https://github.com/riemar/flpit/tree/main/examples/task) for usage examples.
388
+
348
389
  ## ⚙️ Installation
349
390
 
350
391
  ```bash
@@ -369,16 +410,18 @@ Distributed under the MIT License. See `LICENSE` for more information.
369
410
  | ├── test_all.py | 80 |
370
411
  | ├── test_any.py | 90 |
371
412
  | ├── test_average.py | 572 |
413
+ | ├── test_concat.py | 64 |
372
414
  | ├── test_count.py | 37 |
373
415
  | ├── test_generic_list.py | 95 |
374
416
  | ├── test_last.py | 29 |
375
417
  | ├── test_order_by.py | 100 |
376
418
  | ├── test_order_descending_by.py | 45 |
419
+ | ├── test_skip.py | 75 |
377
420
  | ├── test_sum.py | 134 |
378
421
  | ├── test_take.py | 39 |
379
422
  | ├── test_then_by.py | 40 |
380
423
  | └── test_then_descending_by.py | 34 |
381
424
  ----------------------------------------------------------------
382
- | GRAND TOTALS | 1295 |
425
+ | GRAND TOTALS | 1434 |
383
426
  ================================================================
384
427
  ```
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "flpit"
3
- version = "0.2.0.dev2"
3
+ version = "0.2.1"
4
4
  description = "Fluent LINQ for Python — flip your iterables"
5
5
  readme = "README.md"
6
- requires-python = ">=3.11"
6
+ requires-python = ">=3.12"
7
7
  license = { text = "MIT" }
8
8
  keywords = ["linq", "fluent", "iterable", "pipeline", "lazy-evaluation", "pyrefly", "type-safe", "generators"]
9
9
  classifiers = [
@@ -11,14 +11,21 @@ classifiers = [
11
11
  "Intended Audience :: Developers",
12
12
  "License :: OSI Approved :: MIT License",
13
13
  "Programming Language :: Python :: 3",
14
- "Programming Language :: Python :: 3.11",
15
14
  "Programming Language :: Python :: 3.12",
16
15
  "Programming Language :: Python :: 3.13",
17
16
  "Programming Language :: Python :: 3.14",
17
+ "Programming Language :: Python :: 3.15",
18
18
  "Topic :: Software Development :: Libraries :: Python Modules",
19
19
  "Typing :: Typed"
20
20
  ]
21
- dependencies = []
21
+ dependencies = [
22
+ "msgspec>=0.22.0",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/riemar/flpit"
27
+ Repository = "https://github.com/riemar/flpit"
28
+ Issues = "https://github.com/riemar/flpit/issues"
22
29
 
23
30
  [dependency-groups]
24
31
  dev = [
@@ -28,6 +35,7 @@ dev = [
28
35
  "pyright>=1.1.414",
29
36
  "pytest-benchmark>=5.3.0",
30
37
  "mypy>=2.4.0",
38
+ "httpx>=0.28.1",
31
39
  ]
32
40
 
33
41
 
@@ -5,33 +5,59 @@ from __future__ import annotations
5
5
  from importlib.metadata import version
6
6
  __version__ = version("flpit")
7
7
 
8
- import builtins
9
- from typing import Iterable as _Iterable, TypeVar
10
-
11
8
  from flpit import flp
12
9
  from flpit.core.linq import (
13
10
  FlpIt,
14
11
  FlpList,
15
12
  Grouping,
16
13
  OrderedIt,
14
+
15
+ #====================
16
+ # InvalidOperationException
17
+ #====================
18
+ InvalidOperationError,
17
19
  EmptySequenceError,
18
20
  NoMatchError,
19
21
  MultipleElementsError,
20
22
  MultipleMatchesError,
23
+
24
+ #====================
25
+ # ArgumentExceptions
26
+ #====================
27
+ ArgumentError,
28
+ ArgumentNullError,
21
29
  SourceNoneError,
22
30
  PredicateNoneError,
31
+ CollectionNoneError,
32
+ SelectorNoneError,
33
+ SecondNoneError,
34
+ ArgumentOutOfRangeError,
35
+ ArgumentNonNegError,
36
+ ArgumentOutOfBoundsError
23
37
  )
24
38
 
25
39
  __all__ = [
40
+ "flp",
26
41
  "FlpIt",
27
42
  "FlpList",
28
43
  "Grouping",
29
44
  "OrderedIt",
45
+
46
+ "InvalidOperationError",
30
47
  "EmptySequenceError",
31
48
  "NoMatchError",
32
49
  "MultipleElementsError",
33
50
  "MultipleMatchesError",
51
+
52
+ "ArgumentError",
53
+ "ArgumentNullError",
34
54
  "SourceNoneError",
35
55
  "PredicateNoneError",
56
+ "CollectionNoneError",
57
+ "SelectorNoneError",
58
+ "SecondNoneError",
59
+ "ArgumentOutOfRangeError",
60
+ "ArgumentNonNegError",
61
+ "ArgumentOutOfBoundsError",
36
62
  ]
37
63