datavalue 0.1.6__tar.gz → 0.1.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datavalue
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: Librería de tipos de datos primitivos y complejos
5
5
  Author: Specter
6
6
  Requires-Python: >=3.10
@@ -2,6 +2,7 @@
2
2
  from typing import Type, Optional, Any, Iterable
3
3
  from .. import exceptions
4
4
  from .primitive_data import PrimitiveData
5
+ import json
5
6
 
6
7
  # Classes definition
7
8
  class ComplexData:
@@ -95,7 +96,134 @@ class ComplexData:
95
96
 
96
97
  return True
97
98
 
99
+ @classmethod
100
+ def _serialize_recursive(cls, element: Any) -> Any:
101
+ # 1. Caso: Instancias de tus clases
102
+ if isinstance(element, (PrimitiveData, ComplexData)):
103
+ return {
104
+ "__type__": element.__class__.__name__,
105
+ "content": element.to_dict()
106
+ }
107
+
108
+ # 2. Caso: Tipos de datos (clases como str, int, dict)
109
+ if isinstance(element, type):
110
+ return {"__class__": element.__name__}
111
+
112
+ # 3. Caso: Colecciones estándar (recursión profunda)
113
+ if isinstance(element, (list, tuple, set, frozenset)):
114
+ return [cls._serialize_recursive(i) for i in element]
115
+
116
+ if isinstance(element, dict):
117
+ return {str(k): cls._serialize_recursive(v) for k, v in element.items()}
118
+
119
+ # 4. Caso: Literales serializables (int, str, float, bool, None)
120
+ return element
121
+
122
+ @classmethod
123
+ def _deserialize_recursive(cls, element: Any) -> Any:
124
+ SAFE_TYPES = {
125
+ "list": list, "tuple": tuple, "set": set, "frozenset": frozenset,
126
+ "dict": dict, "str": str, "int": int, "float": float, "bool": bool,
127
+ "bytes": bytes, "bytearray": bytearray
128
+ }
129
+
130
+ # A. Manejo de Diccionarios (Estructuras u Objetos Serializados)
131
+ if isinstance(element, dict):
132
+ # Caso 1: Es un objeto serializado (PrimitiveData o ComplexData)
133
+ if "__type__" in element:
134
+ obj_type = element["__type__"]
135
+ content = element["content"]
136
+
137
+ if obj_type == "PrimitiveData":
138
+ return PrimitiveData.from_dict(content)
139
+ elif obj_type == "ComplexData":
140
+ return cls.from_dict(content)
141
+ else:
142
+ raise ValueError(f"Unknown serialized object type: {obj_type}")
143
+
144
+ # Caso 2: Es una referencia a un tipo de clase (__class__)
145
+ if "__class__" in element:
146
+ type_name = element["__class__"]
147
+ if type_name in SAFE_TYPES:
148
+ return SAFE_TYPES[type_name]
149
+ raise ValueError(f"Type '{type_name}' is not allowed or unknown.")
150
+
151
+ # Caso 3: Es un diccionario de datos común (recurse keys & values)
152
+ return {k: cls._deserialize_recursive(v) for k, v in element.items()}
153
+
154
+ # B. Manejo de Listas (recurse items)
155
+ if isinstance(element, list):
156
+ return [cls._deserialize_recursive(item) for item in element]
157
+
158
+ # C. Literales (retorno directo)
159
+ return element
160
+
161
+
98
162
  # Public methods
163
+ def to_dict(self) -> dict:
164
+ return {
165
+ "DATA_TYPE":self.data_type.__name__ if hasattr(self.data_type, '__name__') else str(self.data_type),
166
+ "VALUE":self._serialize_recursive(self.value),
167
+ "MAXIMUM_LENGTH":self.maximum_length,
168
+ "MINIMUM_LENGTH":self.minimum_length,
169
+ "POSSIBLE_VALUES":self._serialize_recursive(self.possible_values) if self.possible_values is not None else None,
170
+ "DATA_CLASS":self.data_class,
171
+ "__type__":"ComplexData"
172
+ }
173
+
174
+
175
+ @classmethod
176
+ def from_dict(cls, data: dict) -> 'ComplexData':
177
+ # 1. Secure types mapping
178
+ SAFE_TYPES = {
179
+ "list": list, "tuple": tuple, "set": set, "frozenset": frozenset,
180
+ "dict": dict, "str": str, "int": int, "float": float, "bool": bool,
181
+ "bytes": bytes, "bytearray": bytearray
182
+ }
183
+
184
+ # 3. Validación y Reconstrucción del Root
185
+ # Recuperamos el tipo de dato principal
186
+ raw_type = data.get("DATA_TYPE")
187
+ data_type = SAFE_TYPES.get(raw_type)
188
+ if not data_type:
189
+ raise TypeError(f"Invalid root data type: {raw_type}")
190
+
191
+ # Procesamos los possible_values con el motor recursivo
192
+ raw_possible = data.get("POSSIBLE_VALUES")
193
+ possible_values = cls._deserialize_recursive(raw_possible) if raw_possible is not None else None
194
+
195
+ # Corrección de tipo para tuplas (JSON no tiene tuplas, devuelve listas)
196
+ # Si su __init__ es estricto y requiere tupla para possible_values, convertimos aquí:
197
+ if isinstance(possible_values, list) and data_type != dict:
198
+ possible_values = tuple(possible_values)
199
+
200
+ # Para dicts, mantenemos la lista de listas o convertimos según su preferencia estricta
201
+ if isinstance(possible_values, list) and data_type == dict:
202
+ # Opcional: convertir sub-listas a tuplas si su validador lo prefiere,
203
+ # aunque su validación actual acepta listas.
204
+ pass
205
+
206
+ return cls(
207
+ data_type=data_type,
208
+ value=data.get("VALUE"), # Asumimos valor literal o serializable simple
209
+ maximum_length=data.get("MAXIMUM_LENGTH"),
210
+ minimum_length=data.get("MINIMUM_LENGTH"),
211
+ possible_values=possible_values,
212
+ data_class=data.get("DATA_CLASS", False)
213
+ )
214
+
215
+
216
+ @classmethod
217
+ def from_json(cls, text_content: str) -> 'ComplexData':
218
+ try:
219
+ data = json.loads(text_content)
220
+ except json.JSONDecodeError as e:
221
+ raise ValueError(f"Invalid JSON: {e}")
222
+ return cls.from_dict(data)
223
+
224
+ def to_json(self) -> str:
225
+ return json.dumps(self.to_dict(), indent=4)
226
+
99
227
  def validate(self, data: Any = None) -> bool:
