Skip to main content

A robust, flexible configuration management system for Python applications

Project description

config-loader 🚀

Una librería robusta y flexible para gestionar configuración en aplicaciones Python. Carga configuración desde múltiples archivos YAML, aplica overrides desde variables de entorno y valida todo con Pydantic.

Versión: 0.1.0
Estado: Alpha
Licencia: MIT


📋 Tabla de Contenidos


✨ Características

Carga desde múltiples YAML - Combina archivos base + overrides con precedencia clara
Overrides de variables de entorno - Aplica ENV vars con convención PREFIX__NESTED__KEY
Validación con Pydantic - Type-safe con mensajes de error claros
Manejo granular de errores - Excepciones específicas para cada tipo de error
Tipos seguros - Type hints completos para mejor IDE support
Sin mutaciones - Las funciones retornan nuevos objetos, no modifican los originales


📦 Instalación

Instalación básica

pip install config-loader

Instalación desde repositorio (desarrollo)

git clone https://github.com/dev-brainstack/config-loader.git
cd config-loader
pip install -e .

Instalación con dependencias de desarrollo

pip install -e ".[dev]"

🚀 Uso Rápido

Ejemplo Básico

from pydantic import BaseModel
from config_loader import load_config

# 1. Define tu modelo de configuración
class DatabaseConfig(BaseModel):
    host: str
    port: int = 5432
    username: str
    password: str

class AppConfig(BaseModel):
    debug: bool = False
    database: DatabaseConfig

# 2. Crea archivos YAML
# config/base.yaml
# debug: false
# database:
#   host: localhost
#   port: 5432
#   username: admin
#   password: secret

# 3. Carga la configuración
config = load_config(
    AppConfig,
    paths=["config/base.yaml"],
    use_env=True,
    env_prefix="APP"
)

# 4. Usa la configuración
print(config.debug)  # False
print(config.database.host)  # localhost

📚 Ejemplos Detallados

Ejemplo 1: Configuración Simple

Entrada - Archivo YAML (config.yaml):

app_name: MyApp
debug: true
port: 8000

Código Python:

from pydantic import BaseModel
from config_loader import load_config

class Config(BaseModel):
    app_name: str
    debug: bool
    port: int

config = load_config(Config, paths=["config.yaml"])

# Salida
print(config.app_name)  # "MyApp"
print(config.debug)     # True
print(config.port)      # 8000

Ejemplo 2: Múltiples Archivos YAML con Overrides

Entrada - Archivos YAML:

config/base.yaml:

app_name: MyApp
debug: false
database:
  host: localhost
  port: 5432
  username: admin

config/production.yaml:

debug: false
database:
  host: prod-db.example.com
  port: 5432

Código Python:

from pydantic import BaseModel
from typing import Dict, Any
from config_loader import load_config

class AppConfig(BaseModel):
    app_name: str
    debug: bool
    database: Dict[str, Any]

# Los archivos se cargan en orden, el último sobrescribe al anterior
config = load_config(
    AppConfig,
    paths=[
        "config/base.yaml",
        "config/production.yaml"
    ]
)

# Salida
print(config.app_name)           # "MyApp" (del base.yaml)
print(config.database["host"])   # "prod-db.example.com" (del production.yaml)
print(config.database["port"])   # 5432 (del production.yaml)
print(config.database["username"]) # "admin" (del base.yaml, no sobrescrito)

Ejemplo 3: Overrides desde Variables de Entorno

Entrada - Variables de Entorno:

export APP__DEBUG=true
export APP__DATABASE__HOST=env-db.example.com
export APP__DATABASE__PORT=3306
export APP__DATABASE__USERNAME=envuser
export APP__DATABASE__PASSWORD=envpass

Código Python:

from pydantic import BaseModel
from typing import Dict, Any
from config_loader import load_config

class AppConfig(BaseModel):
    debug: bool = False
    database: Dict[str, Any]

# use_env=True habilita los overrides desde variables de entorno
config = load_config(
    AppConfig,
    paths=["config/base.yaml"],
    use_env=True,
    env_prefix="APP"
)

