flpit 0.1.3__tar.gz → 0.1.4.dev7__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.
- flpit-0.1.4.dev7/.gitignore +10 -0
- flpit-0.1.4.dev7/LICENSE +21 -0
- flpit-0.1.4.dev7/PKG-INFO +187 -0
- flpit-0.1.4.dev7/README.md +167 -0
- flpit-0.1.3/pyproject.toml.orig → flpit-0.1.4.dev7/pyproject.toml +25 -13
- flpit-0.1.4.dev7/src/flp/__init__.py +21 -0
- flpit-0.1.4.dev7/src/flpit/__init__.py +37 -0
- {flpit-0.1.3/src/flp → flpit-0.1.4.dev7/src/flpit}/core/linq.py +70 -31
- flpit-0.1.3/src/flp/__init__.py → flpit-0.1.4.dev7/src/flpit/flp.py +1 -6
- flpit-0.1.3/PKG-INFO +0 -61
- flpit-0.1.3/README.md +0 -45
- flpit-0.1.3/pyproject.toml +0 -49
- {flpit-0.1.3/src/flp → flpit-0.1.4.dev7/src/flpit}/core/__init__.py +0 -0
- {flpit-0.1.3/src/flp → flpit-0.1.4.dev7/src/flpit}/py.typed +0 -0
flpit-0.1.4.dev7/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 riemar
|
|
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.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: flpit
|
|
3
|
+
Version: 0.1.4.dev7
|
|
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
|
+
# FlpIt
|
|
22
|
+
|
|
23
|
+
> **Fluent LINQ for Python — LINQ semantics, Python-native execution**
|
|
24
|
+
|
|
25
|
+
`FlpIt` brings a strongly typed, LINQ-style API to standard Python iterables. It combines the fluent query model and semantics of .NET LINQ with Python's native iterable and generator model.
|
|
26
|
+
|
|
27
|
+
> **Semantic goal:** `flp` strives to faithfully reproduce .NET LINQ semantics.
|
|
28
|
+
> Where Python's language or type system requires a different API contract, the contract is adapted accordingly.
|
|
29
|
+
> The implementation is actively developed, and semantic deviations may still exist.
|
|
30
|
+
|
|
31
|
+
## ⚡ Key Features
|
|
32
|
+
|
|
33
|
+
* **⚡ Lazy Evaluation (`FlpIt`):** Deferred, streaming execution where the underlying operation permits it. `FlpIt` wraps a standard Python `Iterable[T]`.
|
|
34
|
+
* **📦 Materialized Container (`FlpList`):** Eager, mutable container backed by `collections.UserList`.
|
|
35
|
+
* **🎯 LINQ Semantics:** Operators such as `.any()`, `.first()`, `.single()`, `.order_by()`, and `.then_by()` follow .NET LINQ semantics.
|
|
36
|
+
* **🔀 Independent Query Pipelines:** Derived queries are independent and never mutate or retroactively affect separately created queries.
|
|
37
|
+
* **🧠 Lazy Internal Materialization:** Some operators, such as ordering, require internal materialization during deferred execution, while the query itself remains lazy.
|
|
38
|
+
* **🔹 Static-First Typing:** Designed for precise `pyrefly` inference without plugins (mypy and pyright might give varying results).
|
|
39
|
+
* **🐍 Python-Native:** Built on standard Python iterables, iterators, generators, and callable semantics.
|
|
40
|
+
|
|
41
|
+
## 🤸 Flipping the Pipeline
|
|
42
|
+
|
|
43
|
+
Native Python functional operations tend to produce an inside-out, right-to-left reading pattern...
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
# 🔁 Native Built-ins
|
|
47
|
+
result = list(map(lambda x: x * 10, filter(lambda x: x % 2 == 0, range(1, 11))))
|
|
48
|
+
|
|
49
|
+
# 🤸 FlpIt Pipeline
|
|
50
|
+
result = (
|
|
51
|
+
flp.it(range(1, 11))
|
|
52
|
+
.where(lambda x: x % 2 == 0)
|
|
53
|
+
.select(lambda x: x * 10)
|
|
54
|
+
.to_list()
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 💡 Quick Start
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
|
|
62
|
+
from flpit import flp, FlpIt, FlpList
|
|
63
|
+
|
|
64
|
+
# Deferred iterable via shorthand
|
|
65
|
+
data: FlpIt[int] = flp.it(range(1, 11))
|
|
66
|
+
|
|
67
|
+
# Lazy, streaming pipeline
|
|
68
|
+
query: FlpIt[int] = (
|
|
69
|
+
data
|
|
70
|
+
.where(lambda x: x % 2 == 0)
|
|
71
|
+
.select(lambda x: x * 10)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Explicit materialization
|
|
75
|
+
result: FlpList[int] = query.to_list()
|
|
76
|
+
# [20, 40, 60, 80, 100]
|
|
77
|
+
|
|
78
|
+
# Or start directly with an eager container
|
|
79
|
+
eager_list: FlpList[int] = flp.lst([1, 2, 3, 4])
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 🔬 LINQ Semantics, Python Runtime
|
|
83
|
+
|
|
84
|
+
`flpit` deliberately separates the two:
|
|
85
|
+
|
|
86
|
+
- **LINQ defines the query semantics.**
|
|
87
|
+
- **Python provides the underlying iterable and execution model.**
|
|
88
|
+
|
|
89
|
+
For example:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
query.any()
|
|
93
|
+
query.first()
|
|
94
|
+
query.single()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
use LINQ semantics, while native Python operations remain Pythonic:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
any(query)
|
|
101
|
+
list(query)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This provides a predictable LINQ vocabulary without attempting to replace Python's native iteration model.
|
|
105
|
+
|
|
106
|
+
## 🔗 Independent Queries
|
|
107
|
+
|
|
108
|
+
LINQ queries are composable without mutating the query they were derived from.
|
|
109
|
+
|
|
110
|
+
For example:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
@dataclass
|
|
114
|
+
class Item: group: str; value: int
|
|
115
|
+
|
|
116
|
+
original = flp.it([Item("A", 2), Item("B", 1)])
|
|
117
|
+
|
|
118
|
+
ordered = original.order_by(lambda x: x.group)
|
|
119
|
+
|
|
120
|
+
extended = original.concat([Item("A", 1), Item("B", 2)])
|
|
121
|
+
|
|
122
|
+
ordered_by_group_and_value = ordered.then_by(lambda x: x.value)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The resulting query graph is:
|
|
126
|
+
|
|
127
|
+
```text
|
|
128
|
+
original
|
|
129
|
+
│
|
|
130
|
+
├── order_by ──→ ordered
|
|
131
|
+
│ │
|
|
132
|
+
│ └── then_by ──→ ordered_by_group_and_value
|
|
133
|
+
│
|
|
134
|
+
└── concat ─────→ extended
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`ordered_by_group_and_value` is derived from `ordered`.
|
|
138
|
+
It does not modify `original`, and it is not affected by the separately created `extended` query.
|
|
139
|
+
|
|
140
|
+
This matches the independent-query behavior of .NET LINQ.
|
|
141
|
+
|
|
142
|
+
## 🧠 Lazy Pipelines
|
|
143
|
+
|
|
144
|
+
Streaming operations do not materialize intermediate results:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
query = (
|
|
148
|
+
flp.it(source)
|
|
149
|
+
.where(predicate)
|
|
150
|
+
.select(selector)
|
|
151
|
+
.order_by(key_selector)
|
|
152
|
+
.take(100)
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Conceptually:
|
|
157
|
+
|
|
158
|
+
```text
|
|
159
|
+
source
|
|
160
|
+
│
|
|
161
|
+
▼
|
|
162
|
+
where ← streaming
|
|
163
|
+
│
|
|
164
|
+
▼
|
|
165
|
+
select ← streaming
|
|
166
|
+
│
|
|
167
|
+
▼
|
|
168
|
+
order_by ← lazy, internally buffers when enumerated
|
|
169
|
+
│
|
|
170
|
+
▼
|
|
171
|
+
take ← streaming
|
|
172
|
+
│
|
|
173
|
+
▼
|
|
174
|
+
to_list ← consumer-facing materialization
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Operations such as `order_by()` necessarily materialize their input internally to establish ordering.
|
|
178
|
+
|
|
179
|
+
## ⚙️ Installation
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
uv add flpit
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## 📜 License
|
|
186
|
+
|
|
187
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# FlpIt
|
|
2
|
+
|
|
3
|
+
> **Fluent LINQ for Python — LINQ semantics, Python-native execution**
|
|
4
|
+
|
|
5
|
+
`FlpIt` brings a strongly typed, LINQ-style API to standard Python iterables. It combines the fluent query model and semantics of .NET LINQ with Python's native iterable and generator model.
|
|
6
|
+
|
|
7
|
+
> **Semantic goal:** `flp` strives to faithfully reproduce .NET LINQ semantics.
|
|
8
|
+
> Where Python's language or type system requires a different API contract, the contract is adapted accordingly.
|
|
9
|
+
> The implementation is actively developed, and semantic deviations may still exist.
|
|
10
|
+
|
|
11
|
+
## ⚡ Key Features
|
|
12
|
+
|
|
13
|
+
* **⚡ Lazy Evaluation (`FlpIt`):** Deferred, streaming execution where the underlying operation permits it. `FlpIt` wraps a standard Python `Iterable[T]`.
|
|
14
|
+
* **📦 Materialized Container (`FlpList`):** Eager, mutable container backed by `collections.UserList`.
|
|
15
|
+
* **🎯 LINQ Semantics:** Operators such as `.any()`, `.first()`, `.single()`, `.order_by()`, and `.then_by()` follow .NET LINQ semantics.
|
|
16
|
+
* **🔀 Independent Query Pipelines:** Derived queries are independent and never mutate or retroactively affect separately created queries.
|
|
17
|
+
* **🧠 Lazy Internal Materialization:** Some operators, such as ordering, require internal materialization during deferred execution, while the query itself remains lazy.
|
|
18
|
+
* **🔹 Static-First Typing:** Designed for precise `pyrefly` inference without plugins (mypy and pyright might give varying results).
|
|
19
|
+
* **🐍 Python-Native:** Built on standard Python iterables, iterators, generators, and callable semantics.
|
|
20
|
+
|
|
21
|
+
## 🤸 Flipping the Pipeline
|
|
22
|
+
|
|
23
|
+
Native Python functional operations tend to produce an inside-out, right-to-left reading pattern...
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
# 🔁 Native Built-ins
|
|
27
|
+
result = list(map(lambda x: x * 10, filter(lambda x: x % 2 == 0, range(1, 11))))
|
|
28
|
+
|
|
29
|
+
# 🤸 FlpIt Pipeline
|
|
30
|
+
result = (
|
|
31
|
+
flp.it(range(1, 11))
|
|
32
|
+
.where(lambda x: x % 2 == 0)
|
|
33
|
+
.select(lambda x: x * 10)
|
|
34
|
+
.to_list()
|
|
35
|
+
)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 💡 Quick Start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
|
|
42
|
+
from flpit import flp, FlpIt, FlpList
|
|
43
|
+
|
|
44
|
+
# Deferred iterable via shorthand
|
|
45
|
+
data: FlpIt[int] = flp.it(range(1, 11))
|
|
46
|
+
|
|
47
|
+
# Lazy, streaming pipeline
|
|
48
|
+
query: FlpIt[int] = (
|
|
49
|
+
data
|
|
50
|
+
.where(lambda x: x % 2 == 0)
|
|
51
|
+
.select(lambda x: x * 10)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Explicit materialization
|
|
55
|
+
result: FlpList[int] = query.to_list()
|
|
56
|
+
# [20, 40, 60, 80, 100]
|
|
57
|
+
|
|
58
|
+
# Or start directly with an eager container
|
|
59
|
+
eager_list: FlpList[int] = flp.lst([1, 2, 3, 4])
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 🔬 LINQ Semantics, Python Runtime
|
|
63
|
+
|
|
64
|
+
`flpit` deliberately separates the two:
|
|
65
|
+
|
|
66
|
+
- **LINQ defines the query semantics.**
|
|
67
|
+
- **Python provides the underlying iterable and execution model.**
|
|
68
|
+
|
|
69
|
+
For example:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
query.any()
|
|
73
|
+
query.first()
|
|
74
|
+
query.single()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
use LINQ semantics, while native Python operations remain Pythonic:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
any(query)
|
|
81
|
+
list(query)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This provides a predictable LINQ vocabulary without attempting to replace Python's native iteration model.
|
|
85
|
+
|
|
86
|
+
## 🔗 Independent Queries
|
|
87
|
+
|
|
88
|
+
LINQ queries are composable without mutating the query they were derived from.
|
|
89
|
+
|
|
90
|
+
For example:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
@dataclass
|
|
94
|
+
class Item: group: str; value: int
|
|
95
|
+
|
|
96
|
+
original = flp.it([Item("A", 2), Item("B", 1)])
|
|
97
|
+
|
|
98
|
+
ordered = original.order_by(lambda x: x.group)
|
|
99
|
+
|
|
100
|
+
extended = original.concat([Item("A", 1), Item("B", 2)])
|
|
101
|
+
|
|
102
|
+
ordered_by_group_and_value = ordered.then_by(lambda x: x.value)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The resulting query graph is:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
original
|
|
109
|
+
│
|
|
110
|
+
├── order_by ──→ ordered
|
|
111
|
+
│ │
|
|
112
|
+
│ └── then_by ──→ ordered_by_group_and_value
|
|
113
|
+
│
|
|
114
|
+
└── concat ─────→ extended
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`ordered_by_group_and_value` is derived from `ordered`.
|
|
118
|
+
It does not modify `original`, and it is not affected by the separately created `extended` query.
|
|
119
|
+
|
|
120
|
+
This matches the independent-query behavior of .NET LINQ.
|
|
121
|
+
|
|
122
|
+
## 🧠 Lazy Pipelines
|
|
123
|
+
|
|
124
|
+
Streaming operations do not materialize intermediate results:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
query = (
|
|
128
|
+
flp.it(source)
|
|
129
|
+
.where(predicate)
|
|
130
|
+
.select(selector)
|
|
131
|
+
.order_by(key_selector)
|
|
132
|
+
.take(100)
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Conceptually:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
source
|
|
140
|
+
│
|
|
141
|
+
▼
|
|
142
|
+
where ← streaming
|
|
143
|
+
│
|
|
144
|
+
▼
|
|
145
|
+
select ← streaming
|
|
146
|
+
│
|
|
147
|
+
▼
|
|
148
|
+
order_by ← lazy, internally buffers when enumerated
|
|
149
|
+
│
|
|
150
|
+
▼
|
|
151
|
+
take ← streaming
|
|
152
|
+
│
|
|
153
|
+
▼
|
|
154
|
+
to_list ← consumer-facing materialization
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Operations such as `order_by()` necessarily materialize their input internally to establish ordering.
|
|
158
|
+
|
|
159
|
+
## ⚙️ Installation
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
uv add flpit
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## 📜 License
|
|
166
|
+
|
|
167
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flpit"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.4.dev7"
|
|
4
4
|
description = "Fluent LINQ for Python — flip your iterables"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
license = { text = "MIT" }
|
|
8
|
+
keywords = ["linq", "fluent", "iterable", "pipeline", "lazy-evaluation", "pyrefly", "type-safe", "generators"]
|
|
8
9
|
classifiers = [
|
|
9
10
|
"Development Status :: 3 - Alpha",
|
|
10
11
|
"Intended Audience :: Developers",
|
|
@@ -13,7 +14,9 @@ classifiers = [
|
|
|
13
14
|
"Programming Language :: Python :: 3.11",
|
|
14
15
|
"Programming Language :: Python :: 3.12",
|
|
15
16
|
"Programming Language :: Python :: 3.13",
|
|
17
|
+
"Programming Language :: Python :: 3.14",
|
|
16
18
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
19
|
+
"Typing :: Typed"
|
|
17
20
|
]
|
|
18
21
|
dependencies = []
|
|
19
22
|
|
|
@@ -26,24 +29,33 @@ dev = [
|
|
|
26
29
|
"pytest-benchmark>=5.3.0",
|
|
27
30
|
]
|
|
28
31
|
|
|
32
|
+
|
|
29
33
|
[build-system]
|
|
30
|
-
requires = ["
|
|
31
|
-
build-backend = "
|
|
34
|
+
requires = ["hatchling"]
|
|
35
|
+
build-backend = "hatchling.build"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
# This tells Hatchling exactly which folders to grab
|
|
39
|
+
packages = ["src/flp", "src/flpit"]
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.sdist]
|
|
42
|
+
# This keeps the exact working sdist inclusion rule you had earlier
|
|
43
|
+
include = ["src/flp", "src/flpit"]
|
|
32
44
|
|
|
33
|
-
[tool.uv.build-backend]
|
|
34
|
-
module-name = "flp"
|
|
35
|
-
module-root = "src"
|
|
36
45
|
|
|
37
|
-
[tool.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
[tool.pyrefly]
|
|
47
|
+
project-includes = ["src"]
|
|
48
|
+
project-excludes = ["**/__pycache__", "tests"]
|
|
49
|
+
search-path = ["src"]
|
|
50
|
+
preset = "strict"
|
|
41
51
|
|
|
42
52
|
[tool.pytest.ini_options]
|
|
53
|
+
pythonpath = [
|
|
54
|
+
"src",
|
|
55
|
+
"tests"
|
|
56
|
+
]
|
|
43
57
|
addopts = "-m 'not benchmark'"
|
|
44
58
|
markers = [
|
|
45
59
|
"benchmark: performance benchmarks",
|
|
46
60
|
]
|
|
47
|
-
|
|
48
|
-
#extra-paths = ["src", "tests"]
|
|
49
|
-
#python-interpreter-path = ".venv/bin/python" # or .venv/Scripts/python.exe on Windows
|
|
61
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
import flpit # The NEW location
|
|
3
|
+
|
|
4
|
+
# Enforce a loud, clear deprecation alert
|
|
5
|
+
warnings.warn(
|
|
6
|
+
"The top-level 'flp' namespace is deprecated and has been moved to 'flpit'. "
|
|
7
|
+
"Please update your imports to 'from flpit import flp, FlpIt, FlpList'"
|
|
8
|
+
"This legacy entry point will throw a hard ImportError in a future version.",
|
|
9
|
+
DeprecationWarning,
|
|
10
|
+
stacklevel=2
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = getattr(flpit.flp, "__all__", []) + getattr(flpit, "__all__", [])
|
|
14
|
+
mods = [flpit, flpit.flp]
|
|
15
|
+
|
|
16
|
+
def __getattr__(name: str):
|
|
17
|
+
for mod in mods:
|
|
18
|
+
if hasattr(mod, name):
|
|
19
|
+
return getattr(mod, name)
|
|
20
|
+
|
|
21
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Fluent LINQ for Python — flip your iterables.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
from importlib.metadata import version
|
|
6
|
+
__version__ = version("flpit")
|
|
7
|
+
|
|
8
|
+
import builtins
|
|
9
|
+
from typing import Iterable as _Iterable, TypeVar
|
|
10
|
+
|
|
11
|
+
from flpit import flp
|
|
12
|
+
from flpit.core.linq import (
|
|
13
|
+
FlpIt,
|
|
14
|
+
FlpList,
|
|
15
|
+
Grouping,
|
|
16
|
+
OrderedIt,
|
|
17
|
+
EmptySequenceError,
|
|
18
|
+
NoMatchError,
|
|
19
|
+
MultipleElementsError,
|
|
20
|
+
MultipleMatchesError,
|
|
21
|
+
SourceNoneError,
|
|
22
|
+
PredicateNoneError,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"FlpIt",
|
|
27
|
+
"FlpList",
|
|
28
|
+
"Grouping",
|
|
29
|
+
"OrderedIt",
|
|
30
|
+
"EmptySequenceError",
|
|
31
|
+
"NoMatchError",
|
|
32
|
+
"MultipleElementsError",
|
|
33
|
+
"MultipleMatchesError",
|
|
34
|
+
"SourceNoneError",
|
|
35
|
+
"PredicateNoneError",
|
|
36
|
+
]
|
|
37
|
+
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from threading import Lock
|
|
3
3
|
|
|
4
|
-
from typing import cast
|
|
5
|
-
|
|
6
4
|
import builtins
|
|
7
5
|
from collections import UserList
|
|
8
6
|
from functools import wraps
|
|
@@ -20,6 +18,7 @@ from typing import (
|
|
|
20
18
|
Type,
|
|
21
19
|
TypeVar,
|
|
22
20
|
Union,
|
|
21
|
+
override,
|
|
23
22
|
overload,
|
|
24
23
|
cast as typing_cast,
|
|
25
24
|
)
|
|
@@ -35,26 +34,34 @@ _MISSING = object()
|
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
class EmptySequenceError(ValueError):
|
|
38
|
-
def __init__(self
|
|
39
|
-
super().__init__(
|
|
37
|
+
def __init__(self):
|
|
38
|
+
super().__init__("Sequence contains no elements")
|
|
40
39
|
|
|
41
40
|
class NoMatchError(ValueError):
|
|
42
|
-
def __init__(self
|
|
43
|
-
super().__init__(
|
|
41
|
+
def __init__(self):
|
|
42
|
+
super().__init__("Sequence contains no matching elements")
|
|
44
43
|
|
|
45
44
|
class MultipleMatchesError(ValueError):
|
|
46
|
-
def __init__(self
|
|
47
|
-
super().__init__(
|
|
45
|
+
def __init__(self):
|
|
46
|
+
super().__init__("Sequence contains more than one matching element")
|
|
48
47
|
|
|
49
48
|
class MultipleElementsError(ValueError):
|
|
50
|
-
def __init__(self
|
|
51
|
-
super().__init__(
|
|
49
|
+
def __init__(self):
|
|
50
|
+
super().__init__("Sequence contains more than one element")
|
|
51
|
+
|
|
52
|
+
class SourceNoneError(TypeError):
|
|
53
|
+
def __init__(self):
|
|
54
|
+
super().__init__("Value cannot be None. (Argument 'source')")
|
|
55
|
+
|
|
56
|
+
class PredicateNoneError(TypeError):
|
|
57
|
+
def __init__(self):
|
|
58
|
+
super().__init__("Value cannot be None. (Argument 'predicate')")
|
|
52
59
|
|
|
53
60
|
|
|
54
61
|
def _guard_empty(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
55
62
|
"""Catches Python's native empty-sequence ValueError and re-raises LINQ-compliant error."""
|
|
56
63
|
@wraps(func)
|
|
57
|
-
def wrapper(self, *args: Any, **kwargs: Any) -> Any:
|
|
64
|
+
def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
|
|
58
65
|
try:
|
|
59
66
|
return func(self, *args, **kwargs)
|
|
60
67
|
except ValueError as e:
|
|
@@ -91,8 +98,10 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
91
98
|
"""
|
|
92
99
|
__slots__ = ("_iterable",)
|
|
93
100
|
|
|
94
|
-
def __init__(self,
|
|
95
|
-
|
|
101
|
+
def __init__(self, source: Iterable[TItem]) -> None:
|
|
102
|
+
if source is None:
|
|
103
|
+
raise SourceNoneError()
|
|
104
|
+
self._iterable: Iterable[TItem] = source
|
|
96
105
|
|
|
97
106
|
def __iter__(self) -> Iterator[TItem]:
|
|
98
107
|
return iter(self._iterable)
|
|
@@ -372,7 +381,7 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
372
381
|
|
|
373
382
|
def last(
|
|
374
383
|
self,
|
|
375
|
-
predicate: Optional[Callable[[TItem], bool]] =
|
|
384
|
+
predicate: Optional[Callable[[TItem], bool]] = _SENTINEL,
|
|
376
385
|
) -> TItem:
|
|
377
386
|
"""
|
|
378
387
|
Returns the last element of the sequence, or the last element
|
|
@@ -385,24 +394,27 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
385
394
|
Raises EmptySequenceError if the sequence is empty or if no
|
|
386
395
|
element satisfies the predicate.
|
|
387
396
|
"""
|
|
388
|
-
|
|
397
|
+
if predicate is None:
|
|
398
|
+
raise PredicateNoneError()
|
|
399
|
+
|
|
400
|
+
last_item: TItem | object = _MISSING
|
|
389
401
|
|
|
390
402
|
for item in self:
|
|
391
|
-
if predicate is
|
|
403
|
+
if predicate is _SENTINEL or predicate(item):
|
|
392
404
|
last_item = item
|
|
393
405
|
|
|
394
|
-
if last_item is
|
|
395
|
-
raise EmptySequenceError() if predicate is
|
|
406
|
+
if last_item is _MISSING:
|
|
407
|
+
raise EmptySequenceError() if predicate is _SENTINEL else NoMatchError()
|
|
396
408
|
|
|
397
|
-
return
|
|
409
|
+
return typing_cast(TItem, last_item)
|
|
398
410
|
|
|
399
|
-
def _extreme(self, func, **kwargs) -> TItem:
|
|
411
|
+
def _extreme(self, func: Any, **kwargs: Any) -> TItem:
|
|
400
412
|
result = func(self, default=_MISSING, **kwargs)
|
|
401
413
|
|
|
402
414
|
if result is _MISSING:
|
|
403
415
|
raise EmptySequenceError()
|
|
404
416
|
|
|
405
|
-
return
|
|
417
|
+
return typing_cast(TItem, result)
|
|
406
418
|
|
|
407
419
|
def min(self) -> TItem:
|
|
408
420
|
"""Returns the minimum value in a sequence."""
|
|
@@ -487,6 +499,7 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
487
499
|
except (EmptySequenceError, NoMatchError):
|
|
488
500
|
return default
|
|
489
501
|
|
|
502
|
+
# pyrefly: ignore [bad-function-definition]
|
|
490
503
|
def single(self, predicate: Optional[Callable[[TItem], bool]] = _SENTINEL) -> TItem:
|
|
491
504
|
"""Returns the single, unique element matching a predicate."""
|
|
492
505
|
if predicate is None:
|
|
@@ -514,9 +527,6 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
514
527
|
return FlpList(self)
|
|
515
528
|
|
|
516
529
|
|
|
517
|
-
_OrderCriterion = tuple[Callable[[TItem], Any], bool]
|
|
518
|
-
|
|
519
|
-
|
|
520
530
|
class _OrderState(Generic[TItem]):
|
|
521
531
|
"""
|
|
522
532
|
Shared lazy snapshot for an OrderBy/ThenBy chain.
|
|
@@ -580,8 +590,9 @@ class OrderedIt(FlpIt[TItem]):
|
|
|
580
590
|
# Pass () to FlpIt to prevent storing a permanent reference to the raw source
|
|
581
591
|
super().__init__(())
|
|
582
592
|
|
|
593
|
+
|
|
583
594
|
self._state = _OrderState(source)
|
|
584
|
-
self._criteria: tuple[
|
|
595
|
+
self._criteria: tuple[tuple[Callable[[TItem], Any], bool], ...] = (
|
|
585
596
|
(key_selector, descending),
|
|
586
597
|
)
|
|
587
598
|
|
|
@@ -636,6 +647,7 @@ class OrderedIt(FlpIt[TItem]):
|
|
|
636
647
|
descending=True,
|
|
637
648
|
)
|
|
638
649
|
|
|
650
|
+
@override
|
|
639
651
|
def __iter__(self) -> Iterator[TItem]:
|
|
640
652
|
cached = self._cached_result
|
|
641
653
|
|
|
@@ -690,6 +702,12 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
690
702
|
| Fluent List
|
|
691
703
|
A materialized list extending UserList that yields lazy FlpIt instances for query operations.
|
|
692
704
|
"""
|
|
705
|
+
def __init__(self, source: TItem=_SENTINEL):
|
|
706
|
+
if source is None:
|
|
707
|
+
raise SourceNoneError()
|
|
708
|
+
if source is _SENTINEL:
|
|
709
|
+
source = [] # don't depend on the base class to do the right thing
|
|
710
|
+
super().__init__(source)
|
|
693
711
|
|
|
694
712
|
def add(self, item: TItem) -> None:
|
|
695
713
|
"""
|
|
@@ -802,6 +820,7 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
802
820
|
second: Iterable[TOther],
|
|
803
821
|
result_selector: Optional[Callable[[TItem, TOther], Any]] = None,
|
|
804
822
|
) -> FlpIt[Any]:
|
|
823
|
+
# pyrefly: ignore [bad-argument-type] # yeah well if overloads are just pretend
|
|
805
824
|
return FlpIt(self.data).zip(second, result_selector)
|
|
806
825
|
|
|
807
826
|
def chunk(self, size: int) -> FlpIt[FlpList[TItem]]:
|
|
@@ -839,7 +858,7 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
839
858
|
|
|
840
859
|
@_guard_empty
|
|
841
860
|
def min(self) -> TItem:
|
|
842
|
-
return builtins.min(self.data)
|
|
861
|
+
return builtins.min(self.data) # pyrefly: ignore [bad-specialization]
|
|
843
862
|
|
|
844
863
|
@_guard_empty
|
|
845
864
|
def min_by(self, key_selector: Callable[[TItem], Any]) -> TItem:
|
|
@@ -847,7 +866,7 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
847
866
|
|
|
848
867
|
@_guard_empty
|
|
849
868
|
def max(self) -> TItem:
|
|
850
|
-
return builtins.max(self.data)
|
|
869
|
+
return builtins.max(self.data) # pyrefly: ignore [bad-specialization]
|
|
851
870
|
|
|
852
871
|
@_guard_empty
|
|
853
872
|
def max_by(self, key_selector: Callable[[TItem], Any]) -> TItem:
|
|
@@ -866,7 +885,7 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
866
885
|
return FlpIt(self.data).average(selector)
|
|
867
886
|
|
|
868
887
|
# --- Overloads for the Type System ---
|
|
869
|
-
# 1. Native compatibility path (MUST be first): exact value match
|
|
888
|
+
# 1. Native Python UserList compatibility path (MUST be first): exact value match
|
|
870
889
|
@overload
|
|
871
890
|
def count(self, item: TItem) -> int: ...
|
|
872
891
|
|
|
@@ -880,8 +899,24 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
880
899
|
|
|
881
900
|
# --- The Clean Implementation ---
|
|
882
901
|
# We name the parameter 'item' to perfectly match UserList, but default it to None
|
|
902
|
+
@override
|
|
883
903
|
def count(self, item: Any = _SENTINEL) -> int:
|
|
884
|
-
"""
|
|
904
|
+
"""
|
|
905
|
+
Counts elements using Python/UserList or .NET LINQ conventions.
|
|
906
|
+
|
|
907
|
+
Supported forms:
|
|
908
|
+
|
|
909
|
+
count() -> count all elements (.NET LINQ)
|
|
910
|
+
count(predicate) -> count elements matching predicate (.NET LINQ)
|
|
911
|
+
count(value) -> count occurrences of an exact value (Python)
|
|
912
|
+
|
|
913
|
+
Note:
|
|
914
|
+
Because Python callables are also valid list values, ``count(value)``
|
|
915
|
+
is inherently ambiguous when ``value`` is callable. A callable
|
|
916
|
+
argument is interpreted as a LINQ predicate, so counting occurrences
|
|
917
|
+
of a callable object via the native ``UserList.count(value)`` behavior
|
|
918
|
+
is not distinguishable from ``count(predicate)``.
|
|
919
|
+
"""
|
|
885
920
|
# Scenario C: No argument passed (.count()) -> Return total length
|
|
886
921
|
if item is _SENTINEL:
|
|
887
922
|
return len(self.data)
|
|
@@ -910,7 +945,7 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
910
945
|
|
|
911
946
|
def last(
|
|
912
947
|
self,
|
|
913
|
-
predicate: Optional[Callable[[TItem], bool]] =
|
|
948
|
+
predicate: Optional[Callable[[TItem], bool]] = _SENTINEL,
|
|
914
949
|
) -> TItem:
|
|
915
950
|
"""
|
|
916
951
|
Returns the last element of the sequence, or the last element
|
|
@@ -924,6 +959,9 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
924
959
|
element satisfies the predicate.
|
|
925
960
|
"""
|
|
926
961
|
if predicate is None:
|
|
962
|
+
raise PredicateNoneError()
|
|
963
|
+
|
|
964
|
+
if predicate is _SENTINEL:
|
|
927
965
|
if not self.data:
|
|
928
966
|
raise EmptySequenceError()
|
|
929
967
|
return self.data[-1]
|
|
@@ -932,13 +970,14 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
932
970
|
if predicate(item):
|
|
933
971
|
return item
|
|
934
972
|
|
|
935
|
-
raise NoMatchError()
|
|
973
|
+
raise EmptySequenceError() if predicate is None else NoMatchError()
|
|
936
974
|
|
|
937
975
|
def first_or_default(
|
|
938
976
|
self, default: TResult, predicate: Optional[Callable[[TItem], bool]] = None
|
|
939
977
|
) -> Union[TItem, TResult]:
|
|
940
978
|
return FlpIt(self.data).first_or_default(default, predicate)
|
|
941
979
|
|
|
980
|
+
# pyrefly: ignore [bad-function-definition]
|
|
942
981
|
def single(self, predicate: Optional[Callable[[TItem], bool]] = _SENTINEL) -> TItem:
|
|
943
982
|
return FlpIt(self.data).single(predicate)
|
|
944
983
|
|
|
@@ -3,12 +3,11 @@ Fluent LINQ for Python — flip your iterables.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
from importlib.metadata import version
|
|
6
|
-
__version__ = version("flpit")
|
|
7
6
|
|
|
8
7
|
import builtins
|
|
9
8
|
from typing import Iterable as _Iterable, TypeVar
|
|
10
9
|
|
|
11
|
-
from
|
|
10
|
+
from flpit.core.linq import FlpList, FlpIt
|
|
12
11
|
|
|
13
12
|
TItem = TypeVar("TItem")
|
|
14
13
|
|
|
@@ -30,10 +29,6 @@ def repeat(element: TItem, count: int) -> FlpIt[TItem]:
|
|
|
30
29
|
return FlpIt(element for _ in builtins.range(count))
|
|
31
30
|
|
|
32
31
|
__all__ = [
|
|
33
|
-
"FlpIt",
|
|
34
|
-
"OrderedIt",
|
|
35
|
-
"Grouping",
|
|
36
|
-
"FlpList",
|
|
37
32
|
"it",
|
|
38
33
|
"lst",
|
|
39
34
|
"range",
|
flpit-0.1.3/PKG-INFO
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: flpit
|
|
3
|
-
Version: 0.1.3
|
|
4
|
-
Summary: Fluent LINQ for Python — flip your iterables
|
|
5
|
-
License: MIT
|
|
6
|
-
Classifier: Development Status :: 3 - Alpha
|
|
7
|
-
Classifier: Intended Audience :: Developers
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
-
Requires-Python: >=3.11
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
|
|
17
|
-
# flp
|
|
18
|
-
|
|
19
|
-
> **Fluent LINQ for Python – flip your iterables**
|
|
20
|
-
|
|
21
|
-
`flp` brings a LINQ-inspired fluent API to standard Python iterables with full static typing.
|
|
22
|
-
|
|
23
|
-
## ⚡ Key Features
|
|
24
|
-
|
|
25
|
-
* **⚡ Lazy Evaluation (`FlpIt`):** Deferred execution using Python iterators and generators.
|
|
26
|
-
* **📦 Materialized Container (`FlpList`):** Eager, mutable container backed by `collections.UserList`.
|
|
27
|
-
* **🔹 Static-First Typing:** Built for full `mypy` and `pyright` inference without plugins.
|
|
28
|
-
* **🔄 No Implicit Caching:** Query results are not cached unless explicitly documented, such as `order_by`.
|
|
29
|
-
|
|
30
|
-
## ⚙️ Installation
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
uv add flpit
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## 💡 Quick Start
|
|
37
|
-
|
|
38
|
-
```python
|
|
39
|
-
import flp
|
|
40
|
-
from flp import FlpIt, FlpList
|
|
41
|
-
|
|
42
|
-
# Deferred iterable via shorthand
|
|
43
|
-
data: FlpIt[int] = flp.it([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
44
|
-
|
|
45
|
-
# Deferred / lazy pipeline
|
|
46
|
-
query: FlpIt[int] = (
|
|
47
|
-
data
|
|
48
|
-
.where(lambda x: x % 2 == 0)
|
|
49
|
-
.select(lambda x: x * 10)
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# Materialize query results explicitly
|
|
53
|
-
result: FlpList[int] = query.to_list() # [20, 40, 60, 80, 100]
|
|
54
|
-
|
|
55
|
-
# Or start directly with an eager container
|
|
56
|
-
eager_list: FlpList[int] = flp.lst([1, 2, 3, 4])
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## 📜 License
|
|
60
|
-
|
|
61
|
-
Distributed under the MIT License. See `LICENSE` for more information.
|
flpit-0.1.3/README.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# flp
|
|
2
|
-
|
|
3
|
-
> **Fluent LINQ for Python – flip your iterables**
|
|
4
|
-
|
|
5
|
-
`flp` brings a LINQ-inspired fluent API to standard Python iterables with full static typing.
|
|
6
|
-
|
|
7
|
-
## ⚡ Key Features
|
|
8
|
-
|
|
9
|
-
* **⚡ Lazy Evaluation (`FlpIt`):** Deferred execution using Python iterators and generators.
|
|
10
|
-
* **📦 Materialized Container (`FlpList`):** Eager, mutable container backed by `collections.UserList`.
|
|
11
|
-
* **🔹 Static-First Typing:** Built for full `mypy` and `pyright` inference without plugins.
|
|
12
|
-
* **🔄 No Implicit Caching:** Query results are not cached unless explicitly documented, such as `order_by`.
|
|
13
|
-
|
|
14
|
-
## ⚙️ Installation
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
uv add flpit
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## 💡 Quick Start
|
|
21
|
-
|
|
22
|
-
```python
|
|
23
|
-
import flp
|
|
24
|
-
from flp import FlpIt, FlpList
|
|
25
|
-
|
|
26
|
-
# Deferred iterable via shorthand
|
|
27
|
-
data: FlpIt[int] = flp.it([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
28
|
-
|
|
29
|
-
# Deferred / lazy pipeline
|
|
30
|
-
query: FlpIt[int] = (
|
|
31
|
-
data
|
|
32
|
-
.where(lambda x: x % 2 == 0)
|
|
33
|
-
.select(lambda x: x * 10)
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
# Materialize query results explicitly
|
|
37
|
-
result: FlpList[int] = query.to_list() # [20, 40, 60, 80, 100]
|
|
38
|
-
|
|
39
|
-
# Or start directly with an eager container
|
|
40
|
-
eager_list: FlpList[int] = flp.lst([1, 2, 3, 4])
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## 📜 License
|
|
44
|
-
|
|
45
|
-
Distributed under the MIT License. See `LICENSE` for more information.
|
flpit-0.1.3/pyproject.toml
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "flpit"
|
|
3
|
-
version = "0.1.3"
|
|
4
|
-
description = "Fluent LINQ for Python — flip your iterables"
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.11"
|
|
7
|
-
classifiers = [
|
|
8
|
-
"Development Status :: 3 - Alpha",
|
|
9
|
-
"Intended Audience :: Developers",
|
|
10
|
-
"License :: OSI Approved :: MIT License",
|
|
11
|
-
"Programming Language :: Python :: 3",
|
|
12
|
-
"Programming Language :: Python :: 3.11",
|
|
13
|
-
"Programming Language :: Python :: 3.12",
|
|
14
|
-
"Programming Language :: Python :: 3.13",
|
|
15
|
-
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
16
|
-
]
|
|
17
|
-
dependencies = []
|
|
18
|
-
|
|
19
|
-
[project.license]
|
|
20
|
-
text = "MIT"
|
|
21
|
-
|
|
22
|
-
[dependency-groups]
|
|
23
|
-
dev = [
|
|
24
|
-
"pytest>=8.0",
|
|
25
|
-
"hypothesis>=6.168.0",
|
|
26
|
-
"pyrefly>=1.3.1",
|
|
27
|
-
"pyright>=1.1.414",
|
|
28
|
-
"pytest-benchmark>=5.3.0",
|
|
29
|
-
]
|
|
30
|
-
|
|
31
|
-
[build-system]
|
|
32
|
-
requires = ["uv_build>=0.12.10,<0.13.0"]
|
|
33
|
-
build-backend = "uv_build"
|
|
34
|
-
|
|
35
|
-
[tool.uv.build-backend]
|
|
36
|
-
module-name = "flp"
|
|
37
|
-
module-root = "src"
|
|
38
|
-
|
|
39
|
-
[tool.pyright]
|
|
40
|
-
include = ["src"]
|
|
41
|
-
exclude = [
|
|
42
|
-
"**/__pycache__",
|
|
43
|
-
"tests",
|
|
44
|
-
]
|
|
45
|
-
typeCheckingMode = "basic"
|
|
46
|
-
|
|
47
|
-
[tool.pytest.ini_options]
|
|
48
|
-
addopts = "-m 'not benchmark'"
|
|
49
|
-
markers = ["benchmark: performance benchmarks"]
|
|
File without changes
|
|
File without changes
|