Spaces:
Runtime error
Runtime error
File size: 4,759 Bytes
c19ca42 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import List, Literal, Optional, Type, TypedDict, Union, TYPE_CHECKING
from base_types import InputId
if TYPE_CHECKING:
import navi
InputKind = Literal[
"number",
"slider",
"dropdown",
"text",
"directory",
"file",
"color",
"generic",
]
@dataclass
class InputConversion:
"""
An input conversion can be used to convert the assigned type of an input.
This is useful to model the changes `enforce` makes to values.
`type` is used to declare which type is intended to be converted by this
conversion. `convert` is the expression that does the actual conversion. It
will be given a special parameter called `Input` that will be the value to
convert. The `Input` parameter is guaranteed to be a non-empty sub type of
`type`.
Example:
To convert all numbers to string, use this conversions:
```
InputConversion("number", "toString(Input)")
```
"""
type: navi.ExpressionJson
convert: navi.ExpressionJson
def toDict(self):
return {
"type": self.type,
"convert": self.convert,
}
class LiteralErrorValue(TypedDict):
type: Literal["literal"]
value: str | int | float | None
class FormattedErrorValue(TypedDict):
type: Literal["formatted"]
formatString: str
class UnknownErrorValue(TypedDict):
type: Literal["unknown"]
typeName: str
typeModule: str
ErrorValue = Union[LiteralErrorValue, FormattedErrorValue, UnknownErrorValue]
class BaseInput:
def __init__(
self,
input_type: navi.ExpressionJson,
label: str,
kind: InputKind = "generic",
has_handle=True,
associated_type: Union[Type, None] = None,
):
self.input_type: navi.ExpressionJson = input_type
self.input_conversions: List[InputConversion] = []
self.input_adapt: navi.ExpressionJson | None = None
self.type_definitions: str | None = None
self.kind: InputKind = kind
self.label: str = label
self.optional: bool = False
self.has_handle: bool = has_handle
self.id: InputId = InputId(-1)
self.associated_type: Type = associated_type
# Optional documentation
self.description: str | None = None
self.hint: bool = False
# This is the method that should be created by each input
def enforce(self, value: object):
"""Enforce the input type"""
return value
# This is the method that should be called by the processing code
def enforce_(self, value: object | None):
if self.optional and value is None:
return None
assert value is not None, (
f"Expected value to exist, "
f"but does not exist for {self.kind} input with type {self.input_type} and label {self.label}"
)
return self.enforce(value)
def get_error_value(self, value: object) -> ErrorValue:
if isinstance(value, Enum):
# unwrap enum
value = value.value
if isinstance(value, bool):
# bools need to be 0 or 1
return {"type": "literal", "value": int(value)}
if isinstance(value, (int, float, str)) or value is None:
return {"type": "literal", "value": value}
return {
"type": "unknown",
"typeName": type(value).__qualname__,
"typeModule": type(value).__module__,
}
def toDict(self):
actual_type = [self.input_type, "null"] if self.optional else self.input_type
return {
"id": self.id,
"type": actual_type,
"conversions": [c.toDict() for c in self.input_conversions],
"adapt": self.input_adapt,
"typeDefinitions": self.type_definitions,
"kind": self.kind,
"label": self.label,
"optional": self.optional,
"hasHandle": self.has_handle,
"description": self.description,
"hint": self.hint,
}
def with_id(self, input_id: InputId | int):
self.id = InputId(input_id)
return self
def with_docs(self, *description: str, hint=False):
self.description = "\n\n".join(description)
self.hint = hint
return self
def make_optional(self):
self.optional = True
if self.associated_type is not None:
associated_type = self.associated_type
self.associated_type = Optional[associated_type]
return self
def __repr__(self):
return str(self.toDict())
def __iter__(self):
yield from self.toDict().items()
|