lbxtoolkit 2.4.4__tar.gz → 2.4.5__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: lbxtoolkit
3
- Version: 2.4.4
3
+ Version: 2.4.5
4
4
  Summary: Caixa ferramentas da LBX para automações de processos (RPA), integrações, utilitários Windows e componentes reutilizáveis.
5
5
  Author: Cristiano P. Ferrari
6
6
  Author-email: cristiano.ferrari@lbx.com.br
@@ -14,8 +14,8 @@ Requires-Dist: numpy (>=2.0.1)
14
14
  Requires-Dist: pandas (>=2.2.2)
15
15
  Requires-Dist: psutil (>=6.0.0)
16
16
  Requires-Dist: psycopg2 (>=2.9.9)
17
- Requires-Dist: pygetwindow (>=0.0.9)
18
- Requires-Dist: pywin32 (>=306)
17
+ Requires-Dist: pygetwindow (>=0.0.9) ; sys_platform == "win32"
18
+ Requires-Dist: pywin32 (>=306) ; sys_platform == "win32"
19
19
  Requires-Dist: requests (>=2.32.3)
20
20
  Requires-Dist: selenium (>=4.23.1)
21
21
  Requires-Dist: validators (>=0.33.0)
@@ -265,8 +265,9 @@ Para uso em APIs com autenticação JWT (JSON Web Token), OAuth, Bearer Token Au
265
265
  MsgErro = Retorno
266
266
  except:
267
267
  MsgErro = Retorno.replace(r'\n\t',' ')
268
- finally:
269
- return MsgErro
268
+ # antes: "finally: return MsgErro" — mesmo efeito, sem o
269
+ # SyntaxWarning do Python 3.14 (PEP 765: return em finally)
270
+ return MsgErro
270
271
  else:
271
272
  return Retorno
272
273
  #
@@ -1,13 +1,24 @@
1
1
  import ctypes
2
2
  import os
3
3
  import re
4
- import tkinter as tk
5
4
  from pathlib import Path
6
- from tkinter import filedialog
7
5
  from typing import Any, Optional
8
6
  from unicodedata import normalize
9
7
 
10
- import pygetwindow as gw
8
+ # Dependências gráficas/Windows são opcionais: em Linux headless (VPS) o
9
+ # tkinter pode não existir e o pygetwindow lança NotImplementedError no import.
10
+ # Os métodos que dependem delas verificam a disponibilidade em tempo de execução.
11
+ try:
12
+ import tkinter as tk
13
+ from tkinter import filedialog
14
+ except ImportError: # pragma: no cover - depende da plataforma
15
+ tk = None # type: ignore[assignment]
16
+ filedialog = None # type: ignore[assignment]
17
+
18
+ try:
19
+ import pygetwindow as gw
20
+ except (ImportError, NotImplementedError): # pragma: no cover - depende da plataforma
21
+ gw = None # type: ignore[assignment]
11
22
 
12
23
 
13
24
  class misc:
@@ -55,6 +66,9 @@ class misc:
55
66
  if TiposArquivo is None:
56
67
  TiposArquivo = [("Todos os arquivos", "*.*")]
57
68
 
69
+ if tk is None or filedialog is None:
70
+ raise RuntimeError("seleciona_arquivo indisponível: tkinter não está instalado neste ambiente")
71
+
58
72
  root = tk.Tk()
59
73
  root.withdraw()
60
74
 
@@ -83,6 +97,9 @@ class misc:
83
97
  Caminho do diretório selecionado como `Path`.
84
98
  Se o usuário cancelar, retorna `Path('.')`.
85
99
  """
100
+ if tk is None or filedialog is None:
101
+ raise RuntimeError("seleciona_dir indisponível: tkinter não está instalado neste ambiente")
102
+
86
103
  root = tk.Tk()
87
104
  root.withdraw()
88
105
 
@@ -167,8 +184,12 @@ class misc:
167
184
 
168
185
  Returns:
169
186
  Objeto de janela retornado por `pygetwindow`, ou `None`
170
- se nenhuma janela adequada for encontrada.
187
+ se nenhuma janela adequada for encontrada ou se a plataforma
188
+ não suportar `pygetwindow` (ex.: Linux).
171
189
  """
190
+ if gw is None:
191
+ return None
192
+
172
193
  windows = gw.getWindowsWithTitle("")
173
194
  for window in windows:
174
195
  if window.title and window.visible and window.topleft:
@@ -181,8 +202,12 @@ class misc:
181
202
  """
182
203
  Maximiza a janela atual do console no Windows.
183
204
 
184
- Não faz nada se o handle da janela não puder ser obtido.
205
+ Não faz nada se o handle da janela não puder ser obtido
206
+ ou se a plataforma não for Windows.
185
207
  """
208
+ if os.name != "nt":
209
+ return
210
+
186
211
  kernel32 = ctypes.WinDLL("kernel32")
187
212
  user32 = ctypes.WinDLL("user32")
188
213
  hWnd = kernel32.GetConsoleWindow()
@@ -487,4 +512,4 @@ class misc:
487
512
  try:
488
513
  return int(str(valor).strip())
489
514
  except ValueError as exc:
490
- raise RuntimeError(f"Variável de ambiente {nome} deve ser numérica.") from exc
515
+ raise RuntimeError(f"Variável de ambiente {nome} deve ser numérica.") from exc
@@ -8,8 +8,12 @@ from pathlib import Path
8
8
  import platform
