table-stream 0.1.4__tar.gz → 0.1.6__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.
Files changed (46) hide show
  1. {table_stream-0.1.4 → table_stream-0.1.6}/PKG-INFO +2 -4
  2. table_stream-0.1.6/pyproject.toml +22 -0
  3. table_stream-0.1.6/table_stream/__init__.py +12 -0
  4. table_stream-0.1.4/table_stream/erros/__init__.py → table_stream-0.1.6/table_stream/erros.py +12 -0
  5. table_stream-0.1.6/table_stream/hash_map.py +307 -0
  6. table_stream-0.1.6/table_stream/io.py +228 -0
  7. table_stream-0.1.6/table_stream/notifier.py +170 -0
  8. table_stream-0.1.6/table_stream/pattner/__init__.py +7 -0
  9. table_stream-0.1.6/table_stream/pattner/adapter.py +40 -0
  10. table_stream-0.1.4/table_stream/types/pattner/observer/__init__.py → table_stream-0.1.6/table_stream/pattner/observer.py +62 -46
  11. table_stream-0.1.6/table_stream/sheet/__init__.py +10 -0
  12. table_stream-0.1.6/table_stream/sheet/check_mod.py +9 -0
  13. table_stream-0.1.6/table_stream/sheet/load.py +340 -0
  14. table_stream-0.1.6/table_stream/sheet/options.py +73 -0
  15. table_stream-0.1.4/table_stream/sheet/parse/__init__.py → table_stream-0.1.6/table_stream/sheet/parse.py +94 -70
  16. table_stream-0.1.6/table_stream/sheet/save.py +168 -0
  17. table_stream-0.1.6/table_stream/state.py +284 -0
  18. table_stream-0.1.6/table_stream/sysfiles.py +529 -0
  19. table_stream-0.1.6/table_stream/thread.py +138 -0
  20. table_stream-0.1.4/table_stream/types/workbook/__init__.py → table_stream-0.1.6/table_stream/workbook.py +1 -2
  21. {table_stream-0.1.4 → table_stream-0.1.6}/table_stream.egg-info/PKG-INFO +2 -4
  22. table_stream-0.1.6/table_stream.egg-info/SOURCES.txt +25 -0
  23. table_stream-0.1.6/table_stream.egg-info/requires.txt +2 -0
  24. table_stream-0.1.4/pyproject.toml +0 -41
  25. table_stream-0.1.4/table_stream/__init__.py +0 -11
  26. table_stream-0.1.4/table_stream/base/__init__.py +0 -2
  27. table_stream-0.1.4/table_stream/base/hash_map.py +0 -160
  28. table_stream-0.1.4/table_stream/base/mapping.py +0 -153
  29. table_stream-0.1.4/table_stream/sheet/__init__.py +0 -10
  30. table_stream-0.1.4/table_stream/sheet/csv/__init__.py +0 -97
  31. table_stream-0.1.4/table_stream/sheet/excel/__init__.py +0 -45
  32. table_stream-0.1.4/table_stream/sheet/excel/_col_index.py +0 -25
  33. table_stream-0.1.4/table_stream/sheet/interface/__init__.py +0 -63
  34. table_stream-0.1.4/table_stream/sheet/load_adapter/__init__.py +0 -64
  35. table_stream-0.1.4/table_stream/sheet/ods/__init__.py +0 -48
  36. table_stream-0.1.4/table_stream/thread/__init__.py +0 -369
  37. table_stream-0.1.4/table_stream/types/__init__.py +0 -6
  38. table_stream-0.1.4/table_stream/types/pattner/__init__.py +0 -9
  39. table_stream-0.1.4/table_stream/types/pattner/adapter/__init__.py +0 -82
  40. table_stream-0.1.4/table_stream/types/pattner/mediator/__init__.py +0 -74
  41. table_stream-0.1.4/table_stream.egg-info/SOURCES.txt +0 -27
  42. table_stream-0.1.4/table_stream.egg-info/requires.txt +0 -4
  43. {table_stream-0.1.4 → table_stream-0.1.6}/README.md +0 -0
  44. {table_stream-0.1.4 → table_stream-0.1.6}/setup.cfg +0 -0
  45. {table_stream-0.1.4 → table_stream-0.1.6}/table_stream.egg-info/dependency_links.txt +0 -0
  46. {table_stream-0.1.4 → table_stream-0.1.6}/table_stream.egg-info/top_level.txt +0 -0
@@ -1,13 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: table_stream
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: Tipos de dados e implementações para o uso em tabelas.
5
- Requires-Python: >=3.14
5
+ Requires-Python: >=3.12
6
6
  Description-Content-Type: text/markdown
7
- Requires-Dist: odfpy>=1.4.1
8
7
  Requires-Dist: openpyxl>=3.1.5
9
8
  Requires-Dist: pandas>=3.0.0
10
- Requires-Dist: soup-files>=1.4.2
11
9
 
12
10
  # table
13
11
 
@@ -0,0 +1,22 @@
1
+ [project]
2
+ name = "table_stream"
3
+ version = "0.1.6"
4
+ description = "Tipos de dados e implementações para o uso em tabelas."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "openpyxl>=3.1.5",
9
+ "pandas>=3.0.0",
10
+ ]
11
+
12
+ [dependency-groups]
13
+ dev = [
14
+ "build>=1.4.0",
15
+ "twine>=6.2.0",
16
+ ]
17
+
18
+ [tool.setuptools.packages.find]
19
+ where = ["."]
20
+ include = ["table_stream*"]
21
+
22
+
@@ -0,0 +1,12 @@
1
+ from .hash_map import HashMap, hashMapName, HashMapDataFrame, HashMapDict, ArrayList, K, T
2
+ from .notifier import ValueNotifier, ChangeNotifier, Provider, Consumer
3
+ from .thread import (
4
+ ControllerThread, ThreadStateContext, ConsumerThread, exceptionValue, stateName,
5
+ EventState, InvalidStateTransition,
6
+ )
7
+ from .sheet import (
8
+ SheetLoader, InterfaceSheetLoad, ParserData, CsvOptions, SplitData,
9
+ csvEncoding, csvSeparator, OptionsIOSheet, SaveData,
10
+ FactorySaveData, FactorySheetLoader, SheetType,
11
+ )
12
+ from .state import ContextState, EventState
@@ -15,6 +15,18 @@ class CoreException(Exception):
15
15
  raise self
16
16
 
17
17
 
18
+ class ValueNotifyNullError(CoreException):
19
+
20
+ def __init__(self, message: str = 'Notificação nula...') -> None:
21
+ super().__init__(message)
22
+
23
+
24
+ class UnsuportedSheetError(CoreException):
25
+
26
+ def __init__(self, message: str = 'Planilha inválida...') -> None:
27
+ super().__init__(message)
28
+
29
+
18
30
  #============================================================#
19
31
  # Erros relacionados a HashMap
20
32
  #============================================================#
