nia-etl-utils 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl

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,30 +1,80 @@
1
- """Processamento paralelo de arquivos CSV grandes."""
2
- import sys
3
- from pathlib import Path
4
- from typing import Callable, List, Optional
1
+ """Processamento paralelo de arquivos CSV grandes.
2
+
3
+ Fornece funções para processar arquivos CSV em chunks paralelos,
4
+ otimizando o uso de CPU para transformações em arquivos grandes.
5
+
6
+ Examples:
7
+ Processamento básico:
8
+
9
+ >>> from nia_etl_utils import processar_csv_paralelo
10
+ >>>
11
+ >>> def limpar_texto(texto):
12
+ ... if pd.isna(texto):
13
+ ... return texto
14
+ ... return texto.strip().upper()
15
+ >>>
16
+ >>> processar_csv_paralelo(
17
+ ... caminho_entrada="dados_brutos.csv",
18
+ ... caminho_saida="dados_limpos.csv",
19
+ ... colunas_para_tratar=["nome", "descricao"],
20
+ ... funcao_transformacao=limpar_texto
21
+ ... )
22
+
23
+ Com configurações customizadas:
24
+
25
+ >>> processar_csv_paralelo(
26
+ ... caminho_entrada="arquivo_grande.csv",
27
+ ... caminho_saida="arquivo_processado.csv",
28
+ ... colunas_para_tratar=["texto"],
29
+ ... funcao_transformacao=minha_funcao,
30
+ ... chunksize=5000,
31
+ ... num_processos=4,
32
+ ... remover_entrada=True
33
+ ... )
34
+ """
35
+
36
+ from collections.abc import Callable
5
37
  from multiprocessing import Pool, cpu_count
38
+ from pathlib import Path
6
39
 
7
40
  import pandas as pd
8
41
  from loguru import logger
9
42
 
43
+ from .exceptions import LeituraArquivoError, ProcessamentoError
44
+
10
45
 
11
46
  def calcular_chunksize(caminho_arquivo: str) -> int:
12
47
  """Calcula tamanho ideal de chunk baseado no tamanho do arquivo.
13
48
 
49
+ Retorna um tamanho de chunk otimizado para balancear uso de memória
50
+ e eficiência de processamento paralelo.
51
+
14
52
  Args:
15
53
  caminho_arquivo: Caminho do arquivo CSV.
16
54
 
17
55
  Returns:
18
- int: Tamanho do chunk otimizado.
56
+ Tamanho do chunk em número de linhas:
57
+ - Arquivo < 500MB: 10000 linhas
58
+ - Arquivo 500MB-2GB: 5000 linhas
59
+ - Arquivo 2-5GB: 2000 linhas
60
+ - Arquivo > 5GB: 1000 linhas
61
+
62
+ Raises:
63
+ LeituraArquivoError: Se o arquivo não existir.
19
64
 
20
65
  Examples:
21
66
  >>> chunksize = calcular_chunksize("dados_grandes.csv")
22
- >>> # Arquivo < 500MB: 10000 linhas
23
- >>> # Arquivo 500MB-2GB: 5000 linhas
24
- >>> # Arquivo 2-5GB: 2000 linhas
25
- >>> # Arquivo > 5GB: 1000 linhas
67
+ >>> print(f"Usando chunks de {chunksize} linhas")
26
68
  """
27
- tamanho_mb = Path(caminho_arquivo).stat().st_size / (1024 * 1024)
69
+ arquivo = Path(caminho_arquivo)
70
+
71
+ if not arquivo.exists():
72
+ raise LeituraArquivoError(
73
+ f"Arquivo não encontrado: {caminho_arquivo}",
74
+ details={"caminho": caminho_arquivo}
75
+ )
76
+
77
+ tamanho_mb = arquivo.stat().st_size / (1024 * 1024)
28
78
 
29
79
  if tamanho_mb < 500:
30
80
  return 10000
@@ -39,7 +89,15 @@ def calcular_chunksize(caminho_arquivo: str) -> int:
39
89
  def _processar_chunk(args: tuple) -> pd.DataFrame:
