---
name: scan_directory
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def scan_directory(root: str, supported_extensions: set[str] | None = None, ignore_dirs: set[str] | None = None, include: str | None = None, exclude: str | None = None, strict: bool = False) -> DirectoryScanResult"
description: "Recorre un arbol de directorios y clasifica cada archivo como procesable o no soportado. Util para validacion pre-importacion de directorios. Ignora dot files, symlinks, archivos vacios y directorios de build/venv/cache predefinidos. Soporta filtros include/exclude con globs."
tags: [directory, scan, filesystem, classification, infra, walk, files]
uses_functions: []
uses_types: [classified_file_py_infra, directory_scan_result_py_infra]
returns: [directory_scan_result_py_infra]
returns_optional: false
error_type: "error_go_core"
imports: [os, pathlib, fnmatch, sys, dataclasses]
params:
  - name: root
    desc: "directorio raiz a escanear"
  - name: supported_extensions
    desc: "conjunto opcional de extensiones soportadas (ej: {'.pdf', '.md'})"
  - name: ignore_dirs
    desc: "conjunto opcional de nombres de directorio a ignorar"
  - name: include
    desc: "patrones glob opcionales separados por coma para incluir archivos"
  - name: exclude
    desc: "patrones glob opcionales separados por coma para excluir archivos"
  - name: strict
    desc: "si True, lanza ValueError si hay archivos no soportados"
output: "instancia DirectoryScanResult con listas processable y unsupported de ClassifiedFile"
tested: true
tests:
  - "directorio con mezcla de archivos"
  - "directorio con dot files"
  - "directorio con subdirs ignorados"
  - "filtros include/exclude"
  - "modo strict"
test_file_path: "python/functions/infra/scan_directory_test.py"
file_path: "python/functions/infra/scan_directory.py"
---

## Ejemplo

```python
from scan_directory import scan_directory

# Escanear directorio de documentos, solo PDF y Markdown
result = scan_directory(
    "/data/proyecto",
    supported_extensions={".pdf", ".md"},
    ignore_dirs={"archive"},
    exclude="*.tmp,drafts/",
    strict=False,
)

print(f"Procesables: {len(result.processable)}")
print(f"No soportados: {len(result.unsupported)}")

for f in result.processable:
    print(f"  {f.rel_path}")
```

## Notas

Funcion impura: realiza I/O de sistema de archivos con `os.walk`.

**Directorios ignorados por defecto (`IGNORE_DIRS`):**
`__pycache__`, `node_modules`, `.git`, `.svn`, `.hg`, `venv`, `.venv`, `env`, `.env`, `.tox`, `.nox`, `.mypy_cache`, `.pytest_cache`, `.ruff_cache`, `dist`, `build`, `.next`, `.nuxt`, `target`, `vendor`.

**Logica de include/exclude:**
- `include`: patrones glob separados por coma (ej: `"*.pdf,*.md"`). Si se provee, solo se incluyen archivos que coincidan con al menos un patron.
- `exclude`: patrones glob separados por coma. Si el patron termina con `/` es un prefijo de path relativo (ej: `"drafts/"`); sin `/` es un glob de nombre (ej: `"*.tmp"`).

**Modo strict:** si `strict=True` y hay archivos no soportados, lanza `ValueError` con la lista de archivos no soportados. Util para pipelines que requieren directorio 100% homogeneo.

**Orden de resultados:** `processable` y `unsupported` se ordenan por `rel_path` ascendente para salida determinista.

Los paths relativos en `ClassifiedFile.rel_path` siempre usan forward slashes (`/`) independientemente del OS.
