Renamed project dirs
All checks were successful
Test before pr merge / test-lint (pull_request) Successful in 6s

This commit is contained in:
2025-10-11 09:32:13 +02:00
parent f1aaaf2a5b
commit 90ff8d466d
319 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
from .array import Array
from .enumerable import Enumerable
from .immutable_list import ImmutableList
from .immutable_set import ImmutableSet
from .list import List
from .ordered_enumerable import OrderedEnumerable
from .set import Set
__version__ = "1.0.0"

View File

@@ -0,0 +1,44 @@
from typing import Generic, Iterable, Optional
from cpl.core.typing import T
from cpl.query.list import List
from cpl.query.enumerable import Enumerable
class Array(Generic[T], List[T]):
def __init__(self, length: int, source: Optional[Iterable[T]] = None):
List.__init__(self, source)
self._length = length
@property
def length(self) -> int:
return len(self._source)
def add(self, item: T) -> None:
if self._length == self.length:
raise IndexError("Array is full")
self._source.append(item)
def extend(self, items: Iterable[T]) -> None:
if self._length == self.length:
raise IndexError("Array is full")
self._source.extend(items)
def insert(self, index: int, item: T) -> None:
if index < 0 or index > self.length:
raise IndexError("Index out of range")
self._source.insert(index, item)
def remove(self, item: T) -> None:
self._source.remove(item)
def pop(self, index: int = -1) -> T:
return self._source.pop(index)
def clear(self) -> None:
self._source.clear()
def to_enumerable(self) -> "Enumerable[T]":
from cpl.query.enumerable import Enumerable
return Enumerable(self._source)

View File

