-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add decorator to make plain functions return a Result #33
Comments
I was looking at some Lua stuff (literally first time diving into some Lua stuff, so no prior experience) and found something quite relevant, https://riptutorial.com/lua/example/16000/using-pcall In that case, it's not a decorator, not sure if Lua has decorators. But the idea is somewhat similar, wrap a exception throwing function in a protected 'call-scope' and read out the sucess/failure + exception/value. |
@dbrgn I think it's a interesting idea. Not sure how you feel about the scope of this library and if this falls within it. |
Yeah, it does sound interesting 🙂 I don't currently have the capacity to review and maintain this feature though, so @francium if you're interested just go ahead! |
@francium I also have no experience with Lua but had a quick look. In my opinion, This would still leave the problem with the typing of But I don't see the advantage of importing a function where you probably have to read documentation for, over a two-line |
I was having a quick play around with your sample code. You're correct about the generic args/kwargs typing, ran into same type checking issues without any obvious solution, except for reducing the typing here (1). But even then, I noticed some typing issues in the call site of the wrapped function (2) (1) O = TypeVar("O")
@singledispatch
def safe_wrap(f: Any) -> Callable[[Any], Result[O, Exception]]:
@wraps(f)
def wrapper(*args: Any, **kwargs: Any) -> Result[O, Exception]:
try:
return Ok(f(*args, **kwargs))
except Exception as err:
return Err(err)
return wrapper You also had some issues with how you passed through the (2) @safe_wrap
def throwing() -> None:
raise TypeError()
x = throwing() # type: Result But Anyway, it definitely works barring the type checking Pros:
Cons:
But is there is a huge demand for this helper function? As you mentioned this is same as a few additional lines of try/except which should avoid all the type checking limitations. I do this it's a cool approach to making things runtime safe, but I'm hesitant to push forward with this at this time. Perhaps in the future
I would be inclined to mark this as Let me know if you disagree otherwise I'll close this issue after waiting a bit for you to reply. EDIT
Notice I missed this bit and that would've probably helped resolve (2). |
Yeah, let's keep this open. If a few people express interest, we can still consider shipping an implementation. |
@francium great analysis, thanks for your time! 👍 And the edit you added is correct: This would solve the problem. I was stuck at the same point as you and added the generic dispatch to "solve" it. I also had a deeper look and notices another "Con": The lack of typing for So all in all, this might be a cool feature but I absolutely agree with your concerns. Maybe generic argument typing will be added to Python in the future. |
pep 612's mypy support for this is tracked here: python/mypy#8645 |
Add as_result() helper to make a decorator to turn a function into one that returns a a ``Result``. For type checking, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/) This is currently not supported by Mypy; see python/mypy#8645 Fixes rustedpy#33.
i've built a fully type-safe implementation using see #71 for details. |
Add as_result() helper to make a decorator to turn a function into one that returns a a ``Result``. For type checking, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/) This is currently not supported by Mypy; see python/mypy#8645 Fixes rustedpy#33.
@noah04 out of curiosity, do you remember why you used |
Add as_result() helper to make a decorator to turn a function into one that returns a a ``Result``. For type checking, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/) This is currently not supported by Mypy; see python/mypy#8645 Fixes rustedpy#33.
Add as_result() helper to make a decorator to turn a function into one that returns a a ``Result``. For type checking, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/) This is currently not supported by Mypy; see python/mypy#8645 Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
@wbolster if I remember correctly, I did that to enable generic calls with Mentioning that, I would really like you for investing time on this topic 👍🏼 |
i suggest you look at #71 🙃 which seems promising |
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). This is currently not fully supported by Mypy; see python/mypy#8645 Calling decorated functions works without errors from Mypy, but will not be type-safe, i.e. it will behave as if it is calling a function like f(*args: Any, **kwargs: Any) Fixes rustedpy#33.
Add a as_result() helper to make a decorator to turn a function into one that returns a Result: Regular return values are turned into Ok(return_value). Raised exceptions of the specified exception type(s) are turned into Err(exc). The decorator is signature-preserving, except for wrapping the return type into a Result, of course. For type annotations, this depends on typing.ParamSpec which requires Python 3.10+ (or use typing_extensions); see PEP612 (https://www.python.org/dev/peps/pep-0612/). Fixes #33.
good news everybody, see the readme for example usage! |
Idea
While using the library, I came up with the idea to implement a simple decorator that wraps any plain function into
try-except
and returns anErr
if an exception occurred. This makes it very easy to wrap existing "unsafe" functions and thus prevent runtime errors.Example:
Existing implementation
Just to give you an idea, here is my naive first implementation (shortened):
Downsides
Typing arbitrary
*args
and**kwargs
is not trivial, as described in python/mypy#5876. In my current implementation, I work around this with usingAny
and a generic dispatch. Nevertheless, this may not comply with the excessive typing that the rest ofresult
-module aims for.Question(s)
What do you think about the concept and do you want something like this in the library? If so, I could provide a PR with a more elaborated version of the code shown above.
The text was updated successfully, but these errors were encountered: