Updated script that can be controled by Nodejs web app
This commit is contained in:
20
lib/python3.13/site-packages/outcome/__init__.py
Normal file
20
lib/python3.13/site-packages/outcome/__init__.py
Normal file
@ -0,0 +1,20 @@
|
||||
"""Top-level package for outcome."""
|
||||
|
||||
from ._impl import (
|
||||
Error as Error,
|
||||
Maybe as Maybe,
|
||||
Outcome as Outcome,
|
||||
Value as Value,
|
||||
acapture as acapture,
|
||||
capture as capture,
|
||||
)
|
||||
from ._util import AlreadyUsedError as AlreadyUsedError, fixup_module_metadata
|
||||
from ._version import __version__ as __version__
|
||||
|
||||
__all__ = (
|
||||
'Error', 'Outcome', 'Value', 'Maybe', 'acapture', 'capture',
|
||||
'AlreadyUsedError'
|
||||
)
|
||||
|
||||
fixup_module_metadata(__name__, globals())
|
||||
del fixup_module_metadata
|
239
lib/python3.13/site-packages/outcome/_impl.py
Normal file
239
lib/python3.13/site-packages/outcome/_impl.py
Normal file
@ -0,0 +1,239 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
AsyncGenerator,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Generator,
|
||||
Generic,
|
||||
NoReturn,
|
||||
TypeVar,
|
||||
Union,
|
||||
overload,
|
||||
)
|
||||
|
||||
import attr
|
||||
|
||||
from ._util import AlreadyUsedError, remove_tb_frames
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import ParamSpec, final
|
||||
ArgsT = ParamSpec("ArgsT")
|
||||
else:
|
||||
|
||||
def final(func):
|
||||
return func
|
||||
|
||||
|
||||
__all__ = ['Error', 'Outcome', 'Maybe', 'Value', 'acapture', 'capture']
|
||||
|
||||
ValueT = TypeVar("ValueT", covariant=True)
|
||||
ResultT = TypeVar("ResultT")
|
||||
|
||||
|
||||
@overload
|
||||
def capture(
|
||||
# NoReturn = raises exception, so we should get an error.
|
||||
sync_fn: Callable[ArgsT, NoReturn],
|
||||
*args: ArgsT.args,
|
||||
**kwargs: ArgsT.kwargs,
|
||||
) -> Error:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def capture(
|
||||
sync_fn: Callable[ArgsT, ResultT],
|
||||
*args: ArgsT.args,
|
||||
**kwargs: ArgsT.kwargs,
|
||||
) -> Value[ResultT] | Error:
|
||||
...
|
||||
|
||||
|
||||
def capture(
|
||||
sync_fn: Callable[ArgsT, ResultT],
|
||||
*args: ArgsT.args,
|
||||
**kwargs: ArgsT.kwargs,
|
||||
) -> Value[ResultT] | Error:
|
||||
"""Run ``sync_fn(*args, **kwargs)`` and capture the result.
|
||||
|
||||
Returns:
|
||||
Either a :class:`Value` or :class:`Error` as appropriate.
|
||||
|
||||
"""
|
||||
try:
|
||||
return Value(sync_fn(*args, **kwargs))
|
||||
except BaseException as exc:
|
||||
exc = remove_tb_frames(exc, 1)
|
||||
return Error(exc)
|
||||
|
||||
|
||||
@overload
|
||||
async def acapture(
|
||||
async_fn: Callable[ArgsT, Awaitable[NoReturn]],
|
||||
*args: ArgsT.args,
|
||||
**kwargs: ArgsT.kwargs,
|
||||
) -> Error:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
async def acapture(
|
||||
async_fn: Callable[ArgsT, Awaitable[ResultT]],
|
||||
*args: ArgsT.args,
|
||||
**kwargs: ArgsT.kwargs,
|
||||
) -> Value[ResultT] | Error:
|
||||
...
|
||||
|
||||
|
||||
async def acapture(
|
||||
async_fn: Callable[ArgsT, Awaitable[ResultT]],
|
||||
*args: ArgsT.args,
|
||||
**kwargs: ArgsT.kwargs,
|
||||
) -> Value[ResultT] | Error:
|
||||
"""Run ``await async_fn(*args, **kwargs)`` and capture the result.
|
||||
|
||||
Returns:
|
||||
Either a :class:`Value` or :class:`Error` as appropriate.
|
||||
|
||||
"""
|
||||
try:
|
||||
return Value(await async_fn(*args, **kwargs))
|
||||
except BaseException as exc:
|
||||
exc = remove_tb_frames(exc, 1)
|
||||
return Error(exc)
|
||||
|
||||
|
||||
@attr.s(repr=False, init=False, slots=True)
|
||||
class Outcome(abc.ABC, Generic[ValueT]):
|
||||
"""An abstract class representing the result of a Python computation.
|
||||
|
||||
This class has two concrete subclasses: :class:`Value` representing a
|
||||
value, and :class:`Error` representing an exception.
|
||||
|
||||
In addition to the methods described below, comparison operators on
|
||||
:class:`Value` and :class:`Error` objects (``==``, ``<``, etc.) check that
|
||||
the other object is also a :class:`Value` or :class:`Error` object
|
||||
respectively, and then compare the contained objects.
|
||||
|
||||
:class:`Outcome` objects are hashable if the contained objects are
|
||||
hashable.
|
||||
|
||||
"""
|
||||
_unwrapped: bool = attr.ib(default=False, eq=False, init=False)
|
||||
|
||||
def _set_unwrapped(self) -> None:
|
||||
if self._unwrapped:
|
||||
raise AlreadyUsedError
|
||||
object.__setattr__(self, '_unwrapped', True)
|
||||
|
||||
@abc.abstractmethod
|
||||
def unwrap(self) -> ValueT:
|
||||
"""Return or raise the contained value or exception.
|
||||
|
||||
These two lines of code are equivalent::
|
||||
|
||||
x = fn(*args)
|
||||
x = outcome.capture(fn, *args).unwrap()
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def send(self, gen: Generator[ResultT, ValueT, object]) -> ResultT:
|
||||
"""Send or throw the contained value or exception into the given
|
||||
generator object.
|
||||
|
||||
Args:
|
||||
gen: A generator object supporting ``.send()`` and ``.throw()``
|
||||
methods.
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
async def asend(self, agen: AsyncGenerator[ResultT, ValueT]) -> ResultT:
|
||||
"""Send or throw the contained value or exception into the given async
|
||||
generator object.
|
||||
|
||||
Args:
|
||||
agen: An async generator object supporting ``.asend()`` and
|
||||
``.athrow()`` methods.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@final
|
||||
@attr.s(frozen=True, repr=False, slots=True)
|
||||
class Value(Outcome[ValueT], Generic[ValueT]):
|
||||
"""Concrete :class:`Outcome` subclass representing a regular value.
|
||||
|
||||
"""
|
||||
|
||||
value: ValueT = attr.ib()
|
||||
"""The contained value."""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Value({self.value!r})'
|
||||
|
||||
def unwrap(self) -> ValueT:
|
||||
self._set_unwrapped()
|
||||
return self.value
|
||||
|
||||
def send(self, gen: Generator[ResultT, ValueT, object]) -> ResultT:
|
||||
self._set_unwrapped()
|
||||
return gen.send(self.value)
|
||||
|
||||
async def asend(self, agen: AsyncGenerator[ResultT, ValueT]) -> ResultT:
|
||||
self._set_unwrapped()
|
||||
return await agen.asend(self.value)
|
||||
|
||||
|
||||
@final
|
||||
@attr.s(frozen=True, repr=False, slots=True)
|
||||
class Error(Outcome[NoReturn]):
|
||||
"""Concrete :class:`Outcome` subclass representing a raised exception.
|
||||
|
||||
"""
|
||||
|
||||
error: BaseException = attr.ib(
|
||||
validator=attr.validators.instance_of(BaseException)
|
||||
)
|
||||
"""The contained exception object."""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Error({self.error!r})'
|
||||
|
||||
def unwrap(self) -> NoReturn:
|
||||
self._set_unwrapped()
|
||||
# Tracebacks show the 'raise' line below out of context, so let's give
|
||||
# this variable a name that makes sense out of context.
|
||||
captured_error = self.error
|
||||
try:
|
||||
raise captured_error
|
||||
finally:
|
||||
# We want to avoid creating a reference cycle here. Python does
|
||||
# collect cycles just fine, so it wouldn't be the end of the world
|
||||
# if we did create a cycle, but the cyclic garbage collector adds
|
||||
# latency to Python programs, and the more cycles you create, the
|
||||
# more often it runs, so it's nicer to avoid creating them in the
|
||||
# first place. For more details see:
|
||||
#
|
||||
# https://github.com/python-trio/trio/issues/1770
|
||||
#
|
||||
# In particuar, by deleting this local variables from the 'unwrap'
|
||||
# methods frame, we avoid the 'captured_error' object's
|
||||
# __traceback__ from indirectly referencing 'captured_error'.
|
||||
del captured_error, self
|
||||
|
||||
def send(self, gen: Generator[ResultT, NoReturn, object]) -> ResultT:
|
||||
self._set_unwrapped()
|
||||
return gen.throw(self.error)
|
||||
|
||||
async def asend(self, agen: AsyncGenerator[ResultT, NoReturn]) -> ResultT:
|
||||
self._set_unwrapped()
|
||||
return await agen.athrow(self.error)
|
||||
|
||||
|
||||
# A convenience alias to a union of both results, allowing exhaustiveness checking.
|
||||
Maybe = Union[Value[ValueT], Error]
|
33
lib/python3.13/site-packages/outcome/_util.py
Normal file
33
lib/python3.13/site-packages/outcome/_util.py
Normal file
@ -0,0 +1,33 @@
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
class AlreadyUsedError(RuntimeError):
|
||||
"""An Outcome can only be unwrapped once."""
|
||||
pass
|
||||
|
||||
|
||||
def fixup_module_metadata(
|
||||
module_name: str,
|
||||
namespace: Dict[str, object],
|
||||
) -> None:
|
||||
def fix_one(obj: object) -> None:
|
||||
mod = getattr(obj, "__module__", None)
|
||||
if mod is not None and mod.startswith("outcome."):
|
||||
obj.__module__ = module_name
|
||||
if isinstance(obj, type):
|
||||
for attr_value in obj.__dict__.values():
|
||||
fix_one(attr_value)
|
||||
|
||||
all_list = namespace["__all__"]
|
||||
assert isinstance(all_list, (tuple, list)), repr(all_list)
|
||||
for objname in all_list:
|
||||
obj = namespace[objname]
|
||||
fix_one(obj)
|
||||
|
||||
|
||||
def remove_tb_frames(exc: BaseException, n: int) -> BaseException:
|
||||
tb = exc.__traceback__
|
||||
for _ in range(n):
|
||||
assert tb is not None
|
||||
tb = tb.tb_next
|
||||
return exc.with_traceback(tb)
|
7
lib/python3.13/site-packages/outcome/_version.py
Normal file
7
lib/python3.13/site-packages/outcome/_version.py
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is imported from __init__.py and exec'd from setup.py
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Final
|
||||
|
||||
__version__: 'Final[str]' = "1.3.0.post0"
|
0
lib/python3.13/site-packages/outcome/py.typed
Normal file
0
lib/python3.13/site-packages/outcome/py.typed
Normal file
Reference in New Issue
Block a user