wallaroo.object
1import abc 2from typing import Any, Dict, Optional, Union 3 4import gql # type: ignore 5 6 7class DehydratedValue(object): 8 """Represents a not-set sentinel value. 9 10 Attributes that are null in the database will be returned as None in Python, 11 and we want them to be set as such, so None cannot be used as a sentinel 12 value signaling that an optional attribute is not yet set. Objects of this 13 class fill that role instead. 14 """ 15 16 def __init__(self): 17 pass 18 19 def __eq__(self, other: Any): 20 """All DehydratedValue instances are equal.""" 21 return isinstance(other, DehydratedValue) 22 23 24def rehydrate(attr): 25 """Decorator that rehydrates the named attribute if needed. 26 27 This should decorate getter calls for an attribute: 28 29 ``` 30 @rehydrate(_foo_attr) 31 def foo_attr(self): 32 return self._foo_attr 33 ``` 34 35 This will cause the API object to "rehydrate" (perform a query to fetch and 36 fill in all attributes from the database) if the named attribute is not set. 37 """ 38 39 def decorator(fn): 40 def wrapper(*args, **kwargs): 41 obj = args[0] 42 if not getattr(obj, "_standalone", None): 43 present = getattr(obj, attr) != DehydratedValue() 44 # Uncomment to debug while testing 45 # print( 46 # "rehydrate: {} -> {}".format( 47 # attr, "present" if present else "not present" 48 # ) 49 # ) 50 if not present: 51 obj._rehydrate() 52 result = fn(*args, **kwargs) 53 return result 54 55 return wrapper 56 57 return decorator 58 59 60def value_if_present(data: Dict[str, Any], path: str) -> Union[Any, DehydratedValue]: 61 """Returns a value in a nested dictionary, or DehydratedValue. 62 63 :param str path: Dot-delimited path within a nested dictionary; e.g. 64 `foo.bar.baz` 65 :return: The requested value inside the dictionary, or DehydratedValue if it 66 doesn't exist. 67 """ 68 attrs = path.split(".") 69 current = data 70 for attr in attrs: 71 if attr in current: 72 current = current[attr] 73 else: 74 return DehydratedValue() 75 return current 76 77 78class RequiredAttributeMissing(Exception): 79 """Raised when an API object is initialized without a required attribute.""" 80 81 def __init__(self, class_name: str, attribute_name: str) -> None: 82 super().__init__( 83 "{}: Missing required attribute '{}' in response".format( 84 class_name, attribute_name 85 ) 86 ) 87 88 89class ModelUploadError(Exception): 90 """Raised when a model file fails to upload.""" 91 92 def __init__(self, e): 93 super().__init__("Model failed to upload: {}".format(e)) 94 95 96class EntityNotFoundError(Exception): 97 """Raised when a query for a specific API object returns no results. 98 99 This is specifically for queries by unique identifiers that are expected to 100 return exactly one result; queries that can return 0 to many results should 101 return empty list instead of raising this exception. 102 """ 103 104 def __init__(self, entity_type: str, params: Dict[str, str]): 105 super().__init__("{} not found: {}".format(entity_type, params)) 106 107 108class LimitError(Exception): 109 """Raised when deployment fails.""" 110 111 def __init__(self, e): 112 super().__init__( 113 "You have reached a license limit in your Wallaroo instance. In order to add additional resources, you can remove some of your existing resources. If you have any questions contact us at community@wallaroo.ai: {}".format( 114 e 115 ) 116 ) 117 118 119class DeploymentError(Exception): 120 """Raised when deployment fails.""" 121 122 def __init__(self, e): 123 super().__init__("Model failed to deploy: {}".format(e)) 124 125 126class InferenceError(Exception): 127 """Raised when inference fails.""" 128 129 def __init__(self, error: Dict[str, str]): 130 super().__init__( 131 "Inference failed: \n\t{}: {}".format(error["error_type"], error["error"]) 132 ), 133 134 135class InvalidNameError(Exception): 136 """Raised when an entity's name does not meet the expected critieria. 137 138 :param str name: the name string that is invalid 139 :param str req: a string description of the requirement 140 """ 141 142 def __init__(self, name: str, req: str): 143 super().__init__(f"Name '{name} is invalid: {req}") 144 145 146class CommunicationError(Exception): 147 """Raised when some component cannot be contacted. There is a networking, configuration or 148 installation problem. 149 """ 150 151 def __init__(self, e): 152 super().__init__("Network communication failed: {}".format(e)) 153 154 155class Object(abc.ABC): 156 """Base class for all backend GraphQL API objects. 157 158 This class serves as a framework for API objects to be constructed based on 159 a partially-complete JSON response, and to fill in their remaining members 160 dynamically if needed. 161 """ 162 163 def __init__( 164 self, gql_client: Optional[gql.Client], data: Dict[str, Any], standalone=False 165 ) -> None: 166 """Base constructor. 167 168 Each object requires: 169 * a GraphQL client - in order to fill its missing members dynamically 170 * an initial data blob - typically from unserialized JSON, contains at 171 * least the data for required 172 members (typically the object's primary key) and optionally other data 173 members. 174 """ 175 if gql_client is not None: 176 self._gql_client = gql_client 177 elif gql_client is None and not standalone: 178 raise RuntimeError( 179 "You must either use a client or standalone mode. You cannot mix and match." 180 ) 181 self._standalone = standalone 182 # Each object knows how to fill itself from the provided data. 183 self._fill(data) 184 185 def _rehydrate(self): 186 """Fetches all data for this object and fills in all attributes. 187 188 Each object knows how to: 189 * get data for all its attributes via the GraphQL API 190 * fill in those attributes given the unserialized JSON response 191 192 This method chains them together, so that it doesn't have to be done in 193 each object. 194 """ 195 self._fill(self._fetch_attributes()) 196 197 @abc.abstractmethod 198 def _fill(self, data: Dict[str, Any]) -> None: 199 """Fills an object given a response dictionary from the GraphQL API. 200 201 Only the primary key member must be present; other members will be 202 filled in via rehydration if their corresponding member function is 203 called. 204 """ 205 raise NotImplementedError() 206 207 @abc.abstractmethod 208 def _fetch_attributes(self) -> Dict[str, Any]: 209 """Fetches all member data from the GraphQL API.""" 210 raise NotImplementedError
8class DehydratedValue(object): 9 """Represents a not-set sentinel value. 10 11 Attributes that are null in the database will be returned as None in Python, 12 and we want them to be set as such, so None cannot be used as a sentinel 13 value signaling that an optional attribute is not yet set. Objects of this 14 class fill that role instead. 15 """ 16 17 def __init__(self): 18 pass 19 20 def __eq__(self, other: Any): 21 """All DehydratedValue instances are equal.""" 22 return isinstance(other, DehydratedValue)
Represents a not-set sentinel value.
Attributes that are null in the database will be returned as None in Python, and we want them to be set as such, so None cannot be used as a sentinel value signaling that an optional attribute is not yet set. Objects of this class fill that role instead.
25def rehydrate(attr): 26 """Decorator that rehydrates the named attribute if needed. 27 28 This should decorate getter calls for an attribute: 29 30 ``` 31 @rehydrate(_foo_attr) 32 def foo_attr(self): 33 return self._foo_attr 34 ``` 35 36 This will cause the API object to "rehydrate" (perform a query to fetch and 37 fill in all attributes from the database) if the named attribute is not set. 38 """ 39 40 def decorator(fn): 41 def wrapper(*args, **kwargs): 42 obj = args[0] 43 if not getattr(obj, "_standalone", None): 44 present = getattr(obj, attr) != DehydratedValue() 45 # Uncomment to debug while testing 46 # print( 47 # "rehydrate: {} -> {}".format( 48 # attr, "present" if present else "not present" 49 # ) 50 # ) 51 if not present: 52 obj._rehydrate() 53 result = fn(*args, **kwargs) 54 return result 55 56 return wrapper 57 58 return decorator
Decorator that rehydrates the named attribute if needed.
This should decorate getter calls for an attribute:
@rehydrate(_foo_attr)
def foo_attr(self):
return self._foo_attr
This will cause the API object to "rehydrate" (perform a query to fetch and fill in all attributes from the database) if the named attribute is not set.
61def value_if_present(data: Dict[str, Any], path: str) -> Union[Any, DehydratedValue]: 62 """Returns a value in a nested dictionary, or DehydratedValue. 63 64 :param str path: Dot-delimited path within a nested dictionary; e.g. 65 `foo.bar.baz` 66 :return: The requested value inside the dictionary, or DehydratedValue if it 67 doesn't exist. 68 """ 69 attrs = path.split(".") 70 current = data 71 for attr in attrs: 72 if attr in current: 73 current = current[attr] 74 else: 75 return DehydratedValue() 76 return current
Returns a value in a nested dictionary, or DehydratedValue.
Parameters
- str path: Dot-delimited path within a nested dictionary; e.g.
foo.bar.baz
Returns
The requested value inside the dictionary, or DehydratedValue if it doesn't exist.
79class RequiredAttributeMissing(Exception): 80 """Raised when an API object is initialized without a required attribute.""" 81 82 def __init__(self, class_name: str, attribute_name: str) -> None: 83 super().__init__( 84 "{}: Missing required attribute '{}' in response".format( 85 class_name, attribute_name 86 ) 87 )
Raised when an API object is initialized without a required attribute.
Inherited Members
- builtins.BaseException
- with_traceback
- args
90class ModelUploadError(Exception): 91 """Raised when a model file fails to upload.""" 92 93 def __init__(self, e): 94 super().__init__("Model failed to upload: {}".format(e))
Raised when a model file fails to upload.
Inherited Members
- builtins.BaseException
- with_traceback
- args
97class EntityNotFoundError(Exception): 98 """Raised when a query for a specific API object returns no results. 99 100 This is specifically for queries by unique identifiers that are expected to 101 return exactly one result; queries that can return 0 to many results should 102 return empty list instead of raising this exception. 103 """ 104 105 def __init__(self, entity_type: str, params: Dict[str, str]): 106 super().__init__("{} not found: {}".format(entity_type, params))
Raised when a query for a specific API object returns no results.
This is specifically for queries by unique identifiers that are expected to return exactly one result; queries that can return 0 to many results should return empty list instead of raising this exception.
Inherited Members
- builtins.BaseException
- with_traceback
- args
109class LimitError(Exception): 110 """Raised when deployment fails.""" 111 112 def __init__(self, e): 113 super().__init__( 114 "You have reached a license limit in your Wallaroo instance. In order to add additional resources, you can remove some of your existing resources. If you have any questions contact us at community@wallaroo.ai: {}".format( 115 e 116 ) 117 )
Raised when deployment fails.
Inherited Members
- builtins.BaseException
- with_traceback
- args
120class DeploymentError(Exception): 121 """Raised when deployment fails.""" 122 123 def __init__(self, e): 124 super().__init__("Model failed to deploy: {}".format(e))
Raised when deployment fails.
Inherited Members
- builtins.BaseException
- with_traceback
- args
127class InferenceError(Exception): 128 """Raised when inference fails.""" 129 130 def __init__(self, error: Dict[str, str]): 131 super().__init__( 132 "Inference failed: \n\t{}: {}".format(error["error_type"], error["error"]) 133 ),
Raised when inference fails.
Inherited Members
- builtins.BaseException
- with_traceback
- args
136class InvalidNameError(Exception): 137 """Raised when an entity's name does not meet the expected critieria. 138 139 :param str name: the name string that is invalid 140 :param str req: a string description of the requirement 141 """ 142 143 def __init__(self, name: str, req: str): 144 super().__init__(f"Name '{name} is invalid: {req}")
Raised when an entity's name does not meet the expected critieria.
Parameters
- str name: the name string that is invalid
- str req: a string description of the requirement
Inherited Members
- builtins.BaseException
- with_traceback
- args
147class CommunicationError(Exception): 148 """Raised when some component cannot be contacted. There is a networking, configuration or 149 installation problem. 150 """ 151 152 def __init__(self, e): 153 super().__init__("Network communication failed: {}".format(e))
Raised when some component cannot be contacted. There is a networking, configuration or installation problem.
Inherited Members
- builtins.BaseException
- with_traceback
- args
156class Object(abc.ABC): 157 """Base class for all backend GraphQL API objects. 158 159 This class serves as a framework for API objects to be constructed based on 160 a partially-complete JSON response, and to fill in their remaining members 161 dynamically if needed. 162 """ 163 164 def __init__( 165 self, gql_client: Optional[gql.Client], data: Dict[str, Any], standalone=False 166 ) -> None: 167 """Base constructor. 168 169 Each object requires: 170 * a GraphQL client - in order to fill its missing members dynamically 171 * an initial data blob - typically from unserialized JSON, contains at 172 * least the data for required 173 members (typically the object's primary key) and optionally other data 174 members. 175 """ 176 if gql_client is not None: 177 self._gql_client = gql_client 178 elif gql_client is None and not standalone: 179 raise RuntimeError( 180 "You must either use a client or standalone mode. You cannot mix and match." 181 ) 182 self._standalone = standalone 183 # Each object knows how to fill itself from the provided data. 184 self._fill(data) 185 186 def _rehydrate(self): 187 """Fetches all data for this object and fills in all attributes. 188 189 Each object knows how to: 190 * get data for all its attributes via the GraphQL API 191 * fill in those attributes given the unserialized JSON response 192 193 This method chains them together, so that it doesn't have to be done in 194 each object. 195 """ 196 self._fill(self._fetch_attributes()) 197 198 @abc.abstractmethod 199 def _fill(self, data: Dict[str, Any]) -> None: 200 """Fills an object given a response dictionary from the GraphQL API. 201 202 Only the primary key member must be present; other members will be 203 filled in via rehydration if their corresponding member function is 204 called. 205 """ 206 raise NotImplementedError() 207 208 @abc.abstractmethod 209 def _fetch_attributes(self) -> Dict[str, Any]: 210 """Fetches all member data from the GraphQL API.""" 211 raise NotImplementedError
Base class for all backend GraphQL API objects.
This class serves as a framework for API objects to be constructed based on a partially-complete JSON response, and to fill in their remaining members dynamically if needed.
164 def __init__( 165 self, gql_client: Optional[gql.Client], data: Dict[str, Any], standalone=False 166 ) -> None: 167 """Base constructor. 168 169 Each object requires: 170 * a GraphQL client - in order to fill its missing members dynamically 171 * an initial data blob - typically from unserialized JSON, contains at 172 * least the data for required 173 members (typically the object's primary key) and optionally other data 174 members. 175 """ 176 if gql_client is not None: 177 self._gql_client = gql_client 178 elif gql_client is None and not standalone: 179 raise RuntimeError( 180 "You must either use a client or standalone mode. You cannot mix and match." 181 ) 182 self._standalone = standalone 183 # Each object knows how to fill itself from the provided data. 184 self._fill(data)
Base constructor.
Each object requires:
- a GraphQL client - in order to fill its missing members dynamically
- an initial data blob - typically from unserialized JSON, contains at
- least the data for required members (typically the object's primary key) and optionally other data members.