Skip to main content

lupl 👾😺

tests Coverage Status PyPI version License: GPL v3 Ruff uv

A collection of potentially generally useful Python utilities.

Installation

lupl is a PEP-621-compliant package and available on PyPI.

Usage

ComposeRouter

The ComposeRouter class allows to route attributes access for registered methods through a functional pipeline constructed from components. The pipeline is only triggered if a registered method is accessed via the ComposeRouter namespace.

from lupl import ComposeRouter

class Foo:
	route = ComposeRouter(lambda x: x + 1, lambda y: y * 2)

	@route.register
	def method(self, x, y):
		return x * y

	foo = Foo()

print(foo.method(2, 3))           # 6
print(foo.route.method(2, 3))     # 13

By default, composition in ComposeRouter is right-associative.

Associativity can be controlled by setting the left_associative: bool kwarg either when creating the ComposeRouter instance or when calling it.

class Bar:
	route = ComposeRouter(lambda x: x + 1, lambda y: y * 2, left_associative=True)

	@route.register
	def method(self, x, y):
		return x * y

bar = Bar()

print(bar.method(2, 3))  # 6
print(bar.route.method(2, 3))  # 14
print(bar.route(left_associative=False).method(2, 3))  # 13

Chunk Iterator

The ichunk generator implements a simple chunk iterator that allows to lazily slice an Iterator into sub-iterators.

from collections.abc import Iterator
from lupl import ichunk

iterator: Iterator[int] = iter(range(10))
chunks: Iterator[Iterator[int]] = ichunk(iterator, size=3)

materialized = [tuple(chunk) for chunk in chunks]
print(materialized)  # [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9,)]

Pydantic Tools

CurryModel

CurryModel allows to sequentially initialize (curry) a Pydantic model.

The constructor takes abitrary kwargs and validates them against the respective model fields; calling a CurryModel object without kwargs will instantiate and return the respective curried model based on an internal cache.

from lupl import CurryModel

class MyModel(BaseModel):
	x: str
	y: int
	z: tuple[str, int]


curried_model = CurryModel(MyModel)

# kwargs calls return aggregated CurryModel instances
curried_model(x="1")
curried_model(y=2)
curried_model(z=("3", 4))

# call without kwargs initializes the model
model_instance: MyModel = curried_model()

CurryModel instances are recursive, so it is also possible to do this:

curried_model = CurryModel(MyModel)(x="1")(y=2)(z=("3", 4))

model_instance: MyModel = curried_model()

Currying turns a function of arity n into at most n functions of arity 1 and at least 1 function of arity n (and everything in between), so you can also do e.g. this:

curried_model = CurryModel(MyModel)

model_instance: MyModel = curried_model(x="1", y=2)(z=("3", 4))()

FlatInitModel

The FlatInitModel constructor allows to instantiate a potentially deeply nested Pydantic model from flat kwargs.

from lupl import FlatInitModel
from pydantic import BaseModel

class DeeplyNestedModel(BaseModel):
	z: int

class NestedModel(BaseModel):
	y: int
	deeply_nested: DeeplyNestedModel

class Model(BaseModel):
	x: int
	nested: NestedModel

constructor = FlatInitModel(model=Model)

instance: Model = constructor(x=1, y=2, z=3)
instance.model_dump()  # {'x': 1, 'nested': {'y': 2, 'deeply_nested': {'z': 3}}}

FlatInitModel also handles model union types by processing the first model type of the union.

A common use case for model union types is e.g. to assign a default value to a model union typed field in case a nested model instance does not meet certain criteria, i.e. fails a predicate.

The model_bool parameter in lupl.ConfigDict allows to specify the condition for model truthiness - if the existential condition of a model is met, the model instance gets assigned to the model field, else the constructor falls back to the default value.

The default condition for model truthiness is that any model field must be truthy for the model to be considered truthy.

The model_bool parameter takes either

  • a callable object of arity 1 that receives the model instance at runtime,
  • a str denoting a field of the model that must be truthy in order for the model to be truthy
  • a set[str] denoting fields of the model, all of which must be truthy for the model to be truthy.

The following example defines the truth condition for DeeplyNestedModel to be gt3. NestedModel defines a model union type with a default value - if the model_bool predicate fails, the constructor falls back to the default:

from lupl import ConfigDict, FlatInitModel
from pydantic import BaseModel

class DeeplyNestedModel(BaseModel):
	model_config = ConfigDict(model_bool=lambda model: model.z > 3)

	z: int

class NestedModel(BaseModel):
	y: int
	deeply_nested: DeeplyNestedModel | str = "default"

class Model(BaseModel):
	x: int
	nested: NestedModel

constructor = FlatInitModel(model=Model)

instance: Model = constructor(x=1, y=2, z=3)
instance.model_dump()  # {'x': 1, 'nested': {'y': 2, 'deeply_nested': 'default'}}

If the existential condition of the model is met, the model instance gets assigned:

instance: Model = constructor(x=1, y=2, z=4)
instance.model_dump()  # {'x': 1, 'nested': {'y': 2, 'deeply_nested': {'z': 4}}}

Metadata

Release files for lupl 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lupl 0.1.3
File Size Uploaded
lupl-0.1.3.tar.gz 28.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lupl 0.1.3
File Interpreter ABI Platform
lupl-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 37.2 kB

Release files / lupl-0.1.3.tar.gz

Download URL lupl-0.1.3.tar.gz
Size 28.6 kB
Tags Source
SHA-256 checksum
How to use checksums
594589316c0d78597d7cb8f55aff1c082792d889b715058c3d62043ae0b24c5b
BLAKE2b-256 checksum
How to use checksums
fc91e45f7cff1f3081e0094cd679c08b8e3df4ef8af4d6e4a23a5e7a0443d9fc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.5.22

Release files / lupl-0.1.3-py3-none-any.whl

Download URL lupl-0.1.3-py3-none-any.whl
Size 8.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b08d52725c7cdb9e1a7be55eabefcaf43f6ecb9c82c226d01840a3730780d207
BLAKE2b-256 checksum
How to use checksums
3cdfd1d823b95298735be6ec4c893a31fff62cff72a1b338ff3f6f859a3bec32
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.5.22

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page