100
228
  # Determine objective data
101
229
  if data is None:
@@ -53,21 +53,66 @@ class PrimitiveData:
53
53
  "MINIMUM_SIZE":self.minimum_size,
54
54
  "POSSIBLE_VALUES":self.possible_values if self.possible_values is not None else None,
55
55
  "REGULAR_EXPRESSION":self.regular_expression,
56
- "DATA_CLASS":self.data_class
56
+ "DATA_CLASS":self.data_class,
57
+ "__type__":"PrimitiveData"
57
58
  }
58
59
 
59
60
  def to_json(self) -> str:
60
61
  return json.dumps(self.to_dict(), indent=4)
62
+
63
+ @classmethod
64
+ def from_dict(cls, data: dict) -> 'PrimitiveData':
65
+ # Expected keys definition
66
+ expected_keys = {
67
+ "DATA_TYPE", "VALUE", "MAXIMUM_LENGTH", "MINIMUM_LENGTH",
68
+ "MAXIMUM_SIZE", "MINIMUM_SIZE", "POSSIBLE_VALUES",
69
+ "REGULAR_EXPRESSION", "DATA_CLASS", "__type__"
70
+ }
71
+
72
+ # Verify unknown keys on the table
73
+ unknown_keys = set(data.keys()) - expected_keys
74
+ if unknown_keys:
75
+ raise ValueError(f"Unknown keys in data structure: {unknown_keys}")
61
76
 
62
- def from_json(self, text_content: str) -> dict:
63
- data_table = json.loads(text_content)
64
- local_table = self.to_dict()
77
+ # Secure type mapping
78
+ type_mapping = {
79
+ "str": str,
80
+ "int": int,
81
+ "float": float,
82
+ "bool": bool,
83
+ "bytes": bytes,
84
+ "bytearray": bytearray,
85
+ "NoneType": type(None)
86
+ }
65
87
 
66
- for element in data_table:
67
- if element not in local_table: raise ValueError(f"The loaded table has a unknown value: {element}")
88
+ type_str = data.get("DATA_TYPE")
89
+ real_type = type_mapping.get(type_str)
68
90
 
69
- return data_table
91
+ if real_type is None and type_str != "None":
92
+ raise TypeError(f"Unsupported or unsafe data type for deserialization: {type_str}")
93
+
94
+ # return instance result
95
+ return cls(
96
+ data_type=real_type,
97
+ value=data.get("VALUE"),
98
+ maximum_length=data.get("MAXIMUM_LENGTH"),
99
+ minimum_length=data.get("MINIMUM_LENGTH"),
100
+ maximum_size=data.get("MAXIMUM_SIZE"),
101
+ minimum_size=data.get("MINIMUM_SIZE"),
102
+ possible_values=data.get("POSSIBLE_VALUES"),
103
+ regular_expression=data.get("REGULAR_EXPRESSION"),
104
+ data_class=data.get("DATA_CLASS", False)
105
+ )
70
106
 
107
+ @classmethod
108
+ def from_json(cls, text_content: str) -> 'PrimitiveData':
109
+ try:
110
+ data_table = json.loads(text_content)
111
+ except json.JSONDecodeError as Error:
112
+ raise ValueError(f"Invalid JSON format: {Error}")
113
+
114
+ return cls.from_dict(data_table)
115
+
71
116
  def validate(self, data: Optional[Any] = None) -> bool:
72
117
  # Define the data to validate
73
118
  if data is None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datavalue
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: Librería de tipos de datos primitivos y complejos
5
5
  Author: Specter
6
6
  Requires-Python: >=3.10
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "datavalue"
7
- version = "0.1.6"
7
+ version = "0.1.8"
8
8
  description = "Librería de tipos de datos primitivos y complejos"
9
9
  readme = "README.md"
10
10
  authors = [{ name="Specter" }]
File without changes
File without changes