@@ -0,0 +1,307 @@
1
+ from __future__ import annotations
2
+ from abc import abstractmethod, ABCMeta
3
+ from typing import Any, Callable, TypeVar, Union, Literal
4
+ import json
5
+ from pandas import DataFrame
6
+
7
+
8
+ T = TypeVar('T')
9
+ K = TypeVar('K')
10
+ hashMapName = Literal['DATAFRAME', 'DICT']
11
+
12
+
13
+ class ArrayList[T](list):
14
+
15
+ def __init__(self, items: list[T] | None = None):
16
+ if items is None:
17
+ items = list()
18
+ super().__init__(items)
19
+
20
+ def set_items(self, items: list[T]):
21
+ self.clear()
22
+ super().__init__(items)
23
+
24
+ def for_each(self, func: Callable[[T], Any | None]) -> None:
25
+ for x in self: func(x)
26
+
27
+ def apply_command(self, func: Callable[[T], Any]) -> ArrayList[Any]:
28
+ return ArrayList([func(x) for x in self])
29
+
30
+ def size(self) -> int:
31
+ return len(self)
32
+
33
+ def empty(self) -> bool:
34
+ return self.size() == 0
35
+
36
+ def get_first(self) -> T:
37
+ return self[0]
38
+
39
+ def get_last(self) -> T:
40
+ return self[-1]
41
+
42
+ def hash(self) -> int:
43
+ return hash(tuple(self))
44
+
45
+ def contains(self, _o: T) -> bool:
46
+ for i in self:
47
+ if i == _o:
48
+ return True
49
+ return False
50
+
51
+
52
+ #========================================================#
53
+ # Base para HashMap().
54
+ #========================================================#
55
+ class HashMap[K, T](metaclass=ABCMeta):
56
+
57
+ def __repr__(self) -> str:
58
+ return f"<HashMap()> {self.header()}"
59
+
60
+ @abstractmethod
61
+ def size_values(self) -> int:
62
+ pass
63
+
64
+ @abstractmethod
65
+ def size_header(self) -> int:
66
+ pass
67
+
68
+ @abstractmethod
69
+ def __getitem__(self, key: K) -> T:
70
+ pass
71
+
72
+ @abstractmethod
73
+ def __setitem__(self, key: K, value: T) -> None:
74
+ pass
75
+
76
+ @abstractmethod
77
+ def get_hash_map_name(self) -> hashMapName:
78
+ pass
79
+
80
+ @abstractmethod
81
+ def get_real_hash_map(self) -> Union[dict[K, T], DataFrame]:
82
+ pass
83
+
84
+ @abstractmethod
85
+ def set_real_hash_map(self, hash_map: dict[K, T] | DataFrame) -> None:
86
+ pass
87
+
88
+ @abstractmethod
89
+ def clear(self) -> None:
90
+ pass
91
+
92
+ @abstractmethod
93
+ def get_first(self) -> T:
94
+ pass
95
+
96
+ @abstractmethod
97
+ def set_first(self, value: T) -> None:
98
+ pass
99
+
100
+ @abstractmethod
101
+ def get_last(self) -> T:
102
+ pass
103
+
104
+ @abstractmethod
105
+ def set_last(self, value: T) -> None:
106
+ pass
107
+
108
+ @abstractmethod
109
+ def set_value(self, key: K, value: T) -> None:
110
+ pass
111
+
112
+ @abstractmethod
113
+ def get_value(self, key: K) -> T:
114
+ pass
115
+
116
+ @abstractmethod
117
+ def header(self) -> ArrayList[K]:
118
+ """
119
+ Retornar as chaves de um dicionário ou columns de um DataFrame()
120
+ """
121
+ pass
122
+
123
+ @abstractmethod
124
+ def values(self) -> ArrayList[T]:
125
+ pass
126
+
127
+ @abstractmethod
128
+ def delete_items(self, keys: list[K]) -> None:
129
+ """
130
+ Apaga chaves e valores, semelhante ao método pop().
131
+ """
132
+ pass
133
+
134
+ @abstractmethod
135
+ def to_json(self) -> str:
136
+ """
137
+ Converte o HashMap() para uma representação no formato json.
138
+ """
139
+ pass
140
+
141
+ @abstractmethod
142
+ def to_map_str(self) -> dict[str, Any]:
143
+ """
144
+ Converte o HashMap() para uma representação no formato dict() python.
145
+ """
146
+ pass
147
+
148
+ @classmethod
149
+ def from_json(cls, data: str) -> HashMap:
150
+ pass
151
+
152
+ @classmethod
153
+ def from_map(cls, data: Any) -> HashMap:
154
+ pass
155
+
156
+
157
+ class HashMapDict[K, T](HashMap):
158
+
159
+ def __init__(self, _mapping: dict[K, T] | None = None) -> None:
160
+ super().__init__()
161
+ if _mapping is None:
162
+ _mapping = dict()
163
+ self._mapping: dict[K, T] = _mapping
164
+
165
+ def __repr__(self) -> str:
166
+ return f"<{__class__.__name__}()>\n{self.get_real_hash_map()}"
167
+
168
+ def size_values(self) -> int:
169
+ return len(self._mapping.values())
170
+
171
+ def size_header(self) -> int:
172
+ return self.header().size()
173
+
174
+ def __getitem__(self, key: K) -> T:
175
+ return self._mapping[key]
176
+
177
+ def __setitem__(self, key: K, value: T) -> None:
178
+ self._mapping[key] = value
179
+
180
+ def get_hash_map_name(self) -> Literal['DICT']:
181
+ return "DICT"
182
+
183
+ def get_real_hash_map(self) -> dict[K, T]:
184
+ return self._mapping
185
+
186
+ def set_real_hash_map(self, hash_map: dict[K, T]) -> None:
187
+ self._mapping = hash_map
188
+
189
+ def clear(self) -> None:
190
+ self._mapping.clear()
191
+
192
+ def get_first(self) -> T:
193
+ return self._mapping[self.header().get_first()]
194
+
195
+ def set_first(self, value: T) -> None:
196
+ self._mapping[self.header().get_first()] = value
197
+
198
+ def get_last(self) -> T:
199
+ return self._mapping[self.header().get_last()]
200
+
201
+ def set_last(self, value: T) -> None:
202
+ self._mapping[self.header().get_last()] = value
203
+
204
+ def set_value(self, key: K, value: T) -> None:
205
+ self._mapping[key] = value
206
+
207
+ def get_value(self, key: K) -> T:
208
+ return self._mapping[key]
209
+
210
+ def header(self) -> ArrayList[K]:
211
+ return ArrayList(list(self._mapping.keys()))
212
+
213
+ def values(self) -> ArrayList[T]:
214
+ return ArrayList(list(self._mapping.values()))
215
+
216
+ def delete_items(self, keys: list[K]) -> None:
217
+ for k in keys:
218
+ self._mapping.pop(k)
219
+
220
+ def to_json(self) -> str:
221
+ """
222
+ Converte o mapa real para uma string JSON.
223
+ Nota: As chaves e valores devem ser serializáveis pelo módulo json.
224
+ """
225
+ return json.dumps(self.to_map_str(), ensure_ascii=False, indent=4)
226
+
227
+ def to_map_str(self) -> dict[str, Any]:
228
+ final = dict()
229
+ for i in self.header():
230
+ final[str(i)] = str(self.get_value(i))
231
+ return final
232
+
233
+
234
+ class HashMapDataFrame[K, Series](HashMap):
235
+
236
+ def __init__(self, df: DataFrame=None) -> None:
237
+ super().__init__()
238
+ if df is None:
239
+ df = DataFrame()
240
+ self._df: DataFrame = df
241
+
242
+ def size_values(self) -> int:
243
+ return len(self.values())
244
+
245
+ def size_header(self) -> int:
246
+ return len(self.header())
247
+
248
+ def __getitem__(self, key: K) -> Series:
249
+ return self._df[key]
250
+
251
+ def __setitem__(self, key: K, value: Series) -> None:
252
+ self._df[key] = value
253
+
254
+ def get_hash_map_name(self) -> Literal['DATAFRAME']:
255
+ return "DATAFRAME"
256
+
257
+ def get_real_hash_map(self) -> DataFrame:
258
+ return self._df
259
+
260
+ def set_real_hash_map(self, hash_map: DataFrame) -> None:
261
+ self._df = hash_map
262
+
263
+ def clear(self) -> None:
264
+ self._df = DataFrame()
265
+
266
+ def get_first(self) -> Series:
267
+ return self._df[self.header().get_first()]
268
+
269
+ def set_first(self, value: Series) -> None:
270
+ self._df[self.header().get_first()] = value
271
+
272
+ def get_last(self) -> Series:
273
+ return self._df[self.header().get_last()]
274
+
275
+ def set_last(self, value: T) -> None:
276
+ raise NotImplementedError
277
+
278
+ def set_value(self, key: K, value: Series) -> None:
279
+ self._df[key] = value
280
+
281
+ def get_value(self, key: K) -> Series:
282
+ return self._df[key]
283
+
284
+ def header(self) -> ArrayList[K]:
285
+ return ArrayList(self._df.columns.tolist())
286
+
287
+ def values(self) -> ArrayList[Any]:
288
+ return ArrayList(self._df.values.tolist())
289
+
290
+ def delete_items(self, keys: list[K]) -> None:
291
+ """
292
+ Remove as colunas informadas.
293
+ """
294
+ # axis=1 indica que queremos remover colunas (as chaves do seu mapa)
295
+ # errors='ignore' evita que o código quebre caso uma chave não exista
296
+ # self._data.drop(columns=keys, axis=1, inplace=True, errors='ignore'
297
+ self._df.drop(columns=keys, inplace=True, errors="ignore", )
298
+
299
+ def to_json(self) -> str:
300
+ """
301
+ Serializa o DataFrame para JSON.
302
+ Mantém estrutura por colunas.
303
+ """
304
+ return self._df.astype(str).to_json(orient="columns", force_ascii=False, indent=4)
305
+
306
+ def to_map_str(self) -> dict[str, Any]:
307
+ return self._df.astype('str').to_dict()
@@ -0,0 +1,228 @@
1
+ from __future__ import annotations
2
+ from abc import ABC, abstractmethod
3
+ from io import BufferedWriter, BufferedReader, BytesIO
4
+ from table_stream.sysfiles import File
5
+
6
+
7
+ #----------------------------------------------------#
8
+ # Output Stream
9
+ #----------------------------------------------------#
10
+ class OutputStream(ABC):
11
+
12
+ @abstractmethod
13
+ def get_buffer(self) -> BufferedWriter:
14
+ pass
15
+
16
+ @abstractmethod
17
+ def write(self, b: bytes):
18
+ """Escreve bytes no destino."""
19
+ pass
20
+
21
+ @abstractmethod
22
+ def flush(self):
23
+ """Força a gravação dos dados pendentes."""
24
+ pass
25
+
26
+ @abstractmethod
27
+ def close(self):
28
+ """Fecha o recurso."""
29
+ pass
30
+
31
+ # Facilitador para o 'with' do Python
32
+ def __enter__(self):
33
+ return self
34
+
35
+ def __exit__(self, exc_type, exc_val, exc_tb):
36
+ self.close()
37
+
38
+
39
+ class FileOutputStream(OutputStream):
40
+
41
+ def __init__(self, file_path: File, append: bool = False):
42
+ mode = 'ab' if append else 'wb' # 'b' indica modo binário (bytes)
43
+ #self.file_path: File = file_path
44
+ self._file_buff = open(file_path.absolute(), mode)
45
+
46
+ def get_buffer(self) -> BufferedWriter:
47
+ return self._file_buff
48
+
49
+ def write(self, b: bytes):
50
+ if not isinstance(b, bytes):
51
+ raise TypeError("O FileOutputStream só aceita bytes!")
52
+ self._file_buff.write(b)
53
+
54
+ def flush(self):
55
+ self._file_buff.flush()
56
+
57
+ def close(self):
58
+ self._file_buff.close()
59
+
60
+
61
+ class BytesOutputStream(OutputStream):
62
+
63
+ def __init__(self):
64
+ # Inicializa um buffer vazio na memória (lista de bytes)
65
+ self._buffer = bytearray()
66
+ self._closed = False
67
+
68
+ def get_buffer(self) -> BufferedWriter:
69
+ pass
70
+
71
+ def write(self, b: bytes):
72
+ if self._closed:
73
+ raise ValueError("Operação de escrita em stream fechada.")
74
+ if not isinstance(b, (bytes, bytearray)):
75
+ raise TypeError("O stream só aceita objetos do tipo bytes ou bytearray!")
76
+
77
+ # Adiciona os bytes ao buffer na memória
78
+ self._buffer.extend(b)
79
+
80
+ def flush(self):
81
+ # Em memória, o flush geralmente não faz nada,
82
+ # pois não há buffer de sistema operacional envolvido.
83
+ pass
84
+
85
+ def close(self):
86
+ self._closed = True
87
+
88
+ def to_bytes(self) -> bytes:
89
+ """
90
+ Método específico desta classe para recuperar
91
+ tudo o que foi gravado até agora.
92
+ """
93
+ return bytes(self._buffer)
94
+
95
+ def size(self) -> int:
96
+ """Retorna a quantidade de bytes acumulados."""
97
+ return len(self._buffer)
98
+
99
+
100
+
101
+ #----------------------------------------------------#
102
+ # Input Stream
103
+ #----------------------------------------------------#
104
+
105
+ class InputStream(ABC):
106
+
107
+ def __init__(self) -> None:
108
+ super().__init__()
109
+
110
+ @abstractmethod
111
+ def clear(self):
112
+ pass
113
+
114
+ @abstractmethod
115
+ def to_io(self) -> BytesIO:
116
+ pass
117
+
118
+ @abstractmethod
119
+ def get_buffer(self) -> BufferedReader:
120
+ pass
121
+
122
+ @abstractmethod
123
+ def read(self, size: int = -1) -> bytes:
124
+ """Lê bytes do stream."""
125
+ pass
126
+
127
+ @abstractmethod
128
+ def close(self):
129
+ """Fecha o recurso."""
130
+ pass
131
+
132
+ def __enter__(self):
133
+ return self
134
+
135
+ def __exit__(self, exc_type, exc_val, exc_tb):
136
+ self.close()
137
+
138
+
139
+ #----------------------------------------------------#
140
+ # File Input Stream
141
+ #----------------------------------------------------#
142
+ class FileInputStream(InputStream):
143
+
144
+ def __init__(self, file_path: File):
145
+ super().__init__()
146
+ if not file_path.exists():
147
+ raise FileNotFoundError(f'{__class__.__name__} arquivo não encontrado')
148
+
149
+ self.file_path: File = file_path
150
+ self._file_buff: BufferedReader | None = None
151
+
152
+ def to_io(self) -> BytesIO:
153
+ buff = self.get_buffer()
154
+ buff.seek(0) # garante leitura desde o início
155
+ return BytesIO(buff.read())
156
+
157
+ def get_buffer(self) -> BufferedReader:
158
+ if self._file_buff is None:
159
+ self._file_buff = open(self.file_path.absolute(), 'rb')
160
+ return self._file_buff
161
+
162
+ def read(self, size: int = -1) -> bytes:
163
+ return self.get_buffer().read(size)
164
+
165
+ def close(self):
166
+ if self._file_buff is None:
167
+ return
168
+ self._file_buff.close()
169
+
170
+ def clear(self):
171
+ if self._file_buff is not None:
172
+ self.close()
173
+ self._file_buff = None
174
+
175
+
176
+ #----------------------------------------------------#
177
+ # Bytes Input Stream (memória)
178
+ #----------------------------------------------------#
179
+ class BytesInputStream(InputStream):
180
+
181
+ def __init__(self, data: bytes):
182
+ super().__init__()
183
+ if not isinstance(data, (bytes, bytearray)):
184
+ raise TypeError("BytesInputStream aceita apenas bytes ou bytearray!")
185
+
186
+ data.seek(0)
187
+ self._buffer = memoryview(data)
188
+ self._position = 0
189
+ self._closed = False
190
+
191
+ def to_io(self) -> BytesIO:
192
+ return BytesIO(self._buffer)
193
+
194
+ def set_bytes(self, data: bytes | bytearray):
195
+ self._buffer = memoryview(data)
196
+
197
+ def get_buffer(self) -> BufferedReader:
198
+ # Não há um buffer real compatível com BufferedReader
199
+ # Mantemos None ou poderíamos lançar erro
200
+ return None
201
+
202
+ def read(self, size: int = -1) -> bytes:
203
+ if self._closed:
204
+ raise ValueError("Operação de leitura em stream fechada.")
205
+
206
+ if size < 0:
207
+ size = len(self._buffer) - self._position
208
+
209
+ start = self._position
210
+ end = min(start + size, len(self._buffer))
211
+
212
+ self._position = end
213
+ return self._buffer[start:end].tobytes()
214
+
215
+ def close(self):
216
+ self._closed = True
217
+
218
+ def available(self) -> int:
219
+ """Quantidade de bytes restantes para leitura."""
220
+ return len(self._buffer) - self._position
221
+
222
+ def clear(self):
223
+ if self._buffer is not None:
224
+ self.close()
225
+ self._buffer = None
226
+
227
+
228
+