# Salida
print(config.debug)                  # True (de ENV)
print(config.database["host"])       # "env-db.example.com" (de ENV)
print(config.database["port"])       # 3306 (de ENV, convertido a int)
print(config.database["username"])   # "envuser" (de ENV)
print(config.database["password"])   # "envpass" (de ENV)

Ejemplo 4: Combinación Completa (YAML + ENV)

Entrada - Archivo YAML (config/base.yaml):

app_name: MyApp
debug: false
database:
  host: localhost
  port: 5432
  username: admin
  password: secret
cache:
  enabled: true
  ttl: 3600

Entrada - Variables de Entorno:

export APP__DEBUG=true
export APP__DATABASE__HOST=prod-db.example.com
export APP__CACHE__TTL=7200

Código Python:

from pydantic import BaseModel
from typing import Dict, Any
from config_loader import load_config

class CacheConfig(BaseModel):
    enabled: bool
    ttl: int

class DatabaseConfig(BaseModel):
    host: str
    port: int
    username: str
    password: str

class AppConfig(BaseModel):
    app_name: str
    debug: bool
    database: DatabaseConfig
    cache: CacheConfig

config = load_config(
    AppConfig,
    paths=["config/base.yaml"],
    use_env=True,
    env_prefix="APP"
)

# Salida
print(config.app_name)              # "MyApp" (del YAML)
print(config.debug)                 # True (de ENV, sobrescribe YAML)
print(config.database.host)         # "prod-db.example.com" (de ENV)
print(config.database.port)         # 5432 (del YAML, no sobrescrito)
print(config.database.username)     # "admin" (del YAML)
print(config.database.password)     # "secret" (del YAML)
print(config.cache.enabled)         # True (del YAML)
print(config.cache.ttl)             # 7200 (de ENV, sobrescribe YAML)

Ejemplo 5: Coerción Automática de Tipos

Las variables de entorno son siempre strings, pero config-loader las convierte automáticamente:

Entrada - Variables de Entorno:

export APP__DEBUG=true              # String "true" → Boolean True
export APP__PORT=8000               # String "8000" → Integer 8000
export APP__TIMEOUT=30.5            # String "30.5" → Float 30.5
export APP__NAME=MyApp              # String "MyApp" → String "MyApp"

Código Python:

from pydantic import BaseModel
from config_loader import load_config

class AppConfig(BaseModel):
    debug: bool
    port: int
    timeout: float
    name: str

config = load_config(
    AppConfig,
    paths=[],  # Sin archivos YAML
    use_env=True,
    env_prefix="APP"
)

# Salida
print(config.debug)     # True (bool)
print(config.port)      # 8000 (int)
print(config.timeout)   # 30.5 (float)
print(config.name)      # "MyApp" (str)

🛠️ Desarrollo

Comandos Rápidos con Makefile

El proyecto incluye un Makefile para facilitar tareas comunes de desarrollo:

# Ver todos los comandos disponibles
make help

# Ejecutar verificaciones de calidad (Black, Flake8, Mypy)
make lint

# Auto-formatear código con Black
make format

# Ejecutar tests con reporte de cobertura
make test

# Construir paquete de distribución
make build

# Limpiar artefactos de build y cache
make clean

# Instalar paquete en modo producción
make install

# Instalar paquete con dependencias de desarrollo
make dev-install

Flujo de Desarrollo Recomendado

  1. Hacer cambios en el código
  2. Formatear automáticamente:
    make format
    
  3. Verificar calidad:
    make lint
    
  4. Ejecutar tests:
    make test
    
  5. Si todo pasa, hacer commit

🛠️ Configuración del Ambiente de Desarrollo

Requisitos Previos

  • Python 3.7 o superior
  • pip (gestor de paquetes de Python)
  • git (para clonar el repositorio)

Pasos de Instalación

1. Clonar el repositorio

git clone https://github.com/dev-brainstack/config-loader.git
cd config-loader

2. Crear un ambiente virtual (recomendado)

