Saltar a contenido

Convenciones de Código Python

Type Hints (OBLIGATORIO)

from returns.result import Result

def extract_text(file_path: str, language: str = "spa") -> Result[str, OCRError]:
    """Extrae texto de PDF.

    Args:
        file_path: Ruta absoluta
        language: Código ISO (default: spa)

    Returns:
        Result con texto extraído o OCRError
    """

Naming

Elemento Convención Ejemplo
Clases PascalCase DocumentRepository
Funciones snake_case extract_entities()
Constantes UPPER_SNAKE OCR_TIMEOUT_SECONDS = 300
Privadas _prefijo _normalize_text()
Enums StrEnum class DocumentType(StrEnum)
Booleans is_/has_/can_ is_valid, has_duplicates

Inmutabilidad

@dataclass(frozen=True)
class ProcessDocumentRequest:
    file_path: str
    document_type: DocumentType = DocumentType.TUTELA

Todos los DTOs y value objects usan frozen=True.

Railway-Oriented Programming

from returns.result import Result, Success, Failure

# Composición fluida
result = (
    process_ocr(file_path)
    .bind(validate_text)
    .bind(extract_entities)
    .bind(detect_duplicates)
    .map(build_document)
)

match result:
    case Success(doc): save(doc)
    case Failure(error): log_and_alert(error)

Nunca anidar try/except. Usar Result para errores de negocio.

Commits

Formato: <tipo>(<alcance>): <descripción en español>

Tipos: feat, fix, docs, refactor, test, chore, perf

# ✅ BUENO
git commit -m "feat(ocr): agregar fallback automático a PaddleOCR"

# ❌ MALO — No incluir firma de Claude
git commit -m "feat: add feature

Co-Authored-By: Claude <noreply@anthropic.com>"

Docstrings

Google-style, solo en funciones públicas:

def find_by_id(self, doc_id: str) -> Document | None:
    """Find a document by ID.

    Args:
        doc_id: Document identifier.

    Returns:
        Document if found, None otherwise.
    """

Imports Diferidos en GUI (ADR — S23.1-19)

Los archivos en interfaces/gui/pages/ contienen imports dentro de metodos (no en top-level). Esto es una decision arquitectonica intencional, no un code smell:

# CORRECTO en paginas GUI — import diferido
def _render_validation_form(self, doc: dict) -> None:
    from sherlock_docs.interfaces.gui.config.constants import JUZGADOS_BELLO
    ...

Razon: Streamlit re-ejecuta el modulo completo en cada interaccion del usuario. Los imports pesados (pandas, json, constants grandes) dentro de metodos evitan carga innecesaria en imports que no se usan en cada render cycle.

Donde aplica: Solo en interfaces/gui/pages/. En application/, core/, infrastructure/ y persistence/ los imports DEBEN ser top-level.

No "arreglar": Mover estos imports a top-level puede causar imports circulares y degradar el tiempo de respuesta de la GUI.