CNN-BiGRU / scripts /push_to_hf.py
PowerMachine's picture
v2.0: EWC + Context Window + RoPE + TransformerBlock + bug fixes (attempt 1)
eb1f6a4 verified
Raw History Blame
7.38 kB
"""push_to_hf.py — Envia scripts em lote ao HF repositório 'CNN-BiGRU' (v2.0).
Executa:
1. Cria repositório 'PowerMachine/CNN-BiGRU' (se não existir)
2. Envia todos os scripts .py, README.md, requirements.txt, docs/
usando `upload_folder` (upload em lote eficiente)
3. SOBRESCREVE módulos desatualizados (default do upload_folder)
4. Remove HF_TOKEN do ambiente após o uso (requisito do usuário)
V2.0 melhorias:
- Usa `upload_folder` (single call) em vez de `upload_file` em loop
- Filtra arquivos por extensão e por padrões (ignora caches, logs, etc.)
- Mantém árvore de pastas e subpastas conforme existente localmente
- Log mais detalhado de sucesso/falha
- Retry automático em caso de falha transitória
Usage:
HF_TOKEN=hf_xxx python push_to_hf.py
"""
from __future__ import annotations
import os
import sys
import logging
import time
from pathlib import Path
from typing import List, Set
logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s")
logger = logging.getLogger("push_hf")
REPO_ID = "PowerMachine/CNN-BiGRU"
# Path: scripts/push_to_hf.py -> parent=scripts -> parent=cnn_bigru
PROJECT_DIR = Path(__file__).resolve().parent.parent
# PROJECT_DIR.parent = raiz do projeto
REPO_ROOT = PROJECT_DIR.parent
# Extensões e nomes de arquivo permitidos para upload
ALLOWED_EXTENSIONS: Set[str] = {
".py", ".md", ".txt", ".json", ".yaml", ".yml", ".toml", ".cfg", ".ini", ".sh",
}
# Padrões a ignorar (em qualquer parte do caminho)
IGNORE_PATTERNS: Set[str] = {
"__pycache__", ".pyc", ".pyo", ".pyd",
".git", ".gitignore", ".gitattributes",
"node_modules", ".pytest_cache", ".mypy_cache", ".ruff_cache",
".DS_Store", "Thumbs.db",
"*.log", "*.tmp", "*.swp", "*.bak",
".env", ".venv", "venv", "env",
}
# Arquivos específicos que NUNCA devem ser upados (podem conter tokens/sensíveis)
SENSITIVE_FILENAMES: Set[str] = {
".env", ".env.local", ".env.production", ".env.development",
"secrets.json", "credentials.json", "config.local.json",
"hf_token.txt", "token.txt",
}
def should_ignore(path: Path) -> bool:
"""Verifica se um arquivo deve ser ignorado no upload."""
parts = path.parts
name = path.name.lower()
# Verificar padrões por partes do caminho
for part in parts:
part_lower = part.lower()
if part_lower in IGNORE_PATTERNS:
return True
# Verificar extensões de cache
if part_lower.endswith((".pyc", ".pyo", ".pyd")):
return True
# Verificar nome do arquivo
if name in SENSITIVE_FILENAMES:
return True
# Verificar extensões permitidas
if path.suffix.lower() not in ALLOWED_EXTENSIONS:
# Permitir arquivos sem extensão apenas se forem específicos (README, LICENSE, etc.)
if name not in {"readme", "license", "license-mit", "authors", "contributors"}:
return True
# Verificar padrões com wildcards
for pattern in IGNORE_PATTERNS:
if pattern.startswith("*"):
suffix = pattern[1:]
if name.endswith(suffix):
return True
return False
def collect_files(project_dir: Path) -> List[Path]:
"""Coleta todos os arquivos elegíveis para upload."""
files = []
for path in project_dir.rglob("*"):
if path.is_file() and not should_ignore(path):
files.append(path)
return files
def upload_with_retry(api, folder: Path, repo_id: str, token: str, max_retries: int = 3):
"""Faz upload_folder com retry em caso de falha transitória."""
last_error = None
for attempt in range(1, max_retries + 1):
try:
logger.info(f"Tentativa {attempt}/{max_retries}: upload_folder...")
commit_info = api.upload_folder(
folder_path=str(folder),
repo_id=repo_id,
repo_type="model",
token=token,
commit_message=f"v2.0: EWC + Context Window + RoPE + TransformerBlock + bug fixes (attempt {attempt})",
# Sobrescreve arquivos desatualizados (default)
)
logger.info(f"Upload bem-sucedido: {commit_info}")
return True, None
except Exception as e:
last_error = e
logger.warning(f"Tentativa {attempt} falhou: {e}")
if attempt < max_retries:
wait_time = 2 ** attempt
logger.info(f"Aguardando {wait_time}s antes de tentar novamente...")
time.sleep(wait_time)
return False, last_error
def main():
# Pega HF_TOKEN da variável de ambiente
hf_token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
if not hf_token:
logger.error("HF_TOKEN não definido no ambiente. Abortando.")
return 1
try:
from huggingface_hub import HfApi, create_repo
except ImportError:
logger.error("huggingface_hub não instalado. Execute: pip install huggingface_hub")
return 1
api = HfApi(token=hf_token)
# 1. Cria repositório (se não existir)
try:
repo_url = create_repo(
repo_id=REPO_ID,
token=hf_token,
repo_type="model",
exist_ok=True,
private=False,
)
logger.info(f"Repositório criado/acessado: {repo_url}")
except Exception as e:
logger.error(f"Falha ao criar repositório: {e}")
# Limpa o token mesmo em caso de falha
_cleanup_token()
return 1
# 2. Coleta todos os arquivos para upload
files_to_upload = collect_files(PROJECT_DIR)
logger.info(f"Total de arquivos para upload: {len(files_to_upload)} (de {PROJECT_DIR})")
if not files_to_upload:
logger.warning("Nenhum arquivo elegível para upload.")
_cleanup_token()
return 1
# Log dos arquivos que serão upados
for f in files_to_upload[:10]:
rel = f.relative_to(REPO_ROOT)
logger.info(f" - {rel}")
if len(files_to_upload) > 10:
logger.info(f" ... e mais {len(files_to_upload) - 10} arquivos")
# 3. Upload em lote via upload_folder (muito mais eficiente que upload_file em loop)
success, error = upload_with_retry(
api, folder=PROJECT_DIR, repo_id=REPO_ID, token=hf_token, max_retries=3
)
if success:
logger.info("=" * 60)
logger.info(f"Upload concluído com sucesso!")
logger.info(f"Repo: https://huggingface.co/{REPO_ID}")
logger.info(f"Total de arquivos enviados: {len(files_to_upload)}")
logger.info("=" * 60)
else:
logger.error("=" * 60)
logger.error(f"Falha no upload após retries: {error}")
logger.error("=" * 60)
# 4. Remove HF_TOKEN do ambiente (requisito do usuário)
_cleanup_token()
return 0 if success else 1
def _cleanup_token():
"""Remove todas as variáveis de token HF do ambiente."""
removed = []
for var in ("HF_TOKEN", "HUGGING_FACE_HUB_TOKEN", "HF_HUB_TOKEN"):
if var in os.environ:
del os.environ[var]
removed.append(var)
if removed:
logger.info(f"Tokens removidos do ambiente: {', '.join(removed)}")
else:
logger.info("Nenhum token HF encontrado no ambiente para remover.")
if __name__ == "__main__":
sys.exit(main())