# En Linux/macOS
python3 -m venv .venv
source .venv/bin/activate

# En Windows
python -m venv .venv
.venv\Scripts\activate

3. Instalar dependencias de desarrollo

pip install -e ".[dev]"

Esto instala:

  • config-loader en modo editable (cambios se reflejan inmediatamente)
  • pytest - Framework de testing
  • pytest-cov - Cobertura de código
  • black - Formateador de código
  • flake8 - Linter
  • mypy - Type checker

4. Verificar la instalación

# Ejecutar tests
pytest

# Ver cobertura de código
pytest --cov=src --cov-report=html

# Verificar formato de código
black --check src/

# Ejecutar linter
flake8 src/

# Type checking
mypy src/

Estructura del Proyecto

config-loader/
├── .clinerules/              # Documentación interna
├── .gitignore
├── pyproject.toml            # Configuración del proyecto
├── README.md                 # Este archivo
├── requirements.txt          # Dependencias (alternativa a pip)
├── src/
│   └── config_loader/        # Código fuente
│       ├── __init__.py       # API pública
│       ├── loader.py         # Orquestación principal
│       ├── env.py            # Manejo de variables de entorno
│       ├── exceptions.py     # Excepciones personalizadas
│       ├── types.py          # Definiciones de tipos
│       └── utils.py          # Utilidades internas
├── test/
│   └── test_loader.py        # Suite de tests (60+ tests)
└── openspec/                 # Documentación de cambios

Comandos Útiles para Desarrollo

# Ejecutar todos los tests
pytest

# Ejecutar tests con salida detallada
pytest -v

# Ejecutar un archivo de test específico
pytest test/test_loader.py

# Ejecutar un test específico
pytest test/test_loader.py::test_load_config_simple

# Ver cobertura de código
pytest --cov=src --cov-report=term-missing

# Generar reporte HTML de cobertura
pytest --cov=src --cov-report=html
# Abre htmlcov/index.html en el navegador

# Formatear código con black
black src/ test/

# Verificar formato sin cambiar archivos
black --check src/ test/

# Ejecutar linter
flake8 src/ test/

# Type checking
mypy src/

# Ejecutar todo (tests + coverage + lint + type check)
pytest && black --check src/ && flake8 src/ && mypy src/

Flujo de Desarrollo Típico

  1. Crear una rama para tu cambio:

    git checkout -b feature/mi-cambio
    
  2. Hacer cambios en el código

  3. Ejecutar tests para verificar:

    pytest
    
  4. Formatear código:

    black src/
    
  5. Verificar linting:

    flake8 src/
    
  6. Hacer commit:

    git add .
    git commit -m "Descripción del cambio"
    
  7. Push a GitHub:

    git push origin feature/mi-cambio
    

📖 API Reference

load_config()

Función principal para cargar y validar configuración.

def load_config(
    model_class: Type[T],
    paths: List[str],
    use_env: bool = True,
    env_prefix: str = ""
) -> T:
    """
    Carga configuración desde múltiples YAML + ENV + Validación.

    Args:
        model_class: Clase Pydantic que define el esquema de configuración
        paths: Lista de rutas a archivos YAML (orden = prioridad)
        use_env: Habilitar overrides desde variables de entorno (default: True)
        env_prefix: Prefijo para filtrar variables de entorno (default: "")

    Returns:
        Instancia del modelo validado

    Raises:
        ConfigFileNotFoundError: Si un archivo YAML no existe
        ConfigParseError: Si hay error al parsear YAML
        ConfigTypeError: Si la estructura no es válida
        ConfigValidationError: Si la validación Pydantic falla
    """

Ejemplo:

config = load_config(
    AppConfig,
    paths=["config/base.yaml", "config/production.yaml"],
    use_env=True,
    env_prefix="APP"
)

Excepciones

ConfigError (base)

Excepción base para todos los errores de configuración.

ConfigFileNotFoundError

Se lanza cuando un archivo YAML no existe.

try:
    config = load_config(AppConfig, paths=["nonexistent.yaml"])
except ConfigFileNotFoundError as e:
    print(f"Archivo no encontrado: {e.path}")

ConfigParseError

Se lanza cuando hay error al parsear YAML.

try:
    config = load_config(AppConfig, paths=["invalid.yaml"])
except ConfigParseError as e:
    print(f"Error parseando {e.path}: {e.original_error}")

ConfigTypeError

Se lanza cuando la estructura no es válida.

try:
    config = load_config(AppConfig, paths=["config.yaml"])
except ConfigTypeError as e:
    print(f"Se esperaba {e.expected}, se recibió {e.received}")

ConfigValidationError

Se lanza cuando la validación Pydantic falla.

try:
    config = load_config(AppConfig, paths=["config.yaml"])
except ConfigValidationError as e:
    print(f"Error de validación: {e.raw_message}")

⚠️ Manejo de Errores

Ejemplo Completo

from config_loader import load_config, ConfigError
from config_loader.exceptions import (
    ConfigFileNotFoundError,
    ConfigParseError,
    ConfigValidationError
)

try:
    config = load_config(
        AppConfig,
        paths=["config/base.yaml"],
        use_env=True,
        env_prefix="APP"
    )
except ConfigFileNotFoundError as e:
    print(f"❌ Archivo no encontrado: {e.path}")
    exit(1)
except ConfigParseError as e:
    print(f"❌ Error parseando YAML: {e.original_error}")
    exit(1)
except ConfigValidationError as e:
    print(f"❌ Configuración inválida: {e.raw_message}")
    exit(1)
except ConfigError as e:
    print(f"❌ Error de configuración: {e}")
    exit(1)

print("✅ Configuración cargada exitosamente")

📋 Convenciones

Convención de Variables de Entorno

Las variables de entorno siguen la convención PREFIX__NESTED__KEY:

APP__DEBUG=true
APP__DATABASE__HOST=localhost
APP__DATABASE__PORT=5432
APP__CACHE__REDIS__HOST=redis.local

Se convierte en:

{
    "debug": True,
    "database": {
        "host": "localhost",
        "port": 5432
    },
    "cache": {
        "redis": {
            "host": "redis.local"
        }
    }
}

Reglas de Coerción de Tipos

Las variables de entorno se convierten automáticamente:

Valor ENV Tipo Detectado Resultado
"true" o "false" Boolean True o False
"123" Integer 123
"45.67" Float 45.67
"hello" String "hello"

Precedencia de Configuración

  1. Archivos YAML - Base (primer archivo)
  2. Archivos YAML - Overrides (archivos posteriores sobrescriben anteriores)
  3. Variables de Entorno - Máxima prioridad (sobrescriben todo)
base.yaml → override.yaml → ENV vars
   ↓            ↓            ↓
   └────────────┴────────────┘
         Configuración Final

🧪 Testing

El proyecto incluye una suite completa de tests con >90% de cobertura.

# Ejecutar todos los tests
pytest

# Ver cobertura detallada
pytest --cov=src --cov-report=term-missing

# Generar reporte HTML
pytest --cov=src --cov-report=html

🔄 GitHub Actions Pipelines

El proyecto utiliza dos pipelines de GitHub Actions para automatizar la validación y el lanzamiento de versiones.

CI Pipeline

Cuándo se ejecuta:

  • En cada Pull Request (creación y actualizaciones)
  • En cada push a la rama main

Qué valida:

  • ✅ Formato de código (Black)
  • ✅ Linting (Flake8)
  • ✅ Tests unitarios e integración
  • ✅ Cobertura de código

Requisitos:

  • Python 3.9
  • Todas las dependencias de desarrollo instaladas

Si falla:

  • El Pull Request no puede ser mergeado a main
  • Se bloquea el merge hasta que se corrijan los errores

Pasos del pipeline:

  1. Checkout del código
  2. Setup de Python 3.9
  3. Instalación de dependencias (pip install -e ".[dev]")
  4. Verificación de formato (make format)
  5. Ejecución de tests (make test)

Release Pipeline

Cuándo se ejecuta:

  • Solo en pushes a la rama main (después de merges)
  • NO se ejecuta en Pull Requests

Qué hace:

  • Analiza los commits desde el último release
  • Determina el tipo de versión a crear (major, minor, patch)
  • Crea un tag de versión en GitHub
  • Publica el paquete en PyPI
  • Genera notas de release automáticamente

Requisitos:

  • Python 3.9
  • Tokens de autenticación (GH_TOKEN, PYPI_API_TOKEN)

Cómo funciona:

  • Lee los mensajes de commit
  • Usa semantic versioning para determinar el bump de versión
  • Crea releases automáticamente basadas en los tipos de commits

Comparación de Pipelines

Aspecto CI Pipeline Release Pipeline
Trigger PR + push a main Push a main
Propósito Validar código Crear releases
Validaciones Format, Lint, Tests Tests + Semantic Release
Bloquea merge No
Crea releases No
Publica a PyPI No

Flujo de Ejecución

Developer crea PR
    ↓
CI Pipeline ejecuta (format, lint, tests)
    ↓
¿Pasa CI? → No → PR bloqueado, developer corrige
    ↓ Sí
Code review y aprobación
    ↓
Merge a main
    ↓
Release Pipeline ejecuta
    ↓
Analiza commits desde último release
    ↓
¿Hay cambios que justifiquen release? → No → Fin
    ↓ Sí
Determina versión (feat→minor, fix→patch)
    ↓
Crea tag y release en GitHub
    ↓
Publica en PyPI
    ↓
Fin

🌿 Branching Strategy

El proyecto utiliza una estrategia de branching simple pero efectiva.

Estructura de Branches

Main Branch

  • Rama principal donde reside el código en producción
  • Solo acepta cambios a través de Pull Requests
  • Todos los commits en main deben pasar CI
  • Protegida contra pushes directos

Feature Branches

  • Ramas temporales para desarrollar nuevas características o fixes
  • Se crean desde main
  • Se eliminan después de mergear a main
  • Convención de nombres: feature/<descripción> o fix/<descripción>

Flujo de Trabajo

1. Crear rama desde main
   git checkout -b feature/nueva-caracteristica

2. Hacer cambios y commits
   git add .
   git commit -m "feat: agregar nueva caracteristica"

3. Push a GitHub
   git push origin feature/nueva-caracteristica

4. Crear Pull Request
   - Describe los cambios
   - Espera a que CI pase
   - Solicita revisión de código

5. Code Review
   - Otros desarrolladores revisan
   - Sugieren cambios si es necesario
   - Aprueban cuando está listo

6. Merge a main
   - Squash o merge según preferencia
   - CI ejecuta nuevamente
   - Release pipeline se ejecuta si hay cambios que justifiquen release

7. Eliminar rama
   - GitHub ofrece eliminar automáticamente
   - O manualmente: git branch -d feature/nueva-caracteristica

Reglas de Branch Protection

  • ✅ Requiere Pull Request para cambios
  • ✅ Requiere que CI pase antes de merge
  • ✅ Requiere revisión de código (recomendado)
  • ✅ Bloquea pushes directos a main

Mejores Prácticas

  • Mantén branches pequeñas y enfocadas
  • Crea un PR por cada feature/fix
  • Actualiza tu rama con main antes de mergear
  • Resuelve conflictos localmente antes de push
  • Elimina branches después de mergear

📝 Commit Conventions

El proyecto utiliza Semantic Commit Conventions para automatizar el versionado.

Formato de Commits

<tipo>: <descripción>

[cuerpo opcional]

[pie opcional]

Ejemplo:

feat: agregar soporte para archivos JSON

Permite cargar configuración desde archivos JSON además de YAML.
Implementa parser JSON con validación de esquema.

BREAKING CHANGE: El parámetro 'format' ahora es requerido

Tipos de Commits

Tipo Descripción ¿Release? Versión
feat Nueva característica ✅ Sí Minor (1.0.0 → 1.1.0)
fix Corrección de bug ✅ Sí Patch (1.0.0 → 1.0.1)
perf Mejora de performance ✅ Sí Patch (1.0.0 → 1.0.1)
docs Cambios en documentación ❌ No -
chore Tareas de mantenimiento ❌ No -
test Cambios en tests ❌ No -
refactor Refactorización de código ❌ No -

Ejemplos de Commits

Feature (crea release minor):

feat: agregar soporte para TOML
feat: implementar hot-reloading de configuración
feat: agregar validación de esquema JSON

Fix (crea release patch):

fix: corregir parsing de YAML anidado
fix: resolver memory leak en merge profundo
fix: manejar variables de entorno vacías

Performance (crea release patch):

perf: optimizar merge de diccionarios grandes
perf: cachear archivos YAML parseados

Documentation (NO crea release):

docs: actualizar README con ejemplos
docs: agregar guía de contribución
docs: documentar API de excepciones

Chore (NO crea release):

chore: actualizar dependencias
chore: configurar pre-commit hooks
chore: limpiar código legacy

Test (NO crea release):

test: agregar tests para edge cases
test: aumentar cobertura a 95%
test: agregar tests de integración

Refactor (NO crea release):

refactor: simplificar lógica de merge
refactor: extraer funciones comunes
refactor: mejorar nombres de variables

Breaking Changes

Para cambios que rompen compatibilidad, agrega BREAKING CHANGE: en el cuerpo:

feat: cambiar firma de load_config

BREAKING CHANGE: El parámetro 'env_prefix' ahora es requerido

Esto crea un release major (1.0.0 → 2.0.0).


🔄 Development Workflow

Flujo completo de desarrollo desde idea hasta producción.

Paso 1: Crear una Rama

# Actualizar main
git checkout main
git pull origin main

# Crear rama para tu trabajo
git checkout -b feature/mi-caracteristica

Paso 2: Hacer Cambios

# Editar archivos
# Agregar tests
# Actualizar documentación

Paso 3: Commit Local

# Ver cambios
git status

# Agregar cambios
git add .

# Commit con mensaje semántico
git commit -m "feat: agregar nueva caracteristica"

Paso 4: Verificar Localmente

# Ejecutar tests
make test

# Verificar formato
make format

# Verificar linting
make lint

Paso 5: Push a GitHub

# Push de la rama
git push origin feature/mi-caracteristica

Paso 6: Crear Pull Request

  • Ve a GitHub
  • Haz click en "Compare & pull request"
  • Describe los cambios
  • Espera a que CI pase

Paso 7: Code Review

  • Otros desarrolladores revisan
  • Responde a comentarios
  • Haz cambios si es necesario
  • Solicita re-review

Paso 8: Merge

Una vez aprobado:

# GitHub: Click en "Merge pull request"
# O desde línea de comandos:
git checkout main
git pull origin main
git merge feature/mi-caracteristica
git push origin main

Paso 9: Cleanup

# Eliminar rama local
git branch -d feature/mi-caracteristica

# Eliminar rama remota
git push origin --delete feature/mi-caracteristica

Qué Sucede Después del Merge

  1. CI Pipeline ejecuta - Valida el código en main
  2. Release Pipeline ejecuta - Analiza commits
  3. Si hay cambios que justifiquen release:
    • Crea nuevo tag de versión
    • Publica en PyPI
    • Genera release notes en GitHub

📊 Semantic Versioning

El proyecto utiliza Semantic Versioning (SemVer) para versiones.

Formato de Versión

MAJOR.MINOR.PATCH
  ↓      ↓      ↓
  1      2      3
  • MAJOR: Cambios incompatibles (breaking changes)
  • MINOR: Nuevas características (backward compatible)
  • PATCH: Correcciones de bugs (backward compatible)

Ejemplos de Progresión

Scenario 1: Múltiples fixes

1.0.0
  ↓ (fix: corregir parsing)
1.0.1
  ↓ (fix: manejar edge case)
1.0.2

Scenario 2: Feature + fixes

1.0.0
  ↓ (feat: agregar JSON support)
1.1.0
  ↓ (fix: corregir JSON parsing)
