Download cnn_bigru/scripts/push_to_hf.py from PowerMachine/CNN-BiGRU: direct link, hf CLI and curl.
- Browser
- Download file 11.1 kB
-
https://huggingface.co/PowerMachine/CNN-BiGRU/resolve/main/cnn_bigru/scripts/push_to_hf.py
- Command line
-
hf download hf://PowerMachine/CNN-BiGRU/cnn_bigru/scripts/push_to_hf.py
-
curl -L -o push_to_hf.py https://huggingface.co/PowerMachine/CNN-BiGRU/resolve/main/cnn_bigru/scripts/push_to_hf.py
11.1 kB
| """push_to_hf.py — Envia scripts em lote ao HF repositório 'CNN-BiGRU' (v3.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) | |
| 5. Deleta estado salvo do modelo (não envia para lugar algum) | |
| 6. Filtra arquivos sensíveis (.env, tokens, checkpoints, etc.) | |
| V3.0 melhorias: | |
| - Filtra arquivos de estado do modelo (.pt, .pth, .bin, .safetensors) | |
| - Filtra relatórios de monitor local (monitor_reports/) | |
| - Mantém apenas scripts e documentação | |
| - Log mais detalhado | |
| - 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 shutil | |
| 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", | |
| # Estado salvo do modelo — NÃO ENVIAR (requisito do usuário) | |
| "*.pt", "*.pth", "*.bin", "*.safetensors", "*.ckpt", | |
| # Relatórios de monitor local (contém paths e dados locais) | |
| "monitor_reports", "download", "tool-results", "upload", | |
| } | |
| # 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 extensões de estado de modelo | |
| if part_lower.endswith((".pt", ".pth", ".bin", ".safetensors", ".ckpt")): | |
| 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 delete_saved_model_state(project_dir: Path) -> None: | |
| """Deleta todo o estado salvo do modelo (checkpoints, .pt, .pth, etc.). | |
| Requisito do usuário: "apagar o estado salvo do modelo (não enviar para | |
| lugar algum até ordem em contrário)". | |
| """ | |
| deleted = [] | |
| for path in project_dir.rglob("*"): | |
| if not path.is_file(): | |
| continue | |
| name = path.name.lower() | |
| if name.endswith((".pt", ".pth", ".bin", ".safetensors", ".ckpt")): | |
| try: | |
| path.unlink() | |
| deleted.append(path) | |
| logger.info(f" Deletado: {path.relative_to(project_dir.parent)}") | |
| except Exception as e: | |
| logger.warning(f" Falha ao deletar {path}: {e}") | |
| # Deletar diretórios de cache e checkpoints | |
| for d in project_dir.rglob("*"): | |
| if d.is_dir() and d.name.lower() in {"checkpoints", "checkpoint", "model_state", "weights"}: | |
| try: | |
| shutil.rmtree(d) | |
| deleted.append(d) | |
| logger.info(f" Deletado dir: {d.relative_to(project_dir.parent)}") | |
| except Exception as e: | |
| logger.warning(f" Falha ao deletar dir {d}: {e}") | |
| if deleted: | |
| logger.info(f"Estado salvo do modelo deletado: {len(deleted)} itens") | |
| else: | |
| logger.info("Nenhum estado salvo do modelo encontrado para deletar") | |
| 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. | |
| IMPORTANTE: usa path_in_repo='cnn_bigru' para preservar a árvore de pastas | |
| (todos os arquivos ficam sob cnn_bigru/ no repo HF, não na raiz). | |
| """ | |
| last_error = None | |
| for attempt in range(1, max_retries + 1): | |
| try: | |
| logger.info(f"Tentativa {attempt}/{max_retries}: upload_folder (path_in_repo='cnn_bigru')...") | |
| commit_info = api.upload_folder( | |
| folder_path=str(folder), | |
| repo_id=repo_id, | |
| repo_type="model", | |
| token=token, | |
| commit_message=f"v3.0: 9 novos módulos (CyclicReasoning, Medusa, NLG, NLP, MMA, VQVAE2, W8A8, LongContext 1M, Monitor) + bug fixes (attempt {attempt})", | |
| # Sobrescreve arquivos desatualizados (default) | |
| path_in_repo="cnn_bigru", # PRESERVA árvore de pastas | |
| ) | |
| 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 (até 20) | |
| for f in files_to_upload[:20]: | |
| rel = f.relative_to(REPO_ROOT) | |
| logger.info(f" - {rel}") | |
| if len(files_to_upload) > 20: | |
| logger.info(f" ... e mais {len(files_to_upload) - 20} 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) | |
| # 4. Deletar estado salvo do modelo (após upload bem-sucedido) | |
| logger.info("Deletando estado salvo do modelo...") | |
| delete_saved_model_state(PROJECT_DIR) | |
| else: | |
| logger.error("=" * 60) | |
| logger.error(f"Falha no upload após retries: {error}") | |
| logger.error("=" * 60) | |
| # 5. Remove HF_TOKEN do ambiente (requisito do usuário) | |
| _cleanup_token() | |
| # 6. Verifica e remove tokens hardcoded em scripts (requisito do usuário) | |
| _scan_scripts_for_tokens(PROJECT_DIR) | |
| 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.") | |
| def _scan_scripts_for_tokens(project_dir: Path) -> None: | |
| """Verifica se há tokens HF hardcoded em scripts e os remove.""" | |
| # Padrões de token HF (hf_ seguido de 32+ caracteres alfanuméricos) | |
| import re | |
| token_pattern = re.compile(r'hf_[A-Za-z0-9]{20,}') | |
| scanned = 0 | |
| cleaned = 0 | |
| for path in project_dir.rglob("*.py"): | |
| if "push_to_hf.py" in str(path): | |
| continue # skip self | |
| try: | |
| content = path.read_text(encoding="utf-8") | |
| scanned += 1 | |
| if token_pattern.search(content): | |
| # Substituir por placeholder | |
| new_content = token_pattern.sub("HF_TOKEN_FROM_ENV", content) | |
| path.write_text(new_content, encoding="utf-8") | |
| cleaned += 1 | |
| logger.warning(f" Token HF removido de: {path.relative_to(project_dir.parent)}") | |
| except Exception as e: | |
| logger.debug(f" Falha ao escanear {path}: {e}") | |
| if cleaned > 0: | |
| logger.info(f"Scripts escaneados: {scanned}, scripts com token removido: {cleaned}") | |
| else: | |
| logger.info(f"Scripts escaneados: {scanned}, nenhum token hardcoded encontrado.") | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |