biatoolkit 1.2.0__tar.gz → 1.2.1__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: biatoolkit
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Biblioteca para desenvolvedores que utilizam o BiaAgentBuilder
5
5
  Author: Bia Platform Team
6
6
  Author-email: data.platform@sankhya.com.br
File without changes
@@ -0,0 +1,175 @@
1
+ # biatoolkit/validation/coercion.py
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Iterable, Mapping, Optional
6
+ import math
7
+
8
+
9
+ def ensure_list(value: Any) -> list:
10
+ """
11
+ Normaliza um valor para sempre retornar uma lista.
12
+
13
+ Regras:
14
+ - None -> []
15
+ - list -> a própria lista
16
+ - tuple/set -> list(value)
17
+ - dict (ou Mapping) -> [value] (não "explode" dict em chaves)
18
+ - qualquer outro -> [value]
19
+
20
+ Por que existe:
21
+ - Muitas APIs retornam ora um item único (dict), ora uma lista.
22
+ - Este helper padroniza o consumo e reduz if/else espalhado.
23
+
24
+ Args:
25
+ value: qualquer valor.
26
+
27
+ Returns:
28
+ list: lista normalizada.
29
+ """
30
+ if value is None:
31
+ return []
32
+
33
+ if isinstance(value, list):
34
+ return value
35
+
36
+ if isinstance(value, (tuple, set)):
37
+ return list(value)
38
+
39
+ # Dict/Mapping deve ser tratado como item único, não iterável de chaves.
40
+ if isinstance(value, Mapping):
41
+ return [value]
42
+
43
+ return [value]
44
+
45
+
46
+ def unwrap_dollar_value(value: Any) -> Any:
47
+ """
48
+ "Desembrulha" valores no formato {"$": "..."} (comum em integrações legadas).
49
+
50
+ Exemplo:
51
+ {"$": "123"} -> "123"
52
+ {"$": 123} -> 123
53
+
54
+ Se não for dict com a chave "$", retorna o valor original.
55
+
56
+ Args:
57
+ value: valor de entrada.
58
+
59
+ Returns:
60
+ Any: valor desembrulhado ou original.
61
+ """
62
+ if isinstance(value, dict) and "$" in value:
63
+ return value.get("$")
64
+ return value
65
+
66
+
67
+ def to_int(value: Any, default: int = 0) -> int:
68
+ """
69
+ Converte um valor para int de forma segura.
70
+
71
+ Regras:
72
+ - None / "" -> default
73
+ - strings com espaços são aceitas (" 12 ")
74
+ - strings numéricas com sinal são aceitas ("-3")
75
+ - floats numéricos -> int(value) (trunca)
76
+ - NaN/inf -> default
77
+
78
+ Obs:
79
+ - Não tenta "extrair número do meio do texto" (isso é responsabilidade
80
+ de parse específico, ex: parse_int_list).
81
+
82
+ Args:
83
+ value: valor a converter.
84
+ default: fallback.
85
+
86
+ Returns:
87
+ int: convertido ou default.
88
+ """
89
+ try:
90
+ if value is None:
91
+ return default
92
+
93
+ if isinstance(value, bool):
94
+ # Evita True->1 / False->0 de forma "surpresa"
95
+ return default
96
+
97
+ if isinstance(value, str):
98
+ s = value.strip()
99
+ if s == "":
100
+ return default
101
+ return int(s)
102
+
103
+ if isinstance(value, float):
104
+ if math.isnan(value) or math.isinf(value):
105
+ return default
106
+ return int(value)
107
+
108
+ return int(value)
109
+ except (TypeError, ValueError):
110
+ return default
111
+
112
+
113
+ def to_float(value: Any, default: float = 0.0) -> float:
114
+ """
115
+ Converte um valor para float de forma segura.
116
+
117
+ Regras:
118
+ - None / "" -> default
119
+ - aceita strings com vírgula decimal ("12,34")
120
+ - NaN/inf -> default
121
+
122
+ Args:
123
+ value: valor a converter.
124
+ default: fallback.
125
+
126
+ Returns:
127
+ float: convertido ou default.
128
+ """
129
+ try:
130
+ if value is None:
131
+ return default
132
+
133
+ if isinstance(value, bool):
134
+ return default
135
+
136
+ if isinstance(value, str):
137
+ s = value.strip()
138
+ if s == "":
139
+ return default
140
+ s = s.replace(",", ".")
141
+ v = float(s)
142
+ else:
143
+ v = float(value)
144
+
145
+ if math.isnan(v) or math.isinf(v):
146
+ return default
147
+
148
+ return v
149
+ except (TypeError, ValueError):
150
+ return default
151
+
152
+
153
+
154
+
155
+ # Classe fachada para coerção
156
+ class BiaCoercion:
157
+ """
158
+ Fachada estática para funções de coerção do Bia Toolkit.
159
+ Permite referenciar e utilizar as utilidades de coerção de forma padronizada.
160
+ """
161
+ @staticmethod
162
+ def ensure_list(*args, **kwargs):
163
+ return ensure_list(*args, **kwargs)
164
+
165
+ @staticmethod
166
+ def unwrap_dollar_value(*args, **kwargs):
167
+ return unwrap_dollar_value(*args, **kwargs)
168
+
169
+ @staticmethod
170
+ def to_int(*args, **kwargs):
171
+ return to_int(*args, **kwargs)
172
+
173
+ @staticmethod
174
+ def to_float(*args, **kwargs):
175
+ return to_float(*args, **kwargs)
@@ -0,0 +1,135 @@
1
+ # biatoolkit/validation.py
2
+
3
+ from typing import Any, Iterable
4
+ import re
5
+
6
+
7
+ def parse_int_list(
8
+ value: Any,
9
+ *,
10
+ dedupe: bool = True,
11
+ keep_order: bool = True,
12
+ ) -> list[int]:
13
+ """
14
+ Normaliza um valor arbitrário em uma lista de inteiros.
15
+
16
+ Casos suportados:
17
+ - None -> []
18
+ - int -> [int]
19
+ - str -> extrai números (ex: "100, 200" / "SKU=300231 x2")
20
+ - list/tuple -> processa cada item recursivamente
21
+
22
+ Comportamento:
23
+ - Ignora valores inválidos
24
+ - Deduplica por padrão
25
+ - Mantém a ordem de aparição por padrão
26
+
27
+ Args:
28
+ value: Valor de entrada (None, int, str, list, etc.)
29
+ dedupe: Remove valores duplicados.
30
+ keep_order: Mantém a ordem original dos valores.
31
+
32
+ Returns:
33
+ list[int]: Lista normalizada de inteiros.
34
+ """
35
+ if value is None:
36
+ return []
37
+
38
+ # Normaliza para iterável
39
+ if isinstance(value, (list, tuple, set)):
40
+ raw: Iterable[Any] = value
41
+ else:
42
+ raw = [value]
43
+
44
+ numbers: list[int] = []
45
+
46
+ for item in raw:
47
+ if item is None:
48
+ continue
49
+
50
+ # Inteiro direto
51
+ if isinstance(item, int):
52
+ numbers.append(item)
53
+ continue
54
+
55
+ # String ou outros tipos
56
+ text = str(item)
57
+ matches = re.findall(r"\d+", text)
58
+ for m in matches:
59
+ try:
60
+ numbers.append(int(m))
61
+ except ValueError:
62
+ continue
63
+
64
+ if not dedupe:
65
+ return numbers
66
+
67
+ if keep_order:
68
+ seen = set()
69
+ ordered: list[int] = []
70
+ for n in numbers:
71
+ if n not in seen:
72
+ seen.add(n)
73
+ ordered.append(n)
74
+ return ordered
75
+
76
+ return list(set(numbers))
77
+
78
+ def sanitize_like(
79
+ value: str,
80
+ *,
81
+ max_len: int = 80,
82
+ upper: bool = True,
83
+ ) -> str:
84
+ """
85
+ Sanitiza um texto para uso seguro em filtros do tipo LIKE/search.
86
+
87
+ Regras aplicadas:
88
+ - Remove caracteres fora de uma allowlist básica
89
+ - Limita o tamanho do texto
90
+ - Escapa aspas simples
91
+ - Escapa curingas comuns (% e _)
92
+ - Converte para UPPER por padrão
93
+
94
+ Args:
95
+ value: Texto de entrada.
96
+ max_len: Tamanho máximo permitido.
97
+ upper: Converte o texto final para maiúsculas.
98
+
99
+ Returns:
100
+ str: Texto sanitizado.
101
+ """
102
+ if not value:
103
+ return ""
104
+
105
+ # Normaliza e corta tamanho
106
+ text = str(value).strip()[:max_len]
107
+
108
+ # Allowlist simples (letras, números, acentos, espaço e alguns símbolos, incluindo ', %, _)
109
+ # Mantém ', %, _ para escapá-los depois
110
+ text = re.sub(r"[^0-9A-Za-zÀ-ÿ\s\-\(\)\./'_%]", " ", text)
111
+
112
+ # Escapes básicos
113
+ text = text.replace("'", "''")
114
+ text = text.replace("%", r"\%" ).replace("_", r"\_")
115
+
116
+ # Normaliza espaços
117
+ text = re.sub(r"\s+", " ", text).strip()
118
+
119
+
120
+ return text.upper() if upper else text
121
+
122
+
123
+ # Classe fachada para validação
124
+ class BiaValidation:
125
+ """
126
+ Fachada estática para funções de validação do Bia Toolkit.
127
+ Permite referenciar e utilizar as utilidades de validação de forma padronizada.
128
+ """
129
+ @staticmethod
130
+ def parse_int_list(*args, **kwargs):
131
+ return parse_int_list(*args, **kwargs)
132
+
133
+ @staticmethod
134
+ def sanitize_like(*args, **kwargs):
135
+ return sanitize_like(*args, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: biatoolkit
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Biblioteca para desenvolvedores que utilizam o BiaAgentBuilder
5
5
  Author: Bia Platform Team
6
6
  Author-email: data.platform@sankhya.com.br
@@ -11,4 +11,7 @@ biatoolkit.egg-info/SOURCES.txt
11
11
  biatoolkit.egg-info/dependency_links.txt
12
12
  biatoolkit.egg-info/top_level.txt
13
13
  biatoolkit/schema/__init__.py
14
- biatoolkit/schema/header.py
14
+ biatoolkit/schema/header.py
15
+ biatoolkit/validation/__init__.py
16
+ biatoolkit/validation/coercion.py
17
+ biatoolkit/validation/validation.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='biatoolkit',
5
- version='1.2.0',
5
+ version='1.2.1',
6
6
  packages=find_packages(),
7
7
  install_requires=[],
8
8
  author='Bia Platform Team',
File without changes
File without changes