1.1.1

Scenario 3: Breaking change

1.0.0
  ↓ (feat: cambiar API - BREAKING CHANGE)
2.0.0

Cómo Funciona el Versionado Automático

  1. Merge a main → Release Pipeline ejecuta
  2. Analiza commits desde último tag
  3. Determina tipo de cambio:
    • ¿Hay feat:? → MINOR bump
    • ¿Hay fix: o perf:? → PATCH bump
    • ¿Solo docs:, chore:, etc.? → Sin release
  4. Crea nuevo tag con versión calculada
  5. Publica en PyPI con nueva versión

Verificar Versión Actual

# Ver último tag
git describe --tags

# Ver todos los tags
git tag -l

# Ver releases en GitHub
# https://github.com/dev-brainstack/config-loader/releases

✨ Best Practices

Recomendaciones para trabajar efectivamente con el proyecto.

Commits

  • Commits atómicos: Un cambio lógico por commit
  • Mensajes claros: Describe QUÉ y POR QUÉ
  • Usa tipos semánticos: feat, fix, docs, etc.
  • Minúsculas: feat: no Feat:
  • Evita: Commits genéricos como "fix stuff" o "update"

Buen commit:

feat: agregar validación de esquema JSON

Implementa validación de esquema JSON usando jsonschema.
Permite a usuarios validar configuración contra esquemas.

Mal commit:

fix stuff

Branches

  • Nombres descriptivos: feature/json-support, fix/yaml-parsing
  • Ramas cortas: Completa en 1-2 días
  • Actualiza desde main: Antes de mergear
  • Evita: Branches de larga vida, nombres genéricos

Pull Requests

  • Descripción clara: Explica QUÉ y POR QUÉ
  • PRs pequeños: Más fáciles de revisar
  • Tests incluidos: Nuevas features deben tener tests
  • CI debe pasar: Antes de merge
  • Evita: PRs gigantes, sin descripción

Buen PR:

## Descripción
Agrega soporte para archivos de configuración JSON.

## Cambios
- Implementa parser JSON
- Agrega validación de esquema
- Incluye 15 nuevos tests

## Testing
- Todos los tests pasan
- Cobertura aumenta a 95%

Code Review

  • Sé constructivo: Sugiere mejoras, no critiques
  • Sé rápido: Revisa en 24 horas
  • Aprende: Usa reviews para aprender
  • Evita: Bloquear sin razón, comentarios vagos

Testing

  • Tests para nuevas features: Siempre
  • Tests para bugs: Antes de fix
  • Mantén cobertura >90%: Objetivo del proyecto
  • Tests claros: Nombres descriptivos
  • Evita: Tests que pasan por suerte

Documentación

  • Actualiza README: Si cambias comportamiento
  • Docstrings claros: En funciones públicas
  • Ejemplos: Para features complejas
  • Evita: Documentación desactualizada

Errores Comunes

  1. Commit directo a main

    • ❌ Incorrecto: git push origin main
    • ✅ Correcto: Crear PR, pasar CI, mergear
  2. Mensaje de commit vago

    • ❌ Incorrecto: fix: bug
    • ✅ Correcto: fix: corregir parsing de YAML anidado
  3. PR sin tests

    • ❌ Incorrecto: Feature sin tests
    • ✅ Correcto: Feature + tests + documentación
  4. Ignorar CI failures

    • ❌ Incorrecto: Mergear con CI rojo
    • ✅ Correcto: Corregir errores, pasar CI
  5. Branches no actualizadas

    • ❌ Incorrecto: Mergear sin actualizar desde main
    • ✅ Correcto: git rebase origin/main antes de merge

🔧 Troubleshooting

Soluciones para problemas comunes.

CI Pipeline Failures

Error: Format Check Failed

Problema: Black detecta código mal formateado

Solución:

# Auto-formatear código
make format

# O manualmente
black src/ test/

# Commit cambios
git add .
git commit -m "style: formatear código"
git push

Error: Tests Failed

Problema: Uno o más tests fallan

Solución:

# Ver qué tests fallan
pytest -v

