Control flow constructs for python.
Project description
cflux - Control Flow Objects for Python
Features
Option Type
-
Represent optional values, freeing
Noneto be a usable value.from cflux import Option, Some, Nothing val: Option[int] = Some(42) empty: Option[int] = Nothing()
-
Pattern Matching: Destructure option.
match val: case Some(x): print(x) case Nothing(): print("Empty")
-
Functional Methods: Transform inner values.
val.map(lambda x: x * 2) # Some(84) empty.map_or(lambda x: x * 2, 0) # 0
-
Unwrapping: Extract inner value.
x: int = val.unwrap() # 42 empty.unwrap() # raises UnpackingException y: int = empty.unwrap_or(0) # 0
-
For Loop unpacking: Run code in a block if the value is some, using for loops
some: Option[str] = Some("a") nothing: Option[str] = Nothing() for value in some: print("This messag will be printed.") for value in nothing: print("This message will not be printed.")
Result Type
-
Represent success or error values.
from cflux import Result, Ok, Err success: Result[int, str] = Ok(42) failure: Result[int, str] = Err("Error message")
-
Pattern Matching: Destructure result.
match success: case Ok(x): print(x) case Err(err): print(err)
-
Functional Methods: Transform inner values.
success.map(lambda x: x * 2) # Ok(84) failure.map_err(lambda err: f"Error: {err}") # Err("Error: Error message")
-
Unwrapping: Extract inner value.
x: int = success.unwrap() # 42 failure.unwrap() # raises UnpackingException failure.unwrap_or_raise() # raises wrapped exception y: int = failure.unwrap_or(0) # 0
-
For Loop unpacking: Run code in a block if the value is ok, using for loops.
success: Result[str, int] = Ok("a") failure: Result[str, int] = Err(1) for value in success: print("This message will be printed.") for value in failure: print("This message will not be printed.")
Architecture
- All classes / functions are re-exported to the packages
__init__.py
--> There is only one place to import everything
from cflux import Option, Unit
- The actual definitions are done in private modules. F.e
_options.py
Development Environment
Dependencies
uv: Install from here: https://docs.astral.sh/uv/getting-started/installation/
Environment Setup
On every session start, call:
source env
- Adds relative directory
scripts/toPATH - Calls
scripts/setupand checks for system dependecies - Creates
.venvand syncs dependencies
On the first startup, this should setup the dev environment completely.
Otherwise you can call scripts/setup manually.
Development Tools
- Linting:
ruff - Type-checking:
basedpyright
Internal Dev Commands
Some Commands that might be useful:
style-check: Runs linter and type checkerstyle-format: Runs formattertest-all: Run the tests.- The tests contain calls to other typecheckers. This can be skipped
by passing the
--skip-typingflag. - Use the
test-all --helpcommand for more information.
- The tests contain calls to other typecheckers. This can be skipped
by passing the
Supported Type Checker
While ty is mainly used during the development, the tests also run
additional typecheckers to see how the type-inference works.
The following table contains a summary of the support.
Some type checkerr have additional information in a subsection below.
| type checker | is tested for | functionality |
|---|---|---|
ty |
✓ | Missing feature in ty for match value unpacking. |
pyrefly |
✓ | Failing the exhaustiveness checks in the match statement. |
pyright |
✓ | ✓ + Non-speced feature: Complain on Nothing in a with statement. |
basedpyright |
✓ | ? + Non-speced feature: Complain on Nothing in a with statement. |
mypy |
✗ | ? |
ty
ty =< 0.0.16currently does not infer the type of an unpacked value in a match statement
from typing import assert_type, assert_never
from cflux import Option
# snip...: some code that sets some_value
some_value: Option[int]
match some_value:
case Some(unpacked_value):
# ty will complain here
# its infered type is @Todo
assert_type(unpacked_value, int)
case Nothing():
pass
case _:
assert_never(some_value)
There is a test that will test this case. It will currently always fail.
A workaround requires the typing.cast function. Simply adding the typehint
unpacked_value: int will not work.
from typing import assert_type, assert_never, cast
from cflux import Option
# snip...: some code that sets some_value
some_value: Option[int]
match some_value:
case Some(unpacked_value):
unpacked_value = cast(int, unpacked_value)
assert_type(unpacked_value, int)
case Nothing():
pass
case _:
assert_never(some_value)
This has the drawback of not giving a typeerror when the some_value
ever switches in type.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file cflux-0.3.0.tar.gz.
File metadata
- Download URL: cflux-0.3.0.tar.gz
- Upload date:
- Size: 19.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: Hatch/1.17.0 {"ci":null,"cpu":"x86_64","distro":{"id":"noble","libc":{"lib":"glibc","version":"2.39"},"name":"Pop!_OS","version":"24.04"},"implementation":{"name":"CPython","version":"3.13.5"},"installer":{"name":"hatch","version":"1.17.0"},"openssl_version":"OpenSSL 3.0.16 11 Feb 2025","python":"3.13.5","system":{"name":"Linux","release":"7.0.11-76070011-generic"}} HTTPX2/2.5.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2dc3ce78e31f18d657e41d564bc7e127cb994a636b453129abb33bc64fe4e7c
|
|
| MD5 |
5b17c44e609f61f051f08131bc03777d
|
|
| BLAKE2b-256 |
5801defcf1beb4aec9d15971e63dbed41926cc6a8049268c6410c6c2d866abb7
|