From ea8fb4d011859286ad25cb07cc615de0a301aac2 Mon Sep 17 00:00:00 2001 From: check69 Date: Thu, 23 Nov 2023 00:05:43 +0000 Subject: [PATCH 1/6] Add static method from_optional --- rusty_results/prelude.py | 9 ++++++++- rusty_results/tests/option/test_option.py | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/rusty_results/prelude.py b/rusty_results/prelude.py index c7d703d..df86d0e 100644 --- a/rusty_results/prelude.py +++ b/rusty_results/prelude.py @@ -1,6 +1,6 @@ from abc import abstractmethod from dataclasses import dataclass -from typing import cast, TypeVar, Union, Callable, Generic, Iterator, Tuple, Dict, Any +from typing import cast, TypeVar, Union, Callable, Generic, Iterator, Tuple, Dict, Any, Optional from rusty_results.exceptions import UnwrapException try: from pydantic.fields import ModelField @@ -324,6 +324,13 @@ def __validate_dict(cls, value: Dict, field: "ModelField"): return Some(valid_value) + @staticmethod + def from_optional(value: Optional[T]) -> "Option[T]": + if value is None: + return Empty() + + return Some(value) + @dataclass(eq=True, frozen=True) class Some(OptionProtocol[T]): diff --git a/rusty_results/tests/option/test_option.py b/rusty_results/tests/option/test_option.py index 30d860c..35e354d 100644 --- a/rusty_results/tests/option/test_option.py +++ b/rusty_results/tests/option/test_option.py @@ -32,3 +32,11 @@ def test_option_contains(): def test_option_iter(): assert list(iter(Empty())) == [] assert list(iter(Some(1))) == [1] + + +def test_from_optional(): + assert Some(0) == OptionProtocol.from_optional(0) + assert Empty() == OptionProtocol.from_optional(None) + example_dictionary = {"data": 5} + assert Empty() == OptionProtocol.from_optional(example_dictionary.get("key_not_found")) + assert Some(5) == OptionProtocol.from_optional(example_dictionary.get("data")) From 6a090f62473ebcc8f0e6126a97fa76c747badb09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Cabeza=20Romero?= Date: Mon, 2 Sep 2024 11:48:32 +0200 Subject: [PATCH 2/6] Move Option.from_optional to option_from function. --- rusty_results/prelude.py | 17 ++++++++++------- rusty_results/tests/option/test_option.py | 15 ++++++++++----- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/rusty_results/prelude.py b/rusty_results/prelude.py index df86d0e..9453304 100644 --- a/rusty_results/prelude.py +++ b/rusty_results/prelude.py @@ -324,13 +324,6 @@ def __validate_dict(cls, value: Dict, field: "ModelField"): return Some(valid_value) - @staticmethod - def from_optional(value: Optional[T]) -> "Option[T]": - if value is None: - return Empty() - - return Some(value) - @dataclass(eq=True, frozen=True) class Some(OptionProtocol[T]): @@ -522,6 +515,16 @@ def __get_validators__(cls): Option = Union[Some[T], Empty] +def option_from[T](value: Optional[T]) -> Option[T]: + """ + :param value: Value from any type to be converted to an Option + :return: The value wrapped in an Option or the original Option if it is already an Option + """ + if value is None: + return Empty() + return Some(value).flatten_one() + + class ResultProtocol(Generic[T, E]): @property @abstractmethod diff --git a/rusty_results/tests/option/test_option.py b/rusty_results/tests/option/test_option.py index 35e354d..3d53e15 100644 --- a/rusty_results/tests/option/test_option.py +++ b/rusty_results/tests/option/test_option.py @@ -34,9 +34,14 @@ def test_option_iter(): assert list(iter(Some(1))) == [1] -def test_from_optional(): - assert Some(0) == OptionProtocol.from_optional(0) - assert Empty() == OptionProtocol.from_optional(None) +def test_option_from(): + assert Some(0) == option_from(0) + assert Empty() == option_from(None) example_dictionary = {"data": 5} - assert Empty() == OptionProtocol.from_optional(example_dictionary.get("key_not_found")) - assert Some(5) == OptionProtocol.from_optional(example_dictionary.get("data")) + assert Empty() == option_from(example_dictionary.get("key_not_found")) + assert Some(5) == option_from(example_dictionary.get("data")) + opt = Some(3) + assert opt == option_from(opt) + assert Empty() == option_from(Empty()) + nested_opt = Some(Some(Some(Some(3)))) + assert nested_opt == option_from(nested_opt) From 7a50a672f2ca8a42a396beacf68828c3114d84ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Cabeza=20Romero?= Date: Mon, 2 Sep 2024 11:48:44 +0200 Subject: [PATCH 3/6] Fix mypy List typing. --- examples/patmat_option.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/patmat_option.py b/examples/patmat_option.py index ff5fc10..cabe62f 100644 --- a/examples/patmat_option.py +++ b/examples/patmat_option.py @@ -1,11 +1,12 @@ """ Example on pattern matching handling of Option """ +from typing import List from rusty_results import Option, Some, Empty -def find_index(l: [str], value: str) -> Option[int]: +def find_index(l: List[str], value: str) -> Option[int]: for i, e in enumerate(l): if e == value: return Some(i) From fb8e7093477d5c936342ac0246f5ec62b7d84bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex?= Date: Mon, 2 Sep 2024 11:51:46 +0200 Subject: [PATCH 4/6] Update prelude.py Undo remove line jump. --- rusty_results/prelude.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rusty_results/prelude.py b/rusty_results/prelude.py index d5b4a63..6e08c4a 100644 --- a/rusty_results/prelude.py +++ b/rusty_results/prelude.py @@ -2,6 +2,7 @@ from dataclasses import dataclass from typing import cast, TypeVar, Union, Callable, Generic, Iterator, Tuple, Dict, Any, Optional from rusty_results.exceptions import UnwrapException, EarlyReturnException + try: from pydantic.fields import ModelField except ImportError: # pragma: no cover @@ -1052,4 +1053,4 @@ def __get_validators__(cls): yield from ResultProtocol.__get_validators__() -Result = Union[Ok[T, E], Err[T, E]] \ No newline at end of file +Result = Union[Ok[T, E], Err[T, E]] From 7062f467da545148de243adeb708b76f31d24cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Cabeza=20Romero?= Date: Mon, 2 Sep 2024 11:53:33 +0200 Subject: [PATCH 5/6] Remove incompatible generic type on option_from. --- rusty_results/prelude.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rusty_results/prelude.py b/rusty_results/prelude.py index 6e08c4a..ccc4161 100644 --- a/rusty_results/prelude.py +++ b/rusty_results/prelude.py @@ -540,7 +540,7 @@ def __get_validators__(cls): Option = Union[Some[T], Empty] -def option_from[T](value: Optional[T]) -> Option[T]: +def option_from(value: Optional[T]) -> Option[T]: """ :param value: Value from any type to be converted to an Option :return: The value wrapped in an Option or the original Option if it is already an Option From b8de69225883555f708e9bf845da2bccf06acdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Cabeza=20Romero?= Date: Mon, 2 Sep 2024 13:12:47 +0200 Subject: [PATCH 6/6] Update codecov action and include token. --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55d63ba..e82dad0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,9 @@ jobs: - name: Build coverage file run: pytest --cache-clear --cov=rusty_results --cov-report=xml:pytest-coverage.xml ./rusty_results - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: files: ./pytest-coverage.xml - fail_ci_if_error: true \ No newline at end of file + fail_ci_if_error: true