40
90
  """Processa um chunk aplicando transformações.
41
91
 
42
- Função interna usada pelo Pool.imap().
92
+ Função interna usada pelo Pool.imap(). Não deve ser chamada
93
+ diretamente.
94
+
95
+ Args:
96
+ args: Tupla contendo (chunk, colunas_para_tratar,
97
+ func_tratar_texto, normalizar_colunas).
98
+
99
+ Returns:
100
+ DataFrame com transformações aplicadas.
43
101
  """
44
102
  chunk, colunas_para_tratar, func_tratar_texto, normalizar_colunas = args
45
103
 
@@ -60,48 +118,85 @@ def _processar_chunk(args: tuple) -> pd.DataFrame:
60
118
  def processar_csv_paralelo(
61
119
  caminho_entrada: str,
62
120
  caminho_saida: str,
63
- colunas_para_tratar: List[str],
121
+ colunas_para_tratar: list[str],
64
122
  funcao_transformacao: Callable,
65
- chunksize: Optional[int] = None,
123
+ chunksize: int | None = None,
66
124
  normalizar_colunas: bool = True,
67
125
  remover_entrada: bool = False,
68
- num_processos: Optional[int] = None
69
- ) -> None:
126
+ num_processos: int | None = None
127
+ ) -> int:
70
128
  """Processa CSV grande em paralelo aplicando transformações por chunk.
71
129
 
130
+ Lê o arquivo CSV em chunks, processa cada chunk em paralelo usando
131
+ multiprocessing, e escreve o resultado no arquivo de saída.
132
+
72
133
  Args:
73
- caminho_entrada: Arquivo CSV de entrada.
74
- caminho_saida: Arquivo CSV de saída.
75
- colunas_para_tratar: Lista de colunas para aplicar transformação.
76
- funcao_transformacao: Função que recebe valor e retorna valor transformado.
77
- chunksize: Tamanho do chunk. Se None, calcula automaticamente.
78
- normalizar_colunas: Se True, converte nomes de colunas para lowercase.
79
- remover_entrada: Se True, remove arquivo de entrada após processar.
80
- num_processos: Número de processos paralelos. Se None, usa cpu_count().
134
+ caminho_entrada: Caminho do arquivo CSV de entrada.
135
+ caminho_saida: Caminho do arquivo CSV de saída.
136
+ colunas_para_tratar: Lista de nomes de colunas para aplicar
137
+ a função de transformação.
138
+ funcao_transformacao: Função que recebe um valor e retorna
139
+ o valor transformado. Deve tratar valores nulos (None/NaN).
140
+ chunksize: Número de linhas por chunk. Se None, calcula
141
+ automaticamente baseado no tamanho do arquivo.
142
+ normalizar_colunas: Se True, converte nomes de colunas para
143
+ lowercase. Defaults to True.
144
+ remover_entrada: Se True, remove arquivo de entrada após
145
+ processar com sucesso. Defaults to False.
146
+ num_processos: Número de processos paralelos. Se None, usa
147
+ o número de CPUs disponíveis.
148
+
149
+ Returns:
150
+ Número total de linhas processadas.
81
151
 
82
152
  Raises:
83
- SystemExit: Se arquivo de entrada não existe ou erro no processamento.
153
+ LeituraArquivoError: Se arquivo de entrada não existir.
154
+ ProcessamentoError: Se houver erro durante o processamento.
84
155
 
85
156
  Examples:
86
- >>> from nia_etl_utils import processar_csv_paralelo
87
- >>>
157
+ Processamento básico:
158
+
88
159
  >>> def limpar_texto(texto):
160
+ ... if pd.isna(texto):
161
+ ... return texto
89
162
  ... return texto.strip().upper()
90
163
  >>>
91
- >>> processar_csv_paralelo(
164
+ >>> linhas = processar_csv_paralelo(
92
165
  ... caminho_entrada="dados_brutos.csv",
93
166
  ... caminho_saida="dados_limpos.csv",
94
167
  ... colunas_para_tratar=["nome", "descricao"],
95
- ... funcao_transformacao=limpar_texto,
168
+ ... funcao_transformacao=limpar_texto
169
+ ... )
170
+ >>> print(f"{linhas} linhas processadas")
171
+
172
+ Com configurações customizadas:
173
+
174
+ >>> linhas = processar_csv_paralelo(
175
+ ... caminho_entrada="arquivo_grande.csv",
176
+ ... caminho_saida="arquivo_processado.csv",
177
+ ... colunas_para_tratar=["texto"],
178
+ ... funcao_transformacao=minha_funcao,
179
+ ... chunksize=5000,
180
+ ... num_processos=4,
96
181
  ... remover_entrada=True
97
182
  ... )
183
+
184
+ Tratando erros:
185
+
186
+ >>> from nia_etl_utils.exceptions import ProcessamentoError
187
+ >>> try:
188
+ ... processar_csv_paralelo(...)
189
+ ... except ProcessamentoError as e:
190
+ ... logger.error(f"Falha no processamento: {e}")
98
191
  """
