diff --git a/.coveragerc b/.coveragerc
index ed33143..188cbaf 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,6 +1,8 @@
[run]
source =
result
+omit =
+ result/typetests.py
[report]
# Regexes for lines to exclude from consideration
diff --git a/.travis.yml b/.travis.yml
index 2a7e8eb..d80972c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,6 +11,12 @@ install:
script:
- pip install -e .
- py.test
- - mypy result/result.py
+ - if [ $(python -c 'import sys; print(sys.version_info.minor)') -gt 4 ]; then
+ mypy result/result.py;
+ mypy result/typetests.py;
+ echo "Mypy tests done";
+ else
+ echo "Skipping mypy";
+ fi
after_success:
- coveralls
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc5bf97..10378c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,11 @@ Possible log types:
- `[fixed]` for any bug fixes.
- `[security]` to invite users to upgrade in case of vulnerabilities.
+## Unreleased
+
+ - [changed] Type annotations: Change parameter order
+ from `Result[E, T]` to `Result[T, E]` to match Rust/OCaml/F# (#7)
+
## [0.4.1] - 2020-02-17
- [added] Add `py.typed` for PEP561 package compliance (#16)
diff --git a/README.rst b/README.rst
index f88a9b5..d5a7648 100644
--- a/README.rst
+++ b/README.rst
@@ -9,7 +9,8 @@ Result
:alt: Coverage
:target: https://coveralls.io/github/dbrgn/result
-A simple Result type for Python 3 `inspired by Rust `__.
+A simple Result type for Python 3 `inspired by Rust
+`__, fully type annotated.
The idea is that a ``Result`` value can be either ``Ok(value)`` or ``Err(error)``,
with a way to differentiate between the two. It will change code like this:
@@ -62,8 +63,8 @@ side, you don't have to return semantically unclear tuples anymore.
Not all methods (https://doc.rust-lang.org/std/result/enum.Result.html) have
been implemented, only the ones that make sense in the Python context. You still
-don't get any type safety, but some easier handling of types that can be OK or
-not, without resorting to custom exceptions.
+don't get any type safety at runtime, but some easier handling of types that can
+be OK or not, without resorting to custom exceptions.
API
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 8598ef4..f023f7b 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,4 +1,4 @@
pytest==2.8.5
pytest-cov==2.2.0
pytest-pep8==1.0.6
--e git+https://github.com/python/mypy@005fd6b357b995f13e3d90293cabf3c8c8c2a952#egg=mypy
+mypy==0.720
diff --git a/result/result.py b/result/result.py
index 2490609..1c7983b 100644
--- a/result/result.py
+++ b/result/result.py
@@ -1,29 +1,33 @@
from typing import Callable, Generic, TypeVar, Union, Any, Optional, cast, overload
-E = TypeVar("E")
+T = TypeVar("T") # Success type
+E = TypeVar("E") # Error type
F = TypeVar("F")
-T = TypeVar("T")
U = TypeVar("U")
-class Result(Generic[E, T]):
+class Result(Generic[T, E]):
"""
A simple `Result` type inspired by Rust.
Not all methods (https://doc.rust-lang.org/std/result/enum.Result.html)
have been implemented, only the ones that make sense in the Python context.
"""
- def __init__(self, is_ok: bool, value: Union[E, T], force: bool = False) -> None:
+ def __init__(self, is_ok: bool, value: Union[T, E], force: bool = False) -> None:
"""Do not call this constructor, use the Ok or Err class methods instead.
There are no type guarantees on the value if this is called directly.
Args:
- is_ok: If this represents an ok result
- value: The value inside the result
- force: Force creation of the object. This is false by default to prevent
+ is_ok:
+ If this represents an ok result
+ value:
+ The value inside the result
+ force:
+ Force creation of the object. This is false by default to prevent
accidentally creating instance of a Result in an unsafe way.
+
"""
if force is not True:
raise RuntimeError("Don't instantiate a Result directly. "
@@ -51,20 +55,20 @@ def __repr__(self) -> str:
@classmethod
@overload
- def Ok(cls) -> 'Result[E, bool]':
+ def Ok(cls) -> 'Result[bool, Any]':
pass
@classmethod
@overload
- def Ok(cls, value: T) -> 'Result[E, T]':
+ def Ok(cls, value: T) -> 'Result[T, Any]':
pass
@classmethod
- def Ok(cls, value: Any = True) -> 'Result[E, Any]':
+ def Ok(cls, value: Any = True) -> 'Result[Any, Any]':
return cls(is_ok=True, value=value, force=True)
@classmethod
- def Err(cls, error: E) -> 'Result[E, T]':
+ def Err(cls, error: E) -> 'Result[Any, E]':
return cls(is_ok=False, value=error, force=True)
def is_ok(self) -> bool:
@@ -87,7 +91,7 @@ def err(self) -> Optional[E]:
return cast(E, self._value) if self.is_err() else None
@property
- def value(self) -> Union[E, T]:
+ def value(self) -> Union[T, E]:
"""
Return the inner value. This might be either the ok or the error type.
"""
@@ -120,14 +124,14 @@ def unwrap_or(self, default: T) -> T:
else:
return default
- def map(self, op: Callable[[T], U]) -> 'Result[E, U]':
+ def map(self, op: Callable[[T], U]) -> 'Result[U, E]':
"""
If contained result is `Ok`, return `Ok` with original value mapped to
a new value using the passed in function. Otherwise return `Err` with
same value.
"""
if not self._is_ok:
- return cast(Result[E, U], self)
+ return cast(Result[U, E], self)
return Ok(op(cast(T, self._value)))
def map_or(self, default: U, op: Callable[[T], U]) -> U:
@@ -153,35 +157,35 @@ def map_or_else(
return default_op()
return op(cast(T, self._value))
- def map_err(self, op: Callable[[E], F]) -> 'Result[F, T]':
+ def map_err(self, op: Callable[[E], F]) -> 'Result[T, F]':
"""
If contained result is `Err`, return `Err` with original value mapped
to a new value using the passed in `op` function. Otherwise return `Ok`
with the same value.
"""
if self._is_ok:
- return cast(Result[F, T], self)
+ return cast(Result[T, F], self)
return Err(op(cast(E, self._value)))
@overload
-def Ok() -> Result[E, bool]:
+def Ok() -> Result[bool, Any]:
pass
@overload
-def Ok(value: T) -> Result[E, T]:
+def Ok(value: T) -> Result[T, Any]:
pass
-def Ok(value: Any = True) -> Result[E, Any]:
+def Ok(value: Any = True) -> Result[Any, Any]:
"""
Shortcut function to create a new Result.
"""
return Result.Ok(value)
-def Err(error: E) -> Result[E, T]:
+def Err(error: E) -> Result[Any, E]:
"""
Shortcut function to create a new Result.
"""
diff --git a/result/typetests.py b/result/typetests.py
new file mode 100644
index 0000000..ba0ee86
--- /dev/null
+++ b/result/typetests.py
@@ -0,0 +1,20 @@
+from typing import List, Optional
+
+from .result import Result, Ok, Err
+
+
+res1 = Result.Ok('hello') # type: Result[str, int]
+if res1.is_ok():
+ ok = res1.ok() # type: Optional[str]
+ mapped_to_float = res1.map_or(1.0, lambda s: len(s) * 1.5) # type: float
+else:
+ err = res1.err() # type: Optional[int]
+ mapped_to_list = res1.map_err(lambda e: [e]).err() # type: Optional[List[int]]
+
+# Test constructor functions
+res2 = Ok()
+res3 = Result.Ok()
+res4 = Ok(42)
+res5 = Result.Ok(23)
+res6 = Err(1)
+res7 = Result.Err(2)