more docs!
This commit is contained in:
parent
720d4e97e4
commit
92961c8f58
8 changed files with 92 additions and 17 deletions
|
|
@ -68,6 +68,11 @@ class Functions(
|
||||||
----------
|
----------
|
||||||
function
|
function
|
||||||
A static function whose name will be used as the offered function name.
|
A static function whose name will be used as the offered function name.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError
|
||||||
|
If the name already exists as a function.
|
||||||
"""
|
"""
|
||||||
if cls.is_built_in(function.__name__):
|
if cls.is_built_in(function.__name__):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
|
|
||||||
|
|
@ -405,6 +405,24 @@ class Script:
|
||||||
resolved: Optional[Dict[str, Resolvable]] = None,
|
resolved: Optional[Dict[str, Resolvable]] = None,
|
||||||
unresolvable: Optional[Set[str]] = None,
|
unresolvable: Optional[Set[str]] = None,
|
||||||
) -> Dict[str, Resolvable]:
|
) -> Dict[str, Resolvable]:
|
||||||
|
"""
|
||||||
|
Given a new set of variable definitions, resolve them using the Script, but do not
|
||||||
|
add them to the Script itself.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
variable_definitions
|
||||||
|
Variables to resolve, but not store in the Script
|
||||||
|
resolved
|
||||||
|
Optional. Pre-resolved variables that should be used instead of what is in the script.
|
||||||
|
unresolvable
|
||||||
|
Optional. Unresolvable variables that will be ignored in resolution, including all
|
||||||
|
variables with a dependency to them.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Dict containing the variable names to their resolved values.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
self.add(variable_definitions)
|
self.add(variable_definitions)
|
||||||
return self._resolve(
|
return self._resolve(
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ class BuiltInFunction(Function, BuiltInFunctionType):
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
# pylint: disable=missing-raises-doc
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def callable(self) -> Callable[..., Resolvable]:
|
def callable(self) -> Callable[..., Resolvable]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -106,6 +108,8 @@ class BuiltInFunction(Function, BuiltInFunctionType):
|
||||||
# Should be validated in the parser
|
# Should be validated in the parser
|
||||||
raise UNREACHABLE from exc
|
raise UNREACHABLE from exc
|
||||||
|
|
||||||
|
# pylint: enable=missing-raises-doc
|
||||||
|
|
||||||
@functools.cached_property
|
@functools.cached_property
|
||||||
def function_spec(self) -> FunctionSpec:
|
def function_spec(self) -> FunctionSpec:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -114,8 +114,6 @@ class Hashable(Resolvable, ABC):
|
||||||
Resolvable type that can be used as hashes (i.e. in Maps)
|
Resolvable type that can be used as hashes (i.e. in Maps)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class NonHashable(NamedType, ABC):
|
class NonHashable(NamedType, ABC):
|
||||||
|
|
@ -123,8 +121,6 @@ class NonHashable(NamedType, ABC):
|
||||||
Type that is known to never be hashable.
|
Type that is known to never be hashable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ResolvableToJson(Resolvable, ABC):
|
class ResolvableToJson(Resolvable, ABC):
|
||||||
|
|
@ -151,32 +147,40 @@ class Numeric(ResolvableT[NumericT], ABC, Generic[NumericT]):
|
||||||
Resolvable numeric types (int/float)
|
Resolvable numeric types (int/float)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Integer(Numeric[int], Argument):
|
class Integer(Numeric[int], Argument):
|
||||||
pass
|
"""
|
||||||
|
Resolved Integer type
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Float(Numeric[float], Argument):
|
class Float(Numeric[float], Argument):
|
||||||
pass
|
"""
|
||||||
|
Resolved float type
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Boolean(ResolvableT[bool], Argument):
|
class Boolean(ResolvableT[bool], Argument):
|
||||||
pass
|
"""
|
||||||
|
Resolved bool type
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class String(ResolvableT[str], Argument):
|
class String(ResolvableT[str], Argument):
|
||||||
pass
|
"""
|
||||||
|
Resolved String type
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class NamedCustomFunction(NamedArgument, ABC):
|
class NamedCustomFunction(NamedArgument, ABC):
|
||||||
pass
|
"""
|
||||||
|
A custom function with a defined name (but unknown args)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|
@ -236,5 +240,3 @@ class LambdaReduce(LambdaTwo):
|
||||||
"""
|
"""
|
||||||
Type-hinting for functions that apply a reduce-operation using a lambda (two arguments)
|
Type-hinting for functions that apply a reduce-operation using a lambda (two arguments)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@ class FunctionArgument(Variable):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_idx(cls, idx: int, custom_function_name: Optional[str]) -> "FunctionArgument":
|
def from_idx(cls, idx: int, custom_function_name: Optional[str]) -> "FunctionArgument":
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
FunctionArgument whose variable name is the index, and optionally contains the custom
|
||||||
|
function name its defined in as a prefix.
|
||||||
|
"""
|
||||||
if custom_function_name:
|
if custom_function_name:
|
||||||
return FunctionArgument(name=f"${custom_function_name}___{idx}", index=idx)
|
return FunctionArgument(name=f"${custom_function_name}___{idx}", index=idx)
|
||||||
return FunctionArgument(name=f"${idx}", index=idx)
|
return FunctionArgument(name=f"${idx}", index=idx)
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,9 @@ class VariableDependency(ABC):
|
||||||
output.append(arg)
|
output.append(arg)
|
||||||
|
|
||||||
if isinstance(arg, VariableDependency):
|
if isinstance(arg, VariableDependency):
|
||||||
|
# pylint: disable=protected-access
|
||||||
output.extend(arg._recurse_get(ttype))
|
output.extend(arg._recurse_get(ttype))
|
||||||
|
# pylint: enable=protected-access
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
@ -83,6 +85,8 @@ class VariableDependency(ABC):
|
||||||
"""
|
"""
|
||||||
return set(self._recurse_get(Lambda, subclass=True))
|
return set(self._recurse_get(Lambda, subclass=True))
|
||||||
|
|
||||||
|
# pylint: disable=missing-raises-doc
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@property
|
@property
|
||||||
def custom_functions(self) -> Set[ParsedCustomFunction]:
|
def custom_functions(self) -> Set[ParsedCustomFunction]:
|
||||||
|
|
@ -105,6 +109,8 @@ class VariableDependency(ABC):
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
# pylint: enable=missing-raises-doc
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def resolve(
|
def resolve(
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
|
|
@ -130,11 +130,10 @@ class FunctionArgumentsExceptionFormatter:
|
||||||
for arg in self._input_args:
|
for arg in self._input_args:
|
||||||
if isinstance(arg, BuiltInFunctionType):
|
if isinstance(arg, BuiltInFunctionType):
|
||||||
if is_union(arg.output_type()):
|
if is_union(arg.output_type()):
|
||||||
received_type_names.append(
|
readable_type_names = ", ".join(
|
||||||
f"%{arg.name}(...)->Union["
|
sorted(type_.type_name() for type_ in arg.output_type().__args__)
|
||||||
f"{', '.join(sorted(type_.type_name() for type_ in arg.output_type().__args__))}"
|
|
||||||
f"]"
|
|
||||||
)
|
)
|
||||||
|
received_type_names.append(f"%{arg.name}(...)->Union[{readable_type_names}]")
|
||||||
else:
|
else:
|
||||||
received_type_names.append(f"%{arg.name}(...)->{arg.output_type().type_name()}")
|
received_type_names.append(f"%{arg.name}(...)->{arg.output_type().type_name()}")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,11 @@ class FunctionSpec:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _is_varargs_compatible(self, input_args: List[Argument]) -> bool:
|
def _is_varargs_compatible(self, input_args: List[Argument]) -> bool:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
True if the input args are compatible with the spec's varargs. False otherwise.
|
||||||
|
"""
|
||||||
assert self.varargs is not None
|
assert self.varargs is not None
|
||||||
|
|
||||||
for input_arg in input_args:
|
for input_arg in input_args:
|
||||||
|
|
@ -165,22 +170,42 @@ class FunctionSpec:
|
||||||
raise UNREACHABLE # TODO: functions with no args
|
raise UNREACHABLE # TODO: functions with no args
|
||||||
|
|
||||||
def is_num_args_compatible(self, num_input_args: int) -> bool:
|
def is_num_args_compatible(self, num_input_args: int) -> bool:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
True if the number of input args is compatible with the function spec. False otherwise.
|
||||||
|
"""
|
||||||
if self.args is not None:
|
if self.args is not None:
|
||||||
return self.num_required_args <= num_input_args <= len(self.args)
|
return self.num_required_args <= num_input_args <= len(self.args)
|
||||||
return True # varargs can take any number
|
return True # varargs can take any number
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def num_required_args(self) -> int:
|
def num_required_args(self) -> int:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
The minimum number of args required to call the function.
|
||||||
|
"""
|
||||||
if self.args is not None:
|
if self.args is not None:
|
||||||
return sum(1 for arg in self.args if not is_optional(arg))
|
return sum(1 for arg in self.args if not is_optional(arg))
|
||||||
return 0 # varargs can take any number
|
return 0 # varargs can take any number
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_lambda_reduce_function(self) -> Optional[Type[LambdaReduce]]:
|
def is_lambda_reduce_function(self) -> Optional[Type[LambdaReduce]]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
True if the function is a Lambda-reduce function. False otherwise.
|
||||||
|
"""
|
||||||
return LambdaReduce if LambdaReduce in (self.args or []) else None
|
return LambdaReduce if LambdaReduce in (self.args or []) else None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_lambda_function(self) -> Optional[Type[Lambda | LambdaTwo | LambdaThree]]:
|
def is_lambda_function(self) -> Optional[Type[Lambda | LambdaTwo | LambdaThree]]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
True if the function is a Lambda function (excluding reduce). False otherwise.
|
||||||
|
"""
|
||||||
if LambdaThree in (self.args or []):
|
if LambdaThree in (self.args or []):
|
||||||
return LambdaThree
|
return LambdaThree
|
||||||
if LambdaTwo in (self.args or []):
|
if LambdaTwo in (self.args or []):
|
||||||
|
|
@ -191,6 +216,11 @@ class FunctionSpec:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_lambda_like(self) -> Optional[Type[TLambda]]:
|
def is_lambda_like(self) -> Optional[Type[TLambda]]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
True if the function is a Lambda type (including reduce).
|
||||||
|
"""
|
||||||
if l_type := self.is_lambda_reduce_function:
|
if l_type := self.is_lambda_reduce_function:
|
||||||
return l_type
|
return l_type
|
||||||
if l_type := self.is_lambda_function:
|
if l_type := self.is_lambda_function:
|
||||||
|
|
@ -199,6 +229,11 @@ class FunctionSpec:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_callable(cls, callable_ref: Callable[..., Resolvable]) -> "FunctionSpec":
|
def from_callable(cls, callable_ref: Callable[..., Resolvable]) -> "FunctionSpec":
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
FunctionSpec from a built-in function.
|
||||||
|
"""
|
||||||
arg_spec: FullArgSpec = inspect.getfullargspec(callable_ref)
|
arg_spec: FullArgSpec = inspect.getfullargspec(callable_ref)
|
||||||
if arg_spec.varargs:
|
if arg_spec.varargs:
|
||||||
return FunctionSpec(
|
return FunctionSpec(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue