# kotresult
[](https://pypi.org/project/kotresult/)
[](https://pypi.org/project/kotresult/)
[](https://pypi.org/project/kotresult/)
[](https://github.com/lalcs/kotresult/graphs/contributors)
[](https://pypistats.org/packages/kotresult)

A Python implementation of the Result monad pattern, inspired by Kotlin's Result class. This library provides a way to
handle operations that might succeed or fail without using exceptions for control flow.
## Installation
You can install the package via pip:
```bash
pip install kotresult
```
## Usage
### Result Class
The `Result` class represents an operation that might succeed or fail. It can contain either a successful value or an
exception.
```python
from kotresult import Result
# Create a success result
success = Result.success("Hello, World!")
print(success.is_success) # True
print(success.get_or_none()) # "Hello, World!"
# Create a failure result
failure = Result.failure(ValueError("Something went wrong"))
print(failure.is_failure) # True
print(failure.exception_or_none()) # ValueError("Something went wrong")
```
#### Getting Values Safely
```python
# Get the value or a default
value = success.get_or_default("Default value") # "Hello, World!"
value = failure.get_or_default("Default value") # "Default value"
# Get the value or throw the exception
try:
value = failure.get_or_throw() # Raises ValueError("Something went wrong")
except ValueError as e:
print(f"Caught exception: {e}")
# Throw on failure
success.throw_on_failure() # Does nothing
try:
failure.throw_on_failure() # Raises ValueError("Something went wrong")
except ValueError as e:
print(f"Caught exception: {e}")
```
### run_catching Function
The `run_catching` function executes a function and returns a `Result` object containing either the return value or any
exception that was raised.
```python
from kotresult import run_catching
# With a function that succeeds
def add(a, b):
return a + b
result = run_catching(add, 2, 3)
print(result.is_success) # True
print(result.get_or_none()) # 5
# With a function that fails
def divide(a, b):
return a / b
result = run_catching(divide, 1, 0) # ZeroDivisionError
print(result.is_failure) # True
print(type(result.exception_or_none())) # <class 'ZeroDivisionError'>
# With keyword arguments
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
result = run_catching(greet, name="World", greeting="Hi")
print(result.get_or_none()) # "Hi, World!"
```
### Using Result as a Function Return
You can use the `Result` class as a return type for your functions to handle operations that might fail:
```python
from kotresult import Result
# Function that returns a Result
def parse_int(value: str) -> Result[int]:
try:
return Result.success(int(value))
except ValueError as e:
return Result.failure(e)
# Using the function
result = parse_int("42")
if result.is_success:
print(f"Parsed value: {result.get_or_none()}") # Parsed value: 42
else:
print(f"Failed to parse: {result.exception_or_none()}")
# With a value that can't be parsed
result = parse_int("not_a_number")
if result.is_success:
print(f"Parsed value: {result.get_or_none()}")
else:
print(f"Failed to parse: {result.exception_or_none()}") # Failed to parse: ValueError("invalid literal for int() with base 10: 'not_a_number'")
# You can also chain operations that return Result
def double_parsed_int(value: str) -> Result[int]:
result = parse_int(value)
if result.is_success:
return Result.success(result.get_or_none() * 2)
return result # Return the failure result as is
result = double_parsed_int("21")
print(result.get_or_default(0)) # 42
result = double_parsed_int("not_a_number")
print(result.get_or_default(0)) # 0
# Using on_success and on_failure for handling results
def process_result(value: str):
parse_int(value).on_success(
lambda x: print(f"Successfully parsed {value} to {x}")
).on_failure(
lambda e: print(f"Failed to parse {value}: {e}")
)
process_result("42") # Successfully parsed 42 to 42
process_result("not_a_number") # Failed to parse not_a_number: invalid literal for int() with base 10: 'not_a_number'
```
### Advanced Methods
#### Transforming Results with map and mapCatching
```python
from kotresult import Result, run_catching
# map(): Transform success values
result = Result.success(5)
squared = result.map(lambda x: x ** 2)
print(squared.get_or_none()) # 25
# map() on failure returns the same failure
failure = Result.failure(ValueError("error"))
mapped = failure.map(lambda x: x * 2)
print(mapped.is_failure) # True
# mapCatching(): Transform values and catch exceptions
def risky_transform(x):
if x > 10:
raise ValueError("Too large")
return x * 2
result1 = Result.success(5).map_catching(risky_transform)
print(result1.get_or_none()) # 10
result2 = Result.success(15).map_catching(risky_transform)
print(result2.is_failure) # True
print(type(result2.exception_or_none())) # <class 'ValueError'>
```
#### Recovering from Failures
```python
# recover(): Transform failures to successes
failure = Result.failure(ValueError("error"))
recovered = failure.recover(lambda e: "Default value")
print(recovered.get_or_none()) # "Default value"
# recover() on success returns the same success
success = Result.success(42)
recovered = success.recover(lambda e: 0)
print(recovered.get_or_none()) # 42
# recoverCatching(): Recover with exception handling
def risky_recovery(e):
if "critical" in str(e):
raise RuntimeError("Cannot recover")
return "Recovered"
result1 = Result.failure(ValueError("error")).recover_catching(risky_recovery)
print(result1.get_or_none()) # "Recovered"
result2 = Result.failure(ValueError("critical error")).recover_catching(risky_recovery)
print(result2.is_failure) # True
print(type(result2.exception_or_none())) # <class 'RuntimeError'>
```
#### Folding Results
```python
# fold(): Handle both success and failure cases with one call
def handle_result(value: str) -> str:
return parse_int(value).fold(
on_success=lambda x: f"The number is {x}",
on_failure=lambda e: f"Invalid input: {e}"
)
print(handle_result("42")) # "The number is 42"
print(handle_result("abc")) # "Invalid input: invalid literal for int() with base 10: 'abc'"
# getOrElse(): Get value or compute alternative from exception
result = parse_int("not_a_number")
value = result.get_or_else(lambda e: len(str(e)))
print(value) # Length of the error message
```
### Chaining Operations
```python
# Chain multiple transformations
result = (
run_catching(int, "42")
.map(lambda x: x * 2)
.map(lambda x: x + 10)
.map_catching(lambda x: 100 / x)
)
print(result.get_or_none()) # 1.0526315789473684
# Complex error handling chain
def process_data(data: str) -> str:
return (
run_catching(int, data)
.map(lambda x: x * 2)
.recover_catching(lambda e: 0) # Default to 0 on parse error
.map(lambda x: f"Result: {x}")
.get_or_else(lambda e: "Processing failed")
)
print(process_data("21")) # "Result: 42"
print(process_data("abc")) # "Result: 0"
```
### run_catching_with Function
The `run_catching_with` function executes a function with a receiver object as the first argument. This is similar to Kotlin's extension function version of runCatching.
**Note**: In Kotlin, there are two versions of `runCatching`:
1. Regular function: `runCatching { ... }`
2. Extension function: `someObject.runCatching { ... }`
Since Python doesn't have extension functions, we implement the extension function version as a separate function called `run_catching_with`, where the receiver object is explicitly passed as the first parameter.
```python
from kotresult import run_catching_with
# Basic usage with string operations
# Kotlin: "hello".runCatching { toUpperCase() }
# Python equivalent:
result = run_catching_with("hello", str.upper)
print(result.get_or_null()) # "HELLO"
# With a custom function
def add_prefix(text, prefix):
return prefix + text
result = run_catching_with("world", add_prefix, "Hello, ")
print(result.get_or_null()) # "Hello, world"
# With lambda functions
result = run_catching_with(42, lambda x: x * 2)
print(result.get_or_null()) # 84
# Type conversion with error handling
result = run_catching_with("123", int)
print(result.get_or_null()) # 123
result = run_catching_with("not a number", int)
print(result.is_failure) # True
print(type(result.exception_or_null())) # <class 'ValueError'>
# Chaining operations with receiver
def process_text(text):
return text.strip().lower().replace(" ", "_")
result = run_catching_with(" Hello World ", process_text)
print(result.get_or_null()) # "hello_world"
```
## API Reference
### Result Class
#### Static Methods
- `Result.success(value)`: Creates a success result with the given value
- `Result.failure(exception)`: Creates a failure result with the given exception
#### Properties
- `is_success`: Returns `True` if the result is a success, `False` otherwise
- `is_failure`: Returns `True` if the result is a failure, `False` otherwise
#### Methods
- `get_or_null()`: Returns the value if success, `None` if failure
- `get_or_none()`: Alias for `get_or_null()` for Python naming convention
- `exception_or_null()`: Returns the exception if failure, `None` if success
- `exception_or_none()`: Alias for `exception_or_null()` for Python naming convention
- `to_string()`: Returns a string representation of the result
- `get_or_default(default_value)`: Returns the value if success, the default value if failure
- `get_or_throw()`: Returns the value if success, throws the exception if failure
- `throw_on_failure()`: Throws the exception if failure, does nothing if success
- `on_success(callback)`: Executes the callback with the value if success, returns the Result object for chaining
- `on_failure(callback)`: Executes the callback with the exception if failure, returns the Result object for chaining
- `map(transform)`: Transforms the success value with the given function, returns a new Result
- `map_catching(transform)`: Like map(), but catches exceptions thrown by the transform function
- `recover(transform)`: Transforms the failure exception to a success value, returns a new Result
- `recover_catching(transform)`: Like recover(), but catches exceptions thrown by the transform function
- `fold(on_success, on_failure)`: Applies the appropriate function based on success/failure and returns the result directly (not wrapped in Result)
- `get_or_else(on_failure)`: Returns the success value or computes an alternative value from the exception
### run_catching Function
- `run_catching(func, *args, **kwargs)`: Executes the function with the given arguments and returns a `Result` object
### run_catching_with Function
- `run_catching_with(receiver, func, *args, **kwargs)`: Executes the function with a receiver object as the first argument and returns a `Result` object
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
Raw data
{
"_id": null,
"home_page": "https://github.com/Lalcs/kotresult",
"name": "kotresult",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "result, monad, kotlin, error-handling, exception",
"author": "Vatis",
"author_email": "vatis@lalcs.com",
"download_url": "https://files.pythonhosted.org/packages/23/8f/f6497252ca91f4776fb0b395440266913fa4517ed7b62dca8ea6a9cb57ef/kotresult-1.0.0.tar.gz",
"platform": "POSIX",
"description": "# kotresult\n\n[](https://pypi.org/project/kotresult/)\n[](https://pypi.org/project/kotresult/)\n[](https://pypi.org/project/kotresult/)\n[](https://github.com/lalcs/kotresult/graphs/contributors)\n[](https://pypistats.org/packages/kotresult)\n\n\nA Python implementation of the Result monad pattern, inspired by Kotlin's Result class. This library provides a way to\nhandle operations that might succeed or fail without using exceptions for control flow.\n\n## Installation\n\nYou can install the package via pip:\n\n```bash\npip install kotresult\n```\n\n## Usage\n\n### Result Class\n\nThe `Result` class represents an operation that might succeed or fail. It can contain either a successful value or an\nexception.\n\n```python\nfrom kotresult import Result\n\n# Create a success result\nsuccess = Result.success(\"Hello, World!\")\nprint(success.is_success) # True\nprint(success.get_or_none()) # \"Hello, World!\"\n\n# Create a failure result\nfailure = Result.failure(ValueError(\"Something went wrong\"))\nprint(failure.is_failure) # True\nprint(failure.exception_or_none()) # ValueError(\"Something went wrong\")\n```\n\n#### Getting Values Safely\n\n```python\n# Get the value or a default\nvalue = success.get_or_default(\"Default value\") # \"Hello, World!\"\nvalue = failure.get_or_default(\"Default value\") # \"Default value\"\n\n# Get the value or throw the exception\ntry:\n value = failure.get_or_throw() # Raises ValueError(\"Something went wrong\")\nexcept ValueError as e:\n print(f\"Caught exception: {e}\")\n\n# Throw on failure\nsuccess.throw_on_failure() # Does nothing\ntry:\n failure.throw_on_failure() # Raises ValueError(\"Something went wrong\")\nexcept ValueError as e:\n print(f\"Caught exception: {e}\")\n```\n\n### run_catching Function\n\nThe `run_catching` function executes a function and returns a `Result` object containing either the return value or any\nexception that was raised.\n\n```python\nfrom kotresult import run_catching\n\n\n# With a function that succeeds\ndef add(a, b):\n return a + b\n\n\nresult = run_catching(add, 2, 3)\nprint(result.is_success) # True\nprint(result.get_or_none()) # 5\n\n\n# With a function that fails\ndef divide(a, b):\n return a / b\n\n\nresult = run_catching(divide, 1, 0) # ZeroDivisionError\nprint(result.is_failure) # True\nprint(type(result.exception_or_none())) # <class 'ZeroDivisionError'>\n\n\n# With keyword arguments\ndef greet(name, greeting=\"Hello\"):\n return f\"{greeting}, {name}!\"\n\n\nresult = run_catching(greet, name=\"World\", greeting=\"Hi\")\nprint(result.get_or_none()) # \"Hi, World!\"\n```\n\n### Using Result as a Function Return\n\nYou can use the `Result` class as a return type for your functions to handle operations that might fail:\n\n```python\nfrom kotresult import Result\n\n\n# Function that returns a Result\ndef parse_int(value: str) -> Result[int]:\n try:\n return Result.success(int(value))\n except ValueError as e:\n return Result.failure(e)\n\n\n# Using the function\nresult = parse_int(\"42\")\nif result.is_success:\n print(f\"Parsed value: {result.get_or_none()}\") # Parsed value: 42\nelse:\n print(f\"Failed to parse: {result.exception_or_none()}\")\n\n# With a value that can't be parsed\nresult = parse_int(\"not_a_number\")\nif result.is_success:\n print(f\"Parsed value: {result.get_or_none()}\")\nelse:\n print(f\"Failed to parse: {result.exception_or_none()}\") # Failed to parse: ValueError(\"invalid literal for int() with base 10: 'not_a_number'\")\n\n# You can also chain operations that return Result\ndef double_parsed_int(value: str) -> Result[int]:\n result = parse_int(value)\n if result.is_success:\n return Result.success(result.get_or_none() * 2)\n return result # Return the failure result as is\n\n\nresult = double_parsed_int(\"21\")\nprint(result.get_or_default(0)) # 42\n\nresult = double_parsed_int(\"not_a_number\")\nprint(result.get_or_default(0)) # 0\n\n# Using on_success and on_failure for handling results\ndef process_result(value: str):\n parse_int(value).on_success(\n lambda x: print(f\"Successfully parsed {value} to {x}\")\n ).on_failure(\n lambda e: print(f\"Failed to parse {value}: {e}\")\n )\n\nprocess_result(\"42\") # Successfully parsed 42 to 42\nprocess_result(\"not_a_number\") # Failed to parse not_a_number: invalid literal for int() with base 10: 'not_a_number'\n```\n\n### Advanced Methods\n\n#### Transforming Results with map and mapCatching\n\n```python\nfrom kotresult import Result, run_catching\n\n# map(): Transform success values\nresult = Result.success(5)\nsquared = result.map(lambda x: x ** 2)\nprint(squared.get_or_none()) # 25\n\n# map() on failure returns the same failure\nfailure = Result.failure(ValueError(\"error\"))\nmapped = failure.map(lambda x: x * 2)\nprint(mapped.is_failure) # True\n\n# mapCatching(): Transform values and catch exceptions\ndef risky_transform(x):\n if x > 10:\n raise ValueError(\"Too large\")\n return x * 2\n\nresult1 = Result.success(5).map_catching(risky_transform)\nprint(result1.get_or_none()) # 10\n\nresult2 = Result.success(15).map_catching(risky_transform)\nprint(result2.is_failure) # True\nprint(type(result2.exception_or_none())) # <class 'ValueError'>\n```\n\n#### Recovering from Failures\n\n```python\n# recover(): Transform failures to successes\nfailure = Result.failure(ValueError(\"error\"))\nrecovered = failure.recover(lambda e: \"Default value\")\nprint(recovered.get_or_none()) # \"Default value\"\n\n# recover() on success returns the same success\nsuccess = Result.success(42)\nrecovered = success.recover(lambda e: 0)\nprint(recovered.get_or_none()) # 42\n\n# recoverCatching(): Recover with exception handling\ndef risky_recovery(e):\n if \"critical\" in str(e):\n raise RuntimeError(\"Cannot recover\")\n return \"Recovered\"\n\nresult1 = Result.failure(ValueError(\"error\")).recover_catching(risky_recovery)\nprint(result1.get_or_none()) # \"Recovered\"\n\nresult2 = Result.failure(ValueError(\"critical error\")).recover_catching(risky_recovery)\nprint(result2.is_failure) # True\nprint(type(result2.exception_or_none())) # <class 'RuntimeError'>\n```\n\n#### Folding Results\n\n```python\n# fold(): Handle both success and failure cases with one call\ndef handle_result(value: str) -> str:\n return parse_int(value).fold(\n on_success=lambda x: f\"The number is {x}\",\n on_failure=lambda e: f\"Invalid input: {e}\"\n )\n\nprint(handle_result(\"42\")) # \"The number is 42\"\nprint(handle_result(\"abc\")) # \"Invalid input: invalid literal for int() with base 10: 'abc'\"\n\n# getOrElse(): Get value or compute alternative from exception\nresult = parse_int(\"not_a_number\")\nvalue = result.get_or_else(lambda e: len(str(e)))\nprint(value) # Length of the error message\n```\n\n### Chaining Operations\n\n```python\n# Chain multiple transformations\nresult = (\n run_catching(int, \"42\")\n .map(lambda x: x * 2)\n .map(lambda x: x + 10)\n .map_catching(lambda x: 100 / x)\n)\nprint(result.get_or_none()) # 1.0526315789473684\n\n# Complex error handling chain\ndef process_data(data: str) -> str:\n return (\n run_catching(int, data)\n .map(lambda x: x * 2)\n .recover_catching(lambda e: 0) # Default to 0 on parse error\n .map(lambda x: f\"Result: {x}\")\n .get_or_else(lambda e: \"Processing failed\")\n )\n\nprint(process_data(\"21\")) # \"Result: 42\"\nprint(process_data(\"abc\")) # \"Result: 0\"\n```\n\n### run_catching_with Function\n\nThe `run_catching_with` function executes a function with a receiver object as the first argument. This is similar to Kotlin's extension function version of runCatching.\n\n**Note**: In Kotlin, there are two versions of `runCatching`:\n1. Regular function: `runCatching { ... }`\n2. Extension function: `someObject.runCatching { ... }`\n\nSince Python doesn't have extension functions, we implement the extension function version as a separate function called `run_catching_with`, where the receiver object is explicitly passed as the first parameter.\n\n```python\nfrom kotresult import run_catching_with\n\n# Basic usage with string operations\n# Kotlin: \"hello\".runCatching { toUpperCase() }\n# Python equivalent:\nresult = run_catching_with(\"hello\", str.upper)\nprint(result.get_or_null()) # \"HELLO\"\n\n# With a custom function\ndef add_prefix(text, prefix):\n return prefix + text\n\nresult = run_catching_with(\"world\", add_prefix, \"Hello, \")\nprint(result.get_or_null()) # \"Hello, world\"\n\n# With lambda functions\nresult = run_catching_with(42, lambda x: x * 2)\nprint(result.get_or_null()) # 84\n\n# Type conversion with error handling\nresult = run_catching_with(\"123\", int)\nprint(result.get_or_null()) # 123\n\nresult = run_catching_with(\"not a number\", int)\nprint(result.is_failure) # True\nprint(type(result.exception_or_null())) # <class 'ValueError'>\n\n# Chaining operations with receiver\ndef process_text(text):\n return text.strip().lower().replace(\" \", \"_\")\n\nresult = run_catching_with(\" Hello World \", process_text)\nprint(result.get_or_null()) # \"hello_world\"\n```\n\n## API Reference\n\n### Result Class\n\n#### Static Methods\n\n- `Result.success(value)`: Creates a success result with the given value\n- `Result.failure(exception)`: Creates a failure result with the given exception\n\n#### Properties\n\n- `is_success`: Returns `True` if the result is a success, `False` otherwise\n- `is_failure`: Returns `True` if the result is a failure, `False` otherwise\n\n#### Methods\n\n- `get_or_null()`: Returns the value if success, `None` if failure\n- `get_or_none()`: Alias for `get_or_null()` for Python naming convention\n- `exception_or_null()`: Returns the exception if failure, `None` if success\n- `exception_or_none()`: Alias for `exception_or_null()` for Python naming convention\n- `to_string()`: Returns a string representation of the result\n- `get_or_default(default_value)`: Returns the value if success, the default value if failure\n- `get_or_throw()`: Returns the value if success, throws the exception if failure\n- `throw_on_failure()`: Throws the exception if failure, does nothing if success\n- `on_success(callback)`: Executes the callback with the value if success, returns the Result object for chaining\n- `on_failure(callback)`: Executes the callback with the exception if failure, returns the Result object for chaining\n- `map(transform)`: Transforms the success value with the given function, returns a new Result\n- `map_catching(transform)`: Like map(), but catches exceptions thrown by the transform function\n- `recover(transform)`: Transforms the failure exception to a success value, returns a new Result\n- `recover_catching(transform)`: Like recover(), but catches exceptions thrown by the transform function\n- `fold(on_success, on_failure)`: Applies the appropriate function based on success/failure and returns the result directly (not wrapped in Result)\n- `get_or_else(on_failure)`: Returns the success value or computes an alternative value from the exception\n\n### run_catching Function\n\n- `run_catching(func, *args, **kwargs)`: Executes the function with the given arguments and returns a `Result` object\n\n### run_catching_with Function\n\n- `run_catching_with(receiver, func, *args, **kwargs)`: Executes the function with a receiver object as the first argument and returns a `Result` object\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n",
"bugtrack_url": null,
"license": "MIT License",
"summary": "A Python implementation of the Result monad pattern, inspired by Kotlin",
"version": "1.0.0",
"project_urls": {
"Homepage": "https://github.com/Lalcs/kotresult"
},
"split_keywords": [
"result",
" monad",
" kotlin",
" error-handling",
" exception"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "81dddbc884c30f06fc41980c49686a0420866cf532ccb7a78f326c70c56d43b7",
"md5": "538dc905951447981b54802753d59a21",
"sha256": "d3c18a87a7ab5a648892d88e3f11924daa17e862459fc1bd712b7cd6f24e5853"
},
"downloads": -1,
"filename": "kotresult-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "538dc905951447981b54802753d59a21",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 11095,
"upload_time": "2025-07-18T13:40:14",
"upload_time_iso_8601": "2025-07-18T13:40:14.404157Z",
"url": "https://files.pythonhosted.org/packages/81/dd/dbc884c30f06fc41980c49686a0420866cf532ccb7a78f326c70c56d43b7/kotresult-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "238ff6497252ca91f4776fb0b395440266913fa4517ed7b62dca8ea6a9cb57ef",
"md5": "06f85093f0499b0854044294964cc3bb",
"sha256": "19965a4b2d1d6c73193e0e9a29d9204c37d29a4476a352d3195379bc162e65ce"
},
"downloads": -1,
"filename": "kotresult-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "06f85093f0499b0854044294964cc3bb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 10309,
"upload_time": "2025-07-18T13:40:15",
"upload_time_iso_8601": "2025-07-18T13:40:15.171220Z",
"url": "https://files.pythonhosted.org/packages/23/8f/f6497252ca91f4776fb0b395440266913fa4517ed7b62dca8ea6a9cb57ef/kotresult-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-18 13:40:15",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Lalcs",
"github_project": "kotresult",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "kotresult"
}