diff --git a/src/ytdl_sub/script/functions/__init__.py b/src/ytdl_sub/script/functions/__init__.py index bda1216e..5b98d24b 100644 --- a/src/ytdl_sub/script/functions/__init__.py +++ b/src/ytdl_sub/script/functions/__init__.py @@ -68,6 +68,11 @@ class Functions( ---------- function 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__): raise ValueError( diff --git a/src/ytdl_sub/script/script.py b/src/ytdl_sub/script/script.py index 33ca2e1b..8e6302e3 100644 --- a/src/ytdl_sub/script/script.py +++ b/src/ytdl_sub/script/script.py @@ -405,6 +405,24 @@ class Script: resolved: Optional[Dict[str, Resolvable]] = None, unresolvable: Optional[Set[str]] = None, ) -> 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: self.add(variable_definitions) return self._resolve( diff --git a/src/ytdl_sub/script/types/function.py b/src/ytdl_sub/script/types/function.py index 23389296..94bbad5d 100644 --- a/src/ytdl_sub/script/types/function.py +++ b/src/ytdl_sub/script/types/function.py @@ -93,6 +93,8 @@ class BuiltInFunction(Function, BuiltInFunctionType): return self + # pylint: disable=missing-raises-doc + @property def callable(self) -> Callable[..., Resolvable]: """ @@ -106,6 +108,8 @@ class BuiltInFunction(Function, BuiltInFunctionType): # Should be validated in the parser raise UNREACHABLE from exc + # pylint: enable=missing-raises-doc + @functools.cached_property def function_spec(self) -> FunctionSpec: """ diff --git a/src/ytdl_sub/script/types/resolvable.py b/src/ytdl_sub/script/types/resolvable.py index 50c57ff3..767d2ed9 100644 --- a/src/ytdl_sub/script/types/resolvable.py +++ b/src/ytdl_sub/script/types/resolvable.py @@ -114,8 +114,6 @@ class Hashable(Resolvable, ABC): Resolvable type that can be used as hashes (i.e. in Maps) """ - pass - @dataclass(frozen=True) class NonHashable(NamedType, ABC): @@ -123,8 +121,6 @@ class NonHashable(NamedType, ABC): Type that is known to never be hashable. """ - pass - @dataclass(frozen=True) class ResolvableToJson(Resolvable, ABC): @@ -151,32 +147,40 @@ class Numeric(ResolvableT[NumericT], ABC, Generic[NumericT]): Resolvable numeric types (int/float) """ - pass - @dataclass(frozen=True) class Integer(Numeric[int], Argument): - pass + """ + Resolved Integer type + """ @dataclass(frozen=True) class Float(Numeric[float], Argument): - pass + """ + Resolved float type + """ @dataclass(frozen=True) class Boolean(ResolvableT[bool], Argument): - pass + """ + Resolved bool type + """ @dataclass(frozen=True) class String(ResolvableT[str], Argument): - pass + """ + Resolved String type + """ @dataclass(frozen=True) class NamedCustomFunction(NamedArgument, ABC): - pass + """ + A custom function with a defined name (but unknown args) + """ @dataclass(frozen=True) @@ -236,5 +240,3 @@ class LambdaReduce(LambdaTwo): """ Type-hinting for functions that apply a reduce-operation using a lambda (two arguments) """ - - pass diff --git a/src/ytdl_sub/script/types/variable.py b/src/ytdl_sub/script/types/variable.py index 1698803c..5471ec30 100644 --- a/src/ytdl_sub/script/types/variable.py +++ b/src/ytdl_sub/script/types/variable.py @@ -17,6 +17,12 @@ class FunctionArgument(Variable): @classmethod 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: return FunctionArgument(name=f"${custom_function_name}___{idx}", index=idx) return FunctionArgument(name=f"${idx}", index=idx) diff --git a/src/ytdl_sub/script/types/variable_dependency.py b/src/ytdl_sub/script/types/variable_dependency.py index b96753fe..37215a13 100644 --- a/src/ytdl_sub/script/types/variable_dependency.py +++ b/src/ytdl_sub/script/types/variable_dependency.py @@ -39,7 +39,9 @@ class VariableDependency(ABC): output.append(arg) if isinstance(arg, VariableDependency): + # pylint: disable=protected-access output.extend(arg._recurse_get(ttype)) + # pylint: enable=protected-access return output @@ -83,6 +85,8 @@ class VariableDependency(ABC): """ return set(self._recurse_get(Lambda, subclass=True)) + # pylint: disable=missing-raises-doc + @final @property def custom_functions(self) -> Set[ParsedCustomFunction]: @@ -105,6 +109,8 @@ class VariableDependency(ABC): return output + # pylint: enable=missing-raises-doc + @abstractmethod def resolve( self, diff --git a/src/ytdl_sub/script/utils/exception_formatters.py b/src/ytdl_sub/script/utils/exception_formatters.py index 7cda126a..fb3b5e6e 100644 --- a/src/ytdl_sub/script/utils/exception_formatters.py +++ b/src/ytdl_sub/script/utils/exception_formatters.py @@ -130,11 +130,10 @@ class FunctionArgumentsExceptionFormatter: for arg in self._input_args: if isinstance(arg, BuiltInFunctionType): if is_union(arg.output_type()): - received_type_names.append( - f"%{arg.name}(...)->Union[" - f"{', '.join(sorted(type_.type_name() for type_ in arg.output_type().__args__))}" - f"]" + readable_type_names = ", ".join( + sorted(type_.type_name() for type_ in arg.output_type().__args__) ) + received_type_names.append(f"%{arg.name}(...)->Union[{readable_type_names}]") else: received_type_names.append(f"%{arg.name}(...)->{arg.output_type().type_name()}") else: diff --git a/src/ytdl_sub/script/utils/type_checking.py b/src/ytdl_sub/script/utils/type_checking.py index 2c2ccf7c..bac63c00 100644 --- a/src/ytdl_sub/script/utils/type_checking.py +++ b/src/ytdl_sub/script/utils/type_checking.py @@ -143,6 +143,11 @@ class FunctionSpec: return True 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 for input_arg in input_args: @@ -165,22 +170,42 @@ class FunctionSpec: raise UNREACHABLE # TODO: functions with no args 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: return self.num_required_args <= num_input_args <= len(self.args) return True # varargs can take any number @property def num_required_args(self) -> int: + """ + Returns + ------- + The minimum number of args required to call the function. + """ if self.args is not None: return sum(1 for arg in self.args if not is_optional(arg)) return 0 # varargs can take any number @property 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 @property 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 []): return LambdaThree if LambdaTwo in (self.args or []): @@ -191,6 +216,11 @@ class FunctionSpec: @property 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: return l_type if l_type := self.is_lambda_function: @@ -199,6 +229,11 @@ class FunctionSpec: @classmethod def from_callable(cls, callable_ref: Callable[..., Resolvable]) -> "FunctionSpec": + """ + Returns + ------- + FunctionSpec from a built-in function. + """ arg_spec: FullArgSpec = inspect.getfullargspec(callable_ref) if arg_spec.varargs: return FunctionSpec(