9
9
  import socket
10
10
  import argparse
11
- import servicemanager #type: ignore
12
- import win32serviceutil #type: ignore
11
+ try:
12
+ import servicemanager #type: ignore
13
+ import win32serviceutil #type: ignore
14
+ except ImportError: # pragma: no cover - fora do Windows
15
+ servicemanager = None # type: ignore[assignment]
16
+ win32serviceutil = None # type: ignore[assignment]
13
17
 
14
18
  class Servicer(): # Cria um daemon para rodar como serviço
15
19
  """
@@ -216,6 +220,10 @@ class ServicerConfig(Servicer):
216
220
  elif [chave for chave in config if config[chave] is None]:
217
221
  raise ValueError(f'Argumento "config" ({config}) possui as seguintes chaves com valores inválidos [None]: {[chave for chave in config if config[chave] is None]:}')
218
222
 
223
+ _cmd_servico = len(sys.argv) > 1 and sys.argv[1] in ['install', 'remove', 'start', 'stop', 'restart', 'debug']
224
+ if (servicemanager is None or win32serviceutil is None) and (_cmd_servico or self.ambiente == 'Serviço'):
225
+ raise RuntimeError('Gerenciamento de serviço Windows indisponível nesta plataforma (pywin32 ausente); no Linux use systemd')
226
+
219
227
  ServicoWindows._svc_name_ = config['name']
220
228
  ServicoWindows._svc_display_name_ = config['display_name']
221
229
  ServicoWindows._svc_description_ = config['description']
@@ -7,12 +7,38 @@ from pathlib import Path
7
7
  import subprocess
8
8
  import platform
9
9
  import socket
10
- import servicemanager
11
- import win32serviceutil
12
- import win32service
13
- import win32event
10
+ # pywin32 só existe no Windows. Em outras plataformas o módulo continua
11
+ # importável (o __init__ do pacote o importa sempre), mas a classe não pode
12
+ # ser instanciada — no Linux o equivalente é uma unit do systemd.
13
+ try:
14
+ import servicemanager
15
+ import win32serviceutil
16
+ import win32service
17
+ import win32event
18
+ _BaseServico = win32serviceutil.ServiceFramework
19
+ PYWIN32_DISPONIVEL = True
20
+ except ImportError: # pragma: no cover - depende da plataforma
21
+ servicemanager = None # type: ignore[assignment]
22
+ win32serviceutil = None # type: ignore[assignment]
23
+ win32service = None # type: ignore[assignment]
24
+ win32event = None # type: ignore[assignment]
25
+ PYWIN32_DISPONIVEL = False
14
26
 
15
- class ServicoWindows(win32serviceutil.ServiceFramework): # Gerencia a execução/instalação como serviço do windows
27
+ class _BaseServico: # type: ignore[no-redef]
28
+ """
29
+ Base substituta usada quando o pywin32 não está disponível.
30
+
31
+ Permite importar o pacote fora do Windows; qualquer tentativa de
32
+ instanciar o serviço falha com mensagem explícita.
33
+
34
+ Example:
35
+ ServicoWindows(args) # RuntimeError fora do Windows
36
+ """
37
+
38
+ def __init__(self, *args, **kwargs) -> None:
39
+ raise RuntimeError("ServicoWindows requer pywin32 (somente Windows); no Linux use systemd")
40
+
41
+ class ServicoWindows(_BaseServico): # type: ignore[misc, valid-type] # Gerencia a execução/instalação como serviço do windows
16
42
  """
17
43
  #### Classe **ServicoWindows**
18
44
 
@@ -41,6 +67,8 @@ O objetos mandatórios a serem inicilizados pelo **ConfigManager** são:
41
67
  _svc_display_name_ = None
42
68
  _svc_description_ = None
43
69
  def __init__(self, args):
70
+ if not PYWIN32_DISPONIVEL:
71
+ raise RuntimeError("ServicoWindows requer pywin32 (somente Windows); no Linux use systemd")
44
72
  self.log = ConfigManager.get('log')
45
73
  self.daemon = ConfigManager.get('daemon')
46
74
  self.config = ConfigManager.get('config')
@@ -357,4 +385,4 @@ set PYTHONPATH=%~dp0
357
385
  finally:
358
386
  log.info('daemon encerrado!')
359
387
  #
360
- #
388
+ #
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "lbxtoolkit"
3
- version = "2.4.4"
3
+ version = "2.4.5"
4
4
  description = "Caixa ferramentas da LBX para automações de processos (RPA), integrações, utilitários Windows e componentes reutilizáveis."
5
5
  authors = ["Cristiano P. Ferrari <cristiano.ferrari@lbx.com.br>"]
6
6
  readme = "README.md"
@@ -16,8 +16,8 @@ requests = ">=2.32.3"
16
16
  selenium = ">=4.23.1"
17
17
  webdriver-manager = ">=4.0.2"
18
18
  validators = ">=0.33.0"
19
- pygetwindow = ">=0.0.9"
20
- pywin32 = ">=306"
19
+ pygetwindow = { version = ">=0.0.9", markers = "sys_platform == 'win32'" }
20
+ pywin32 = { version = ">=306", markers = "sys_platform == 'win32'" }
21
21
 
22
22
 
23
23
  [tool.poetry.group.docs.dependencies]
File without changes