@@ -0,0 +1,213 @@
from itertools import islice, groupby, chain
from typing import Generic, Callable, Iterable, Iterator, Dict, Tuple, Optional
from cpl.core.typing import T, R
from cpl.query.typing import Predicate, K, Selector
class Enumerable(Generic[T]):
def __init__(self, source: Iterable[T]):
self._source = source
def __iter__(self) -> Iterator[T]:
return iter(self._source)
@property
def length(self) -> int:
return len(list(self._source))
def where(self, f: Predicate) -> "Enumerable[T]":
return Enumerable(x for x in self._source if f(x))
def select(self, f: Selector) -> "Enumerable[R]":
return Enumerable(f(x) for x in self._source)
def select_many(self, f: Callable[[T], Iterable[R]]) -> "Enumerable[R]":
return Enumerable(y for x in self._source for y in f(x))
def take(self, count: int) -> "Enumerable[T]":
return Enumerable(islice(self._source, count))
def skip(self, count: int) -> "Enumerable[T]":
return Enumerable(islice(self._source, count, None))
def take_while(self, f: Predicate) -> "Enumerable[T]":
def generator():
for x in self._source:
if f(x):
yield x
else:
break
return Enumerable(generator())
def skip_while(self, f: Predicate) -> "Enumerable[T]":
def generator():
it = iter(self._source)
for x in it:
if not f(x):
yield x
break
yield from it
return Enumerable(generator())
def distinct(self) -> "Enumerable[T]":
def generator():
seen = set()
for x in self._source:
if x not in seen:
seen.add(x)
yield x
return Enumerable(generator())
def union(self, other: Iterable[T]) -> "Enumerable[T]":
return Enumerable(chain(self.distinct(), Enumerable(other).distinct())).distinct()
def intersect(self, other: Iterable[T]) -> "Enumerable[T]":
other_set = set(other)
return Enumerable(x for x in self._source if x in other_set)
def except_(self, other: Iterable[T]) -> "Enumerable[T]":
other_set = set(other)
return Enumerable(x for x in self._source if x not in other_set)
def concat(self, other: Iterable[T]) -> "Enumerable[T]":
return Enumerable(chain(self._source, other))
# --- Aggregation ---
def count(self) -> int:
return sum(1 for _ in self._source)
def sum(self, f: Optional[Selector] = None) -> R:
if f:
return sum(f(x) for x in self._source)
return sum(self._source) # type: ignore
def min(self, f: Optional[Selector] = None) -> R:
if f:
return min(f(x) for x in self._source)
return min(self._source) # type: ignore
def max(self, f: Optional[Selector] = None) -> R:
if f:
return max(f(x) for x in self._source)
return max(self._source) # type: ignore
def average(self, f: Optional[Callable[[T], float]] = None) -> float:
values = list(self.select(f).to_list()) if f else list(self._source)
return sum(values) / len(values) if values else 0.0
def aggregate(self, func: Callable[[R, T], R], seed: Optional[R] = None) -> R:
it = iter(self._source)
if seed is None:
acc = next(it) # type: ignore
else:
acc = seed
for x in it:
acc = func(acc, x)
return acc
def any(self, f: Optional[Predicate] = None) -> bool:
return any(f(x) if f else x for x in self._source)
def all(self, f: Predicate) -> bool:
return all(f(x) for x in self._source)
def contains(self, value: T) -> bool:
return any(x == value for x in self._source)
def sequence_equal(self, other: Iterable[T]) -> bool:
return list(self._source) == list(other)
def group_by(self, key_f: Callable[[T], K]) -> "Enumerable[Tuple[K, List[T]]]":
def generator():
sorted_data = sorted(self._source, key=key_f)
for key, group in groupby(sorted_data, key=key_f):
yield (key, list(group))
return Enumerable(generator())
def join(
self, inner: Iterable[R], outer_key: Callable[[T], K], inner_key: Callable[[R], K], result: Callable[[T, R], R]
) -> "Enumerable[R]":
def generator():
lookup: Dict[K, List[R]] = {}
for i in inner:
k = inner_key(i)
lookup.setdefault(k, []).append(i)
for o in self._source:
k = outer_key(o)
if k in lookup:
for i in lookup[k]:
yield result(o, i)
return Enumerable(generator())
def first(self, f: Optional[Predicate] = None) -> T:
if f:
for x in self._source:
if f(x):
return x
raise ValueError("No matching element")
return next(iter(self._source))
def first_or_default(self, default: Optional[T] = None) -> Optional[T]:
return next(iter(self._source), default)
def last(self) -> T:
return list(self._source)[-1]
def single(self, f: Optional[Predicate] = None) -> T:
items = [x for x in self._source if f(x)] if f else list(self._source)
if len(items) != 1:
raise ValueError("Sequence does not contain exactly one element")
return items[0]
def to_list(self) -> "List[T]":
from cpl.query.list import List
return List(self)
def to_set(self) -> "Set[T]":
from cpl.query.set import Set
return Set(self)
def to_dict(self, key_f: Callable[[T], K], value_f: Selector) -> Dict[K, R]:
return {key_f(x): value_f(x) for x in self._source}
def cast(self, t: Selector) -> "Enumerable[R]":
return Enumerable(t(x) for x in self._source)
def of_type(self, t: type) -> "Enumerable[T]":
return Enumerable(x for x in self._source if isinstance(x, t))
def reverse(self) -> "Enumerable[T]":
return Enumerable(reversed(list(self._source)))
def zip(self, other: Iterable[R]) -> "Enumerable[Tuple[T, R]]":
return Enumerable(zip(self._source, other))
def order_by(self, key_selector: Callable[[T], K]) -> "OrderedEnumerable[T]":
from cpl.query.ordered_enumerable import OrderedEnumerable
return OrderedEnumerable(self._source, [(key_selector, False)])
def order_by_descending(self, key_selector: Callable[[T], K]) -> "OrderedEnumerable[T]":
from cpl.query.ordered_enumerable import OrderedEnumerable
return OrderedEnumerable(self._source, [(key_selector, True)])
@staticmethod
def range(start: int, count: int) -> "Enumerable[int]":
return Enumerable(range(start, start + count))
@staticmethod
def repeat(value: T, count: int) -> "Enumerable[T]":
return Enumerable(value for _ in range(count))
@staticmethod
def empty() -> "Enumerable[T]":
return Enumerable([])

View File

@@ -0,0 +1,65 @@
from typing import Generic, Iterable, Iterator, Optional
from cpl.core.typing import T
from cpl.query.enumerable import Enumerable
class ImmutableList(Generic[T], Enumerable[T]):
def __init__(self, source: Optional[Iterable[T]] = None):
Enumerable.__init__(self, [])
if source is None:
source = []
elif not isinstance(source, list):
source = list(source)
self.__source = source
@property
def _source(self) -> list[T]:
return self.__source
@_source.setter
def _source(self, value: list[T]) -> None:
self.__source = value
def __iter__(self) -> Iterator[T]:
return iter(self._source)
def __len__(self) -> int:
return len(self._source)
def __getitem__(self, index: int) -> T:
return self._source[index]
def __contains__(self, item: T) -> bool:
return item in self._source
def __repr__(self) -> str:
return f"List({self._source!r})"
@property
def length(self) -> int:
return len(self._source)
def add(self, item: T) -> None:
self._source.append(item)
def extend(self, items: Iterable[T]) -> None:
self._source.extend(items)
def insert(self, index: int, item: T) -> None:
self._source.insert(index, item)
def remove(self, item: T) -> None:
self._source.remove(item)
def pop(self, index: int = -1) -> T:
return self._source.pop(index)
def clear(self) -> None:
self._source.clear()
def to_enumerable(self) -> "Enumerable[T]":
from cpl.query.enumerable import Enumerable
return Enumerable(self._source)

View File

@@ -0,0 +1,47 @@
from typing import Generic, Iterable, Iterator, Optional
from cpl.core.typing import T
from cpl.query.enumerable import Enumerable
class ImmutableSet(Generic[T], Enumerable[T]):
def __init__(self, source: Optional[Iterable[T]] = None):
Enumerable.__init__(self, [])
if source is None:
source = set()
elif not isinstance(source, set):
source = set(source)
self.__source = source
@property
def _source(self) -> set[T]:
return self.__source
@_source.setter
def _source(self, value: set[T]) -> None:
if not isinstance(value, set):
value = set(value)
self.__source = value
def __iter__(self) -> Iterator[T]:
return iter(self._source)
def __len__(self) -> int:
return len(self._source)
def __contains__(self, item: T) -> bool:
return item in self._source
def __repr__(self) -> str:
return f"Set({self._source!r})"
@property
def length(self) -> int:
return len(self._source)
def to_enumerable(self) -> "Enumerable[T]":
from cpl.query.enumerable import Enumerable
return Enumerable(self._source)

View File

@@ -0,0 +1,36 @@
from typing import Generic, Iterable, Optional
from cpl.core.typing import T
from cpl.query.immutable_list import ImmutableList
from cpl.query.enumerable import Enumerable
class List(Generic[T], ImmutableList[T]):
def __init__(self, source: Optional[Iterable[T]] = None):
ImmutableList.__init__(self, source)
def __setitem__(self, index: int, value: T) -> None:
self._source[index] = value
def add(self, item: T) -> None:
self._source.append(item)
def extend(self, items: Iterable[T]) -> None:
self._source.extend(items)
def insert(self, index: int, item: T) -> None:
self._source.insert(index, item)
def remove(self, item: T) -> None:
self._source.remove(item)
def pop(self, index: int = -1) -> T:
return self._source.pop(index)
def clear(self) -> None:
self._source.clear()
def to_enumerable(self) -> "Enumerable[T]":
from cpl.query.enumerable import Enumerable
return Enumerable(self._source)

View File

@@ -0,0 +1,40 @@
from typing import Callable, List, Generic, Iterator
from cpl.core.typing import T
from cpl.query.enumerable import Enumerable
from cpl.query.typing import K
class OrderedEnumerable(Enumerable[T]):
def __init__(self, source, key_selectors: List[tuple[Callable[[T], K], bool]]):
Enumerable.__init__(self, source)
self._key_selectors = key_selectors
def __iter__(self) -> Iterator[T]:
def composite_key(x):
keys = []
for selector, descending in self._key_selectors:
k = selector(x)
keys.append((k, not descending))
return tuple(k if asc else _DescendingWrapper(k) for k, asc in keys)
return iter(sorted(self._source, key=composite_key))
def then_by(self, key_selector: Callable[[T], K]) -> "OrderedEnumerable[T]":
return OrderedEnumerable(self._source, self._key_selectors + [(key_selector, False)])
def then_by_descending(self, key_selector: Callable[[T], K]) -> "OrderedEnumerable[T]":
return OrderedEnumerable(self._source, self._key_selectors + [(key_selector, True)])
class _DescendingWrapper:
def __init__(self, value):
self.value = value
def __lt__(self, other):
return self.value > other.value
def __gt__(self, other):
return self.value < other.value
def __eq__(self, other):
return self.value == other.value

View File

@@ -0,0 +1 @@
from .sequence import Sequence

View File