# Ver detalles del error
pytest -v --tb=long

# Ejecutar test específico
pytest test/test_loader.py::test_name -v

# Corregir código
# ...

# Verificar que pasa
pytest

# Commit
git add .
git commit -m "fix: corregir test fallido"
git push

Error: Linting Failed

Problema: Flake8 detecta problemas de estilo

Solución:

# Ver errores
flake8 src/

# Corregir manualmente o con herramientas
# Algunos errores se pueden auto-corregir con black

# Verificar
flake8 src/

# Commit
git add .
git commit -m "style: corregir linting"
git push

Merge Conflicts

Problema: Conflicto al mergear rama

Solución:

# Actualizar rama desde main
git fetch origin
git rebase origin/main

# O merge (menos limpio)
git merge origin/main

# Resolver conflictos manualmente
# Editar archivos con conflictos
# Buscar marcadores: <<<<<<<, =======, >>>>>>>

# Después de resolver
git add .
git rebase --continue  # Si usaste rebase
# O
git commit -m "Merge main"  # Si usaste merge

# Push
git push origin feature/mi-rama --force-with-lease

Pull Request Feedback

Problema: Reviewer solicita cambios

Solución:

# Hacer cambios solicitados
# Editar archivos

# Commit con cambios
git add .
git commit -m "Cambios solicitados en review"

# Push (actualiza automáticamente el PR)
git push origin feature/mi-rama

# Responder en GitHub indicando que hiciste los cambios

Check Pipeline Status

Problema: Quiero ver el estado del CI/Release pipeline

Solución:

# En GitHub:
# 1. Ve a tu PR
# 2. Scroll down a "Checks"
# 3. Haz click en "Details" para ver logs

# O desde línea de comandos:
# Ver commits recientes
git log --oneline

# Ver tags (releases)
git tag -l

# Ver releases en GitHub
# https://github.com/dev-brainstack/config-loader/releases

Local Testing Before Push

Problema: Quiero verificar que todo pasa antes de push

Solución:

# Ejecutar todo localmente
make test      # Tests
make format    # Formateo
make lint      # Linting

# O todo junto
pytest && black --check src/ && flake8 src/ && mypy src/

# Si todo pasa, push con confianza
git push origin feature/mi-rama

📝 Licencia

MIT License - Ver LICENSE para más detalles.


🤝 Contribuciones

Las contribuciones son bienvenidas. Por favor:

  1. Fork el repositorio
  2. Crea una rama para tu cambio (git checkout -b feature/mi-cambio)
  3. Commit tus cambios (git commit -am 'Agrega mi cambio')
  4. Push a la rama (git push origin feature/mi-cambio)
  5. Abre un Pull Request

📞 Soporte

Para reportar bugs o sugerir mejoras, abre un issue en: https://github.com/dev-brainstack/config-loader/issues


Hecho con ❤️ por el equipo de desarrollo

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

brainstack_config_loader-0.1.1.tar.gz (38.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

brainstack_config_loader-0.1.1-py3-none-any.whl (17.0 kB view details)

Uploaded Python 3

File details

Details for the file brainstack_config_loader-0.1.1.tar.gz.

File metadata

  • Download URL: brainstack_config_loader-0.1.1.tar.gz
  • Upload date:
  • Size: 38.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for brainstack_config_loader-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b3a4ccc5e0cc07ac18ebe4250ad44d47ecbeafe2fadf9227f41a602c6095f4f9
MD5 9c2081b96d885d4c54308107a573d715
BLAKE2b-256 9faea968f870e2ac920540b6ca244a1462a77441ebc7e433e40018024ab7674a

See more details on using hashes here.

File details

Details for the file brainstack_config_loader-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for brainstack_config_loader-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6c0e69ea1d68b4e9016aeace8aa2e58847fc2dfcfab3df7e2f02dd8d9d0b943b
MD5 6071a32207c2c1b855b1590342162717
BLAKE2b-256 4a85edef2325b71a6ff3133d55317ce54af98b320a8552db17a96d8e77363ead

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page