Coretex
base_parameter.py
1 # Copyright (C) 2023 Coretex LLC
2 
3 # This file is part of Coretex.ai
4 
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
14 
15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <https://www.gnu.org/licenses/>.
17 
18 from typing import Dict, Optional, Any, Tuple, List, TypeVar, Generic, Union
19 from abc import ABC, abstractmethod
20 
21 import logging
22 
23 from .parameter_type import ParameterType
24 from ...project import ProjectType
25 
26 
27 T = TypeVar("T")
28 
29 
30 class BaseParameter(ABC, Generic[T]):
31 
32  def __init__(
33  self,
34  name: str,
35  description: str,
36  value: Optional[T],
37  dataType: Union[str, ParameterType],
38  required: bool,
39  type: int = 3
40  ) -> None:
41 
42  if isinstance(dataType, str):
43  dataType = ParameterType(dataType)
44 
45  self.name = name
46  self.description = description
47  self.value = value
48  self.dataType = dataType
49  self.required = required
50  self.type_ = type
51 
52  @property
53  @abstractmethod
54  def types(self) -> List[type]:
55  pass
56 
57  def makeExceptionMessage(self) -> str:
58  expected = self.dataType.value
59  received = self.generateTypeDescription()
60 
61  return f"Parameter \"{self.name}\" has invalid type. Expected \"{expected}\", got \"{received}\""
62 
63  def validate(self) -> Tuple[bool, Optional[str]]:
64  if not self.required and self.value is None:
65  return True, None
66 
67  # bool is a subclass of int, do not allow validation to pass if
68  # we are looking for integer, but bool is received
69  if isinstance(self.value, bool) and int in self.types and not bool in self.types:
70  return False, None
71 
72  if not any(isinstance(self.value, dataType) for dataType in self.types):
73  return False, None
74 
75  return True, None
76 
77  def generateTypeDescription(self) -> str:
78  if not isinstance(self.value, list) or self.value is None:
79  return type(self.value).__name__
80 
81  elementTypes = ", ".join({type(value).__name__ for value in self.value})
82  return f"list[{elementTypes}]"
83 
84  def parseValue(self, type_: ProjectType) -> Optional[Any]:
85  return self.value
86 
87  def overrideValue(self, value: Optional[Any]) -> Optional[Any]:
88  return value
89 
90  def encode(self) -> Dict[str, Any]:
91  return {
92  "name": self.name,
93  "description": self.description,
94  "value": self.value,
95  "data_type": self.dataType.value,
96  "required": self.required
97  }
98 
99 
100 def validateParameters(parameters: List[BaseParameter], verbose: bool = True) -> Dict[str, Any]:
101  parameterValidationResults: Dict[str, bool] = {}
102 
103  for parameter in parameters:
104  isValid, message = parameter.validate()
105  if not isValid:
106  if message is None:
107  message = parameter.makeExceptionMessage()
108 
109  if verbose:
110  logging.getLogger("coretexpylib").fatal(f">> [Coretex] {message}")
111 
112  parameterValidationResults[parameter.name] = isValid
113 
114  return parameterValidationResults