коммит вечером

This commit is contained in:
2025-11-06 20:49:24 +03:00
parent d95b5f10d1
commit c50c97bc82
4 changed files with 609 additions and 12 deletions

154
smb_excel.py Normal file
View File

@@ -0,0 +1,154 @@
# smb_excel.py (обновлённый фрагмент)
from io import BytesIO
from typing import Optional, Union, Any, Dict
from smb.SMBConnection import SMBConnection
import pandas as pd
import re
class SMBDownloadError(Exception):
pass
def _normalize_path(p: str) -> str:
"""Убирает случайные r"..."/"...", лишние кавычки и ставит прямые слэши."""
if p is None:
return p
# уберём обрамляющие кавычки/префикс r"..."
m = re.fullmatch(r"""r?["'](.*)["']""", p.strip())
if m:
p = m.group(1)
# заменим \ на /
p = p.replace("\\", "/").lstrip("/") # путь внутри шары не должен начинаться с /
return p
def _try_connect(
remote_name: str,
server_ip: str,
username: str,
password: str,
*,
client_machine_name: str,
domain: str,
port: int
) -> Optional[SMBConnection]:
conn = SMBConnection(
username,
password,
client_machine_name,
remote_name, # ВАЖНО: remote_name — имя сервера (или IP как fallback)
domain=domain,
use_ntlm_v2=True,
is_direct_tcp=True
)
try:
if conn.connect(server_ip, port):
return conn
except Exception:
pass
return None
def _connect_smb(
server_name: str,
server_ip: str,
username: str,
password: str,
*,
client_machine_name: str = "client",
domain: str = "",
port: int = 445,
) -> SMBConnection:
"""
Пробуем два варианта remote_name: (1) реальное имя сервера, (2) IP.
"""
for remote_name in (server_name, server_ip):
if not remote_name:
continue
conn = _try_connect(
remote_name=remote_name,
server_ip=server_ip,
username=username,
password=password,
client_machine_name=client_machine_name,
domain=domain,
port=port,
)
if conn:
return conn
raise SMBDownloadError(
f"Не удалось подключиться к SMB {server_ip}:{port}. "
f"Проверь server_name (реальное имя хоста), домен/логин и доступность порта 445."
)
def fetch_smb_file(
*,
server_name: str,
server_ip: str,
share_name: str,
file_path_in_share: str,
username: str,
password: str,
client_machine_name: str = "client",
domain: str = "",
port: int = 445,
) -> BytesIO:
"""
Возвращает содержимое файла из SMB как BytesIO.
"""
file_path_in_share = _normalize_path(file_path_in_share)
conn: Optional[SMBConnection] = None
try:
conn = _connect_smb(
server_name=server_name,
server_ip=server_ip,
username=username,
password=password,
client_machine_name=client_machine_name,
domain=domain,
port=port,
)
# Быстрая проверка существования каталога/файла
parent = "/".join(file_path_in_share.split("/")[:-1]) or ""
# listPath кидает исключение, если каталога/шары нет — получим понятную ошибку
if parent:
conn.listPath(share_name, parent)
buf = BytesIO()
conn.retrieveFile(share_name, file_path_in_share, buf)
buf.seek(0)
return buf
except Exception as e:
raise SMBDownloadError(
f"Ошибка скачивания {share_name}/{file_path_in_share}: {e}"
) from e
finally:
if conn:
try:
conn.close()
except Exception:
pass
def read_excel_from_smb(
*,
server_name: str,
server_ip: str,
share_name: str,
file_path_in_share: str,
username: str,
password: str,
client_machine_name: str = "client",
domain: str = "",
port: int = 445,
sheet_name: Optional[Union[str, int, list]] = None,
**read_excel_kwargs: Dict[str, Any],
) -> pd.DataFrame:
file_obj = fetch_smb_file(
server_name=server_name,
server_ip=server_ip,
share_name=share_name,
file_path_in_share=file_path_in_share,
username=username,
password=password,
client_machine_name=client_machine_name,
domain=domain,
port=port,
)
return pd.read_excel(file_obj, sheet_name=sheet_name, **read_excel_kwargs)