@@ -0,0 +1,59 @@
from typing import Protocol, Callable, Dict, Tuple, Optional, Iterable
from cpl.core.typing import T, R
from cpl.query.list import List
from cpl.query.typing import Selector, Predicate, K
class Sequence(Protocol[T]):
def select(self, f: Selector) -> "Sequence[R]": ...
def where(self, f: Predicate) -> "Sequence[T]": ...
def select_many(self, f: Callable[[T], Iterable[R]]) -> "Sequence[R]": ...
def take(self, count: int) -> "Sequence[T]": ...
def skip(self, count: int) -> "Sequence[T]": ...
def take_while(self, f: Predicate) -> "Sequence[T]": ...
def skip_while(self, f: Predicate) -> "Sequence[T]": ...
def distinct(self) -> "Sequence[T]": ...
def union(self, other: Iterable[T]) -> "Sequence[T]": ...
def intersect(self, other: Iterable[T]) -> "Sequence[T]": ...
def except_(self, other: Iterable[T]) -> "Sequence[T]": ...
def concat(self, other: Iterable[T]) -> "Sequence[T]": ...
def count(self) -> int: ...
def sum(self, f: Optional[Selector] = None) -> R: ...
def min(self, f: Optional[Selector] = None) -> R: ...
def max(self, f: Optional[Selector] = None) -> R: ...
def average(self, f: Optional[Callable[[T], float]] = None) -> float: ...
def aggregate(self, func: Callable[[R, T], R], seed: Optional[R] = None) -> R: ...
def any(self, f: Optional[Predicate] = None) -> bool: ...
def all(self, f: Predicate) -> bool: ...
def contains(self, value: T) -> bool: ...
def sequence_equal(self, other: Iterable[T]) -> bool: ...
def group_by(self, key_f: Callable[[T], K]) -> "Sequence[Tuple[K, List[T]]]": ...
def join(
self, inner: Iterable[R], outer_key: Callable[[T], K], inner_key: Callable[[R], K], result: Callable[[T, R], R]
) -> "Sequence[R]": ...
def first(self, f: Optional[Predicate] = None) -> T: ...
def first_or_default(self, default: Optional[T] = None) -> Optional[T]: ...
def last(self) -> T: ...
def single(self, f: Optional[Predicate] = None) -> T: ...
def to_list(self) -> List[T]: ...
def to_dict(self, key_f: Callable[[T], K], value_f: Selector) -> Dict[K, R]: ...
def cast(self, t: Selector) -> "Sequence[R]": ...
def of_type(self, t: type) -> "Sequence[T]": ...
def reverse(self) -> "Sequence[T]": ...
def zip(self, other: Iterable[R]) -> "Sequence[Tuple[T, R]]": ...
@staticmethod
def range(start: int, count: int) -> "Sequence[int]": ...
@staticmethod
def repeat(value: T, count: int) -> "Sequence[T]": ...
@staticmethod
def empty() -> "Sequence[T]": ...

View File

@@ -0,0 +1,28 @@
from typing import Generic, Iterable, Optional
from cpl.core.typing import T
from cpl.query.immutable_set import ImmutableSet
from cpl.query.enumerable import Enumerable
class Set(Generic[T], ImmutableSet[T]):
def __init__(self, source: Optional[Iterable[T]] = None):
ImmutableSet.__init__(self, source)
@property
def length(self) -> int:
return len(self._source)
def add(self, item: T) -> None:
self._source.add(item)
def remove(self, item: T) -> None:
self._source.remove(item)
def clear(self) -> None:
self._source.clear()
def to_enumerable(self) -> "Enumerable[T]":
from cpl.query.enumerable import Enumerable
return Enumerable(self._source)

View File

@@ -0,0 +1,8 @@
from typing import Callable, TypeVar
from cpl.core.typing import T, R
K = TypeVar("K")
Predicate = Callable[[T], bool]
Selector = Callable[[T], R]

30
src/query/pyproject.toml Normal file
View File

@@ -0,0 +1,30 @@
[build-system]
requires = ["setuptools>=70.1.0", "wheel>=0.43.0"]
build-backend = "setuptools.build_meta"
[project]
name = "cpl-query"
version = "2024.7.0"
description = "CPL query"
readme ="CPL query package"
requires-python = ">=3.12"
license = { text = "MIT" }
authors = [
{ name = "Sven Heidemann", email = "sven.heidemann@sh-edraft.de" }
]
keywords = ["cpl", "query", "backend", "shared", "library"]
dynamic = ["dependencies", "optional-dependencies"]
[project.urls]
Homepage = "https://www.sh-edraft.de"
[tool.setuptools.packages.find]
where = ["."]
include = ["cpl*"]
[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }
optional-dependencies.dev = { file = ["requirements.dev.txt"] }

View File

@@ -0,0 +1 @@
black==25.1.0

View File