def get_fact_result(self) -> Result[str, Exception]: """Return a Result for a cat fact.""" return ( Result.of( requests.get, "https://cat-fact.herokuapp.com/facts/random", # this is the default, but sometimes the type checker wants us # to make it explicit. See python/mypy#3737 for deets. catch=Exception, ) .and_then(lambda resp: Result.of(resp.json)) .map( lambda parsed: t.cast( str, parsed.get("text", "Unexpected cat fact format!") ) ) )
def test_collect_short_circuits(self) -> None: """Ensure collect does not iterate after an err is reached.""" until_err: t.List[Result[int, str]] = [Ok(1), Ok(2), Err("no")] def _iterable() -> t.Iterable[Result[int, str]]: yield from until_err # If we continue iterating after the err, we will raise a # runtime Error. assert False, "Result.collect() did not short circuit on err!" assert Result.collect(_iterable()) == Err("no")
def get_fact(self) -> str: """Get a cat fact!""" return ( # Create a Result from making our GET request. # Now we can start chaining! Result.of( requests.get, "https://cat-fact.herokuapp.com/facts/random" ) # Let's first consider the success path. # If we got a response, it should be JSON, so let's try to parse .and_then(lambda resp: Result.of(resp.json)) # If we successfully parsed JSON, we must have a dict, so let's # grab our cat fact, or a useful message. .map( lambda parsed: t.cast( str, parsed.get("text", "Unexpected cat fact format!") ) ) # From here, all we need to do to consider the error case is # convert our Err type (which for Result.of() is any exception # that was raised) into the expected return type, which we # do by passing the error to `str()` .unwrap_or_else(str) )
def test_result_cannot_be_instantiated(self) -> None: """Result cannot be instantiated""" with pytest.raises(NotImplementedError): r: Result[str, str] = Result("a") assert r
def test_ok_if(self, predicate: t.Callable, val: t.Any, exp: Result) -> None: """Test constructing based on some predicate.""" assert Result.ok_if(predicate, val) == exp
def test_collect(self, iterable: t.Iterable[Result[int, str]], exp: Result[int, str]) -> None: """Test collecting an iterable of results into a single result.""" assert Result.collect(iterable) == exp
def test_of_with_kwargs(self) -> None: """Test getting a result from a callable with args.""" def foo(a: int, b: str = None) -> t.Optional[str]: return b assert Result.of(foo, 1, b="a").unwrap() == "a"
def test_of_with_args(self) -> None: """Test getting a result from a callable with args.""" assert Result.of(lambda x: bool(x > 0), 1).unwrap() is True
def test_of(self, fn: t.Callable, exp: Result) -> None: """Test getting a result from a callable.""" if exp.is_err(): assert isinstance(Result.of(fn).unwrap_err(), exp.unwrap_err()) else: assert Result.of(fn) == exp