99
192
  caminho_entrada_path = Path(caminho_entrada)
100
193
 
101
194
  # Validação de entrada
102
195
  if not caminho_entrada_path.exists():
103
- logger.error(f"Arquivo de entrada não encontrado: {caminho_entrada}")
104
- sys.exit(1)
196
+ raise LeituraArquivoError(
197
+ f"Arquivo de entrada não encontrado: {caminho_entrada}",
198
+ details={"caminho": caminho_entrada}
199
+ )
105
200
 
106
201
  try:
107
202
  logger.info(f"Iniciando processamento paralelo: {caminho_entrada}")
@@ -110,12 +205,14 @@ def processar_csv_paralelo(
110
205
  if chunksize is None:
111
206
  chunksize = calcular_chunksize(caminho_entrada)
112
207
 
113
- logger.info(f"Chunksize: {chunksize} linhas | Processos: {num_processos or cpu_count()}")
208
+ processos = num_processos or cpu_count()
209
+ logger.info(f"Chunksize: {chunksize} linhas | Processos: {processos}")
114
210
 
115
211
  # Processamento paralelo
116
212
  primeiro_chunk = True
213
+ total_linhas = 0
117
214
 
118
- with Pool(processes=num_processos or cpu_count()) as pool:
215
+ with Pool(processes=processos) as pool:
119
216
  reader = pd.read_csv(caminho_entrada, chunksize=chunksize)
120
217
 
121
218
  # Prepara tasks para processamento paralelo
@@ -126,7 +223,9 @@ def processar_csv_paralelo(
126
223
 
127
224
  # Processa chunks em paralelo
128
225
  for i, chunk_processado in enumerate(pool.imap(_processar_chunk, tasks), start=1):
129
- logger.info(f"Escrevendo chunk {i} ({len(chunk_processado)} linhas)")
226
+ linhas_chunk = len(chunk_processado)
227
+ total_linhas += linhas_chunk
228
+ logger.debug(f"Escrevendo chunk {i} ({linhas_chunk} linhas)")
130
229
 
131
230
  chunk_processado.to_csv(
132
231
  caminho_saida,
@@ -136,7 +235,7 @@ def processar_csv_paralelo(
136
235
  )
137
236
  primeiro_chunk = False
138
237
 
139
- logger.success(f"Processamento concluído: {caminho_saida}")
238
+ logger.success(f"Processamento concluído: {caminho_saida} ({total_linhas} linhas)")
140
239
 
141
240
  # Remove arquivo de entrada se solicitado
142
241
  if remover_entrada:
@@ -146,6 +245,20 @@ def processar_csv_paralelo(
146
245
  except Exception as e:
147
246
  logger.warning(f"Falha ao remover arquivo de entrada: {e}")
148
247
 
149
- except Exception as error:
150
- logger.exception(f"Erro no processamento paralelo: {error}")
151
- sys.exit(1)
248
+ return total_linhas
249
+
250
+ except pd.errors.EmptyDataError as e:
251
+ raise ProcessamentoError(
252
+ f"Arquivo de entrada está vazio: {caminho_entrada}",
253
+ details={"caminho": caminho_entrada, "erro": str(e)}
254
+ ) from e
255
+ except pd.errors.ParserError as e:
256
+ raise ProcessamentoError(
257
+ f"Erro ao parsear CSV: {caminho_entrada}",
258
+ details={"caminho": caminho_entrada, "erro": str(e)}
259
+ ) from e
260
+ except Exception as e:
261
+ raise ProcessamentoError(
262
+ f"Erro no processamento paralelo: {caminho_entrada}",
263
+ details={"caminho": caminho_entrada, "erro": str(e)}
264
+ ) from e
@@ -0,0 +1,304 @@
1
+ """Dataclasses de resultado para operações do pacote nia_etl_utils.
2
+
3
+ Este módulo define estruturas de dados para retorno de operações,
4
+ fornecendo informações estruturadas sobre o resultado de cada ação.
5
+
6
+ Examples:
7
+ Resultado de extração:
8
+
9
+ >>> resultado = ResultadoExtracao(
10
+ ... nome="clientes",
11
+ ... caminho="/tmp/clientes_2025_01_20.csv",
12
+ ... linhas=1500,
13
+ ... sucesso=True
14
+ ... )
15
+ >>> if resultado.sucesso:
16
+ ... print(f"Exportados {resultado.linhas} registros")
17
+
18
+ Resultado com erro:
19
+
20
+ >>> resultado = ResultadoExtracao(
21
+ ... nome="vendas",
22
+ ... caminho=None,
23
+ ... linhas=0,
24
+ ... sucesso=False,
25
+ ... erro="Nenhum dado retornado"
26
+ ... )
27
+ """
28
+
29
+ from dataclasses import dataclass, field
30
+ from typing import Any
31
+
32
+
33
+ @dataclass
34
+ class Conexao:
35
+ """Wrapper para conexão de banco de dados.
36
+
37
+ Encapsula cursor e connection, fornecendo interface consistente
38
+ e suporte a context manager para fechamento automático.
39
+
40
+ Attributes:
41
+ cursor: Cursor ativo para execução de queries.
42
+ connection: Objeto de conexão subjacente (psycopg2 ou cx_Oracle).
43
+ database: Nome/identificador do banco conectado.
44
+
45
+ Examples:
46
+ Uso com context manager (recomendado):
47
+
48
+ >>> with conectar_postgresql(config) as conn:
49
+ ... conn.cursor.execute("SELECT * FROM tabela")
50
+ ... dados = conn.cursor.fetchall()
51
+ ... # conexão fechada automaticamente
52
+
53
+ Uso manual:
54
+
55
+ >>> conn = conectar_postgresql(config)
56
+ >>> try:
57
+ ... conn.cursor.execute("SELECT 1")
58
+ ... resultado = conn.cursor.fetchone()
59
+ ... finally:
60
+ ... conn.fechar()
61
+
62
+ Acesso aos componentes:
63
+
64
+ >>> conn = conectar_postgresql(config)
65
+ >>> conn.cursor.execute("SELECT COUNT(*) FROM usuarios")
66
+ >>> total = conn.cursor.fetchone()[0]
67
+ >>> conn.connection.commit() # se necessário
68
+ >>> conn.fechar()
69
+ """
70
+
71
+ cursor: Any
72
+ connection: Any
73
+ database: str
74
+
75
+ def fechar(self) -> None:
76
+ """Encerra cursor e conexão de forma segura.
77
+
78
+ Tenta fechar cursor e conexão, logando warnings em caso
79
+ de erro mas nunca levantando exceções.
80
+
81
+ Examples:
82
+ >>> conn = conectar_postgresql(config)
83
+ >>> # ... usar conexão ...
84
+ >>> conn.fechar() # sempre seguro
85
+ """
86
+ from loguru import logger
87
+
88
+ try:
89
+ if self.cursor:
90
+ self.cursor.close()
91
+ logger.debug("Cursor fechado com sucesso.")
92
+ except Exception as e:
93
+ logger.warning(f"Erro ao fechar cursor: {e}")
94
+
95
+ try:
96
+ if self.connection:
97
+ self.connection.close()
98
+ logger.debug("Conexão encerrada com sucesso.")
99
+ except Exception as e:
100
+ logger.warning(f"Erro ao fechar conexão: {e}")
101
+
102
+ def __enter__(self) -> "Conexao":
103
+ """Entrada do context manager."""
104
+ return self
105
+
106
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
107
+ """Saída do context manager - fecha conexão automaticamente."""
108
+ self.fechar()
109
+
110
+
111
+ @dataclass
112
+ class ResultadoExtracao:
113
+ """Resultado de uma operação de extração e exportação CSV.
114
+
115
+ Fornece informações estruturadas sobre o resultado de uma
116
+ extração, incluindo métricas e status de sucesso/erro.
117
+
118
+ Attributes:
119
+ nome: Identificador da extração.
120
+ caminho: Caminho do arquivo CSV gerado (None se falhou).
121
+ linhas: Quantidade de registros extraídos.
122
+ sucesso: True se a operação completou com sucesso.
123
+ erro: Mensagem de erro se sucesso=False, None caso contrário.
124
+ colunas: Quantidade de colunas no DataFrame (opcional).
125
+ tamanho_bytes: Tamanho do arquivo em bytes (opcional).
126
+
127
+ Examples:
128
+ Extração bem-sucedida:
129
+
130
+ >>> resultado = ResultadoExtracao(
131
+ ... nome="clientes",
132
+ ... caminho="/tmp/clientes_2025_01_20.csv",
133
+ ... linhas=1500,
134
+ ... sucesso=True,
135
+ ... colunas=12,
136
+ ... tamanho_bytes=45000
137
+ ... )
138
+ >>> print(f"Exportados {resultado.linhas} registros para {resultado.caminho}")
139
+
140
+ Extração com falha:
141
+
142
+ >>> resultado = ResultadoExtracao(
143
+ ... nome="vendas",
144
+ ... caminho=None,
145
+ ... linhas=0,
146
+ ... sucesso=False,
147
+ ... erro="Nenhum dado retornado para extração 'vendas'"
148
+ ... )
149
+ >>> if not resultado.sucesso:
150
+ ... logger.warning(resultado.erro)
151
+
152
+ Verificando resultados em lote:
153
+
154
+ >>> resultados = exportar_multiplos_csv(extractions, ...)
155
+ >>> sucesso = [r for r in resultados if r.sucesso]
156
+ >>> falhas = [r for r in resultados if not r.sucesso]
157
+ >>> print(f"{len(sucesso)} OK, {len(falhas)} falhas")
158
+ """
159
+
160
+ nome: str
161
+ caminho: str | None
162
+ linhas: int
163
+ sucesso: bool
164
+ erro: str | None = None
165
+ colunas: int | None = None
166
+ tamanho_bytes: int | None = None
167
+
168
+ @property
169
+ def tamanho_kb(self) -> float | None:
170
+ """Tamanho do arquivo em kilobytes.
171
+
172
+ Returns:
173
+ Tamanho em KB ou None se tamanho_bytes não definido.
174
+
175
+ Examples:
176
+ >>> resultado.tamanho_bytes = 45000
177
+ >>> resultado.tamanho_kb
178
+ 43.945...
179
+ """
180
+ if self.tamanho_bytes is None:
181
+ return None
182
+ return self.tamanho_bytes / 1024
183
+
184
+ @property
185
+ def tamanho_mb(self) -> float | None:
186
+ """Tamanho do arquivo em megabytes.
187
+
188
+ Returns:
189
+ Tamanho em MB ou None se tamanho_bytes não definido.
190
+
191
+ Examples:
192
+ >>> resultado.tamanho_bytes = 1048576
193
+ >>> resultado.tamanho_mb
194
+ 1.0
195
+ """
196
+ if self.tamanho_bytes is None:
197
+ return None
198
+ return self.tamanho_bytes / (1024 * 1024)
199
+
200
+
201
+ @dataclass
202
+ class ResultadoLote:
203
+ """Resultado consolidado de operações em lote.
204
+
205
+ Agrupa múltiplos ResultadoExtracao e fornece métricas
206
+ consolidadas sobre o lote.
207
+
208
+ Attributes:
209
+ resultados: Lista de ResultadoExtracao individuais.
210
+ total: Número total de extrações no lote.
211
+ sucesso: Número de extrações bem-sucedidas.
212
+ falhas: Número de extrações que falharam.
213
+
214
+ Examples:
215
+ >>> lote = ResultadoLote(resultados=[r1, r2, r3])
216
+ >>> print(f"Taxa de sucesso: {lote.taxa_sucesso:.1%}")
217
+ >>> if lote.todos_sucesso:
218
+ ... print("Todas extrações OK!")
219
+ >>> for falha in lote.extrações_falhas:
220
+ ... print(f"Falhou: {falha.nome} - {falha.erro}")
221
+ """
222
+
223
+ resultados: list[ResultadoExtracao] = field(default_factory=list)
224
+
225
+ @property
226
+ def total(self) -> int:
227
+ """Número total de extrações no lote."""
228
+ return len(self.resultados)
229
+
230
+ @property
231
+ def sucesso(self) -> int:
232
+ """Número de extrações bem-sucedidas."""
233
+ return sum(1 for r in self.resultados if r.sucesso)
234
+
235
+ @property
236
+ def falhas(self) -> int:
237
+ """Número de extrações que falharam."""
238
+ return sum(1 for r in self.resultados if not r.sucesso)
239
+
240
+ @property
241
+ def todos_sucesso(self) -> bool:
242
+ """True se todas as extrações foram bem-sucedidas."""
243
+ return self.falhas == 0
244
+
245
+ @property
246
+ def taxa_sucesso(self) -> float:
247
+ """Taxa de sucesso (0.0 a 1.0)."""
248
+ if self.total == 0:
249
+ return 0.0
250
+ return self.sucesso / self.total
251
+
252
+ @property
253
+ def extracoes_sucesso(self) -> list[ResultadoExtracao]:
254
+ """Lista de extrações bem-sucedidas."""
255
+ return [r for r in self.resultados if r.sucesso]
256
+
257
+ @property
258
+ def extracoes_falhas(self) -> list[ResultadoExtracao]:
259
+ """Lista de extrações que falharam."""
260
+ return [r for r in self.resultados if not r.sucesso]
261
+
262
+ @property
263
+ def total_linhas(self) -> int:
264
+ """Total de linhas extraídas em todas as extrações."""
265
+ return sum(r.linhas for r in self.resultados)
266
+
267
+ def adicionar(self, resultado: ResultadoExtracao) -> None:
268
+ """Adiciona um resultado ao lote.
269
+
270
+ Args:
271
+ resultado: ResultadoExtracao a ser adicionado.
272
+
273
+ Examples:
274
+ >>> lote = ResultadoLote()
275
+ >>> lote.adicionar(resultado1)
276
+ >>> lote.adicionar(resultado2)
277
+ """
278
+ self.resultados.append(resultado)
279
+
280
+
281
+ @dataclass
282
+ class ResultadoEmail:
283
+ """Resultado de envio de email.
284
+
285
+ Attributes:
286
+ sucesso: True se o email foi enviado com sucesso.
287
+ destinatarios: Lista de destinatários do email.
288
+ assunto: Assunto do email enviado.
289
+ erro: Mensagem de erro se sucesso=False.
290
+ anexo: Caminho do anexo enviado (se houver).
291
+
292
+ Examples:
293
+ >>> resultado = ResultadoEmail(
294
+ ... sucesso=True,
295
+ ... destinatarios=["admin@empresa.com"],
296
+ ... assunto="Relatório Diário"
297
+ ... )
298
+ """
299
+
300
+ sucesso: bool
301
+ destinatarios: list[str]
302
+ assunto: str
303
+ erro: str | None = None
304
+ anexo: str | None = None
@@ -1,10 +1,25 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nia-etl-utils
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Utilitários compartilhados para pipelines ETL do NIA/MPRJ
5
5
  Author-email: Nícolas Esmael <nicolas.esmael@mprj.mp.br>
6
6
  License: MIT
7
7
  Project-URL: Repository, https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils
8
+ Project-URL: Documentation, https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils/-/blob/main/README.md
9
+ Project-URL: Changelog, https://gitlab-dti.mprj.mp.br/nia/etl-nia/nia-etl-utils/-/blob/main/CHANGELOG.md
10
+ Keywords: etl,data-engineering,pipeline,mprj,nia
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Database
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Typing :: Typed
8
23
  Requires-Python: >=3.10
9
24
  Description-Content-Type: text/markdown
10
25
  Requires-Dist: loguru>=0.7.0
@@ -17,6 +32,12 @@ Provides-Extra: dev
17
32
  Requires-Dist: pytest>=7.0.0; extra == "dev"
18
33
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
19
34
  Requires-Dist: ruff>=0.1.0; extra == "dev"
35
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
36
+ Requires-Dist: pandas-stubs>=2.0.0; extra == "dev"
37
+ Provides-Extra: docs
38
+ Requires-Dist: mkdocs>=1.5.0; extra == "docs"
39
+ Requires-Dist: mkdocs-material>=9.0.0; extra == "docs"
40
+ Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "docs"
20
41
 
21
42
  # nia-etl-utils
22
43
 
@@ -0,0 +1,15 @@
1
+ nia_etl_utils/__init__.py,sha256=ODg-ykR1Imq2bwtVopyKeSpu3e2X8fJvC1r2JbyfYkQ,6157
2
+ nia_etl_utils/config.py,sha256=ITgG-BxmCOKGsyxTJnsQYqQJv5TeM2ng9JlGtUxz0Sw,11842
3
+ nia_etl_utils/database.py,sha256=Ggn9EqSb7SyIRUpumx_DIO3bLN-9-ZrgwkKmuqfIKS0,10789
4
+ nia_etl_utils/email_smtp.py,sha256=F4eVeVmNFnzK-4HU7bKijgPzMzWOWOWXTlFF_1fFdFc,8339
5
+ nia_etl_utils/env_config.py,sha256=LH1FCpaAlo2nVx4SIuL9RGKchkgYP8jJurPe_3_IHEA,5506
6
+ nia_etl_utils/exceptions.py,sha256=nsZoleUB9aV7Tfb5gLuYy_LCP1TgHiuccTSFA4qlZUA,8728
7
+ nia_etl_utils/limpeza_pastas.py,sha256=OuiSCp7Hxpby0WRgox2Ak-B3ATFpyj08uidmBMwV434,7906
8
+ nia_etl_utils/logger_config.py,sha256=Thhv7uwuhvXMKxef6_2M7lTwE1MfN_LAxWuTHjbLPd0,6787
9
+ nia_etl_utils/processa_csv.py,sha256=BzgN6UUq2mGXjqKdnOaimLOLTXWu9H1SYdE1tJSWZ3M,11834
10
+ nia_etl_utils/processa_csv_paralelo.py,sha256=0tQwsbHzi6UYEc2fp6o2E3pl23XOTvKhCShwCdcqrW0,8859
11
+ nia_etl_utils/results.py,sha256=ah0ZaIymjdDi-4lO24EYiBeZTQpm289_ZHmUpoNQEVs,9156
12
+ nia_etl_utils-0.2.0.dist-info/METADATA,sha256=eSUBmRTdIFMmNYOhCGqFV0MKiyNF8Zr2cFV6fJ2a2Ik,18196
13
+ nia_etl_utils-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ nia_etl_utils-0.2.0.dist-info/top_level.txt,sha256=LYLtk9Gh-GaiyyQkwpVs1CoOtnn1G9t8_ijxcFHyjfY,14
15
+ nia_etl_utils-0.2.0.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- nia_etl_utils/__init__.py,sha256=PzeQS2LIdUtj99uUKJVASYFkYaCYaA-Nn82sQ6c83QA,3124
2
- nia_etl_utils/database.py,sha256=XRE6n8mCoknw3r0bLQrYUIwR-TpZrrcPTc4AImTbbAM,8349
3
- nia_etl_utils/email_smtp.py,sha256=dkiPlmJJLdRVjaxo-ge54GQGsqThijwvExcfK263EOk,4488
4
- nia_etl_utils/env_config.py,sha256=MRXxeKXKEaPhc8mHGboQ6NGUcMKTx7rsh68RVmYOKBo,1438
5
- nia_etl_utils/limpeza_pastas.py,sha256=w4Ky1cEkiB2630eNLDrimXw0NCtrn6HSvV11HEDVbPE,4540
6
- nia_etl_utils/logger_config.py,sha256=2IWvmQQ76yIlfGjPX9bcQLWtQZeI1nA5XiMyjFC-zMI,5098
7
- nia_etl_utils/processa_csv.py,sha256=JgYaPN71Nu4v7qQeaYsfTCzJ-yAOAb5kQBoUYjEHUfo,6959
8
- nia_etl_utils/processa_csv_paralelo.py,sha256=hCjVdX_LIkq1YVPxXrXLmc0gIKoaf4i-jLFAWVCNjKY,5146
9
- nia_etl_utils-0.1.0.dist-info/METADATA,sha256=4pFYjkKZ33xTJqDJiOKeW0VPZHTBaiQM1lfNbLtpVUM,17091
10
- nia_etl_utils-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- nia_etl_utils-0.1.0.dist-info/top_level.txt,sha256=LYLtk9Gh-GaiyyQkwpVs1CoOtnn1G9t8_ijxcFHyjfY,14
12
- nia_etl_utils-0.1.0.dist-info/RECORD,,