# Fichier: python_cheats/cheatsheets/yaml.txt
# Cheatsheet YAML - Guide Complet de Débutant à Expert


[OK] INTRODUCTION À YAML

# YAML = "YAML Ain't Markup Language"
# Format de sérialisation de données lisible par l'homme
# Extension: .yaml ou .yml (plus courant)
# Sensible à l'indentation (comme Python)
# Utilisé pour: configs, CI/CD, Docker, Kubernetes, Ansible, etc.

# Avantages:
# [OK] Plus lisible que JSON/XML
# [OK] Supporte commentaires
# [OK] Types de données riches
# [OK] Références et ancres
# [X] Sensible aux espaces
# [X] Parsing plus complexe que JSON


[OK] SYNTAXE DE BASE


# === COMMENTAIRES ===

# Ceci est un commentaire
# YAML supporte uniquement les commentaires sur une seule ligne
# Il n'y a pas de commentaires multi-lignes

key: value  # Commentaire en fin de ligne


# === SCALAIRES (valeurs simples) ===

# Chaînes de caractères
name: John Doe
title: Software Engineer
city: "Paris"           # Guillemets optionnels
state: 'France'         # Simple ou double guillemets

# Chaînes avec caractères spéciaux
special: "Contient: des caractères spéciaux"
quote: 'L''apostrophe nécessite doublement'
quote2: "Les \"guillemets\" nécessitent échappement"

# Chaînes multi-lignes (littéral - préserve retours à ligne)
description: |
  Ceci est une chaîne
  sur plusieurs lignes.
  Les retours à ligne sont préservés.
  
  Les lignes vides aussi.

# Chaînes multi-lignes (plié - remplace retours par espaces)
summary: >
  Ceci est une longue chaîne
  qui sera pliée en une seule ligne.
  Les retours à ligne deviennent des espaces.

# Chaînes multi-lignes avec contrôle du dernier retour
literal_strip: |-       # Supprime dernier retour
  Texte sans retour final
literal_keep: |+        # Garde tous les retours finaux
  Texte avec retours finaux


folded_strip: >-        # Supprime dernier retour
  Texte plié
folded_keep: >+         # Garde retours finaux
  Texte plié

# Nombres
age: 30                 # Entier
price: 99.99            # Float
scientific: 1.5e+3      # Notation scientifique (1500)
octal: 0o755            # Octal
hexadecimal: 0xFF       # Hexadécimal
binary: 0b1010          # Binaire (10)

# Booléens (multiples syntaxes)
active: true
enabled: false
is_admin: yes           # Équivalent à true
is_guest: no            # Équivalent à false
debug: True             # Aussi valide
production: FALSE       # Aussi valide
on_state: on            # true
off_state: off          # false

# Null/vide
empty: null
also_empty: ~           # Équivalent à null
no_value:               # Aussi null (rien après :)

# Dates et timestamps
date: 2025-11-16                    # Date
datetime: 2025-11-16T14:30:00       # ISO 8601
timestamp: 2025-11-16 14:30:00.000  # Avec millisecondes
canonical: 2025-11-16T14:30:00.00Z  # UTC


[OK] COLLECTIONS


# === LISTES / SÉQUENCES ===

# Style bloc (avec tirets)
fruits:
  - apple
  - banana
  - orange

# Liste inline (style flux)
colors: [red, green, blue]

# Liste d'objets
users:
  - name: Alice
    age: 30
    role: admin
  - name: Bob
    age: 25
    role: user
  - name: Charlie
    age: 35
    role: moderator

# Liste de listes
matrix:
  - [1, 2, 3]
  - [4, 5, 6]
  - [7, 8, 9]

# Liste mixte
mixed:
  - string
  - 42
  - true
  - null
  - [nested, list]
  - key: value

# Liste vide
empty_list: []
also_empty_list:


# === DICTIONNAIRES / MAPPINGS ===

# Style bloc
person:
  name: John
  age: 30
  email: john@example.com

# Style inline (style flux)
point: {x: 10, y: 20, z: 30}

# Dictionnaire imbriqué
employee:
  personal:
    name: Alice Smith
    age: 30
    address:
      street: 123 Main St
      city: Paris
      country: France
  professional:
    title: Senior Developer
    department: Engineering
    salary: 75000

# Dictionnaire de listes
departments:
  engineering:
    - Alice
    - Bob
  marketing:
    - Charlie
    - Diana
  sales:
    - Eve
    - Frank

# Dictionnaire vide
empty_dict: {}
also_empty_dict:


# === STRUCTURES COMPLEXES ===

# Configuration complète
application:
  name: MyApp
  version: 1.2.3
  
  database:
    host: localhost
    port: 5432
    name: mydb
    credentials:
      username: admin
      password: secret123
    
  servers:
    - name: web-01
      ip: 192.168.1.10
      roles: [web, api]
    - name: db-01
      ip: 192.168.1.20
      roles: [database]
  
  features:
    authentication: true
    caching: true
    logging:
      level: info
      formats: [json, text]


[OK] TYPES DE DONNÉES AVANCÉS


# === TAGS DE TYPE ===

# Forcer un type
explicit_string: !!str 123          # "123" (string)
explicit_int: !!int "123"           # 123 (integer)
explicit_float: !!float 42          # 42.0 (float)
explicit_bool: !!bool "yes"         # true (boolean)

# Types de collections
explicit_list: !!seq
  - item1
  - item2

explicit_dict: !!map
  key1: value1
  key2: value2

# Type set (ensemble - pas de doublons)
unique_items: !!set
  ? item1
  ? item2
  ? item3

# Paires ordonnées
ordered_pairs: !!omap
  - key1: value1
  - key2: value2
  - key3: value3

# Binaire (base64)
image: !!binary |
  R0lGODlhDAAMAIQAAP//9/X
  17unp5WZmZgAAAOfn515eXv
  Pz7Y6OjuDg4J+fn5OTk6enp


# === ANCRES ET ALIAS (Références) ===

# Définir une ancre avec &
defaults: &default_settings
  timeout: 30
  retries: 3
  debug: false

# Réutiliser avec *
production:
  <<: *default_settings      # Merge (fusion)
  host: prod.example.com
  debug: false               # Override

development:
  <<: *default_settings
  host: dev.example.com
  debug: true                # Override

# Ancre pour une valeur simple
version: &app_version 1.2.3

api:
  version: *app_version

web:
  version: *app_version

# Ancres multiples
base: &base
  name: Base Config
  type: default

extended: &extended
  <<: *base
  extra: value

final:
  <<: [*base, *extended]     # Merge multiple ancres

# Ancre dans liste
common_packages: &packages
  - python
  - pip
  - git

server1:
  packages:
    - *packages              # Référence la liste
    - nginx

server2:
  packages:
    - *packages
    - apache


[OK] CLÉS SPÉCIALES


# === CLÉS COMPLEXES ===

# Clé avec espaces (entre guillemets)
"first name": John
'last name': Doe

# Clé null
~: null value
null: also null value

# Clés booléennes
true: yes key
false: no key

# Clés numériques
123: numeric key
0xFF: hex key

# Clé multi-ligne
? |
  This is a very long key
  that spans multiple lines
: and this is its value

# Clé complexe (objet)
? - key
  - parts
: value for complex key


[OK] DOCUMENTS MULTIPLES


# Séparer plusieurs documents dans un fichier avec ---
---
document: 1
name: First Document
---
document: 2
name: Second Document
---
document: 3
name: Third Document

# Fin explicite de document avec ...
---
start: document
content: data
...

# Exemple pratique: Kubernetes
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  database_url: postgresql://localhost/mydb
---
apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  selector:
    app: myapp
  ports:
    - port: 80


[OK] ÉCHAPPEMENT ET CARACTÈRES SPÉCIAUX


# === GUILLEMETS ===

# Sans guillemets (simple)
simple: Hello World

# Guillemets doubles (échappement avec \)
escaped: "Line 1\nLine 2\tTabbed"
unicode: "Unicode: \u0041"          # A
path: "C:\\Users\\Documents"

# Guillemets simples (littéral, '' pour ')
literal: 'No escape: \n just text'
apostrophe: 'It''s working'

# Caractères spéciaux YAML à échapper
special_chars: "Contains: colon, {brace}, [bracket], #hash, &anchor, *alias, !tag, -, |, >"


# === PRÉSERVER MISE EN FORME ===

# Bloc littéral |
code: |
  def hello():
      print("Hello")
      return True

# Bloc plié >
paragraph: >
  This is a long paragraph
  that will be folded into
  a single line with spaces.

# Avec indicateurs de chomping
strip_newlines: |-
  No trailing newlines
  
keep_newlines: |+
  Keep all trailing

  

clip_newlines: |
  Default behavior
  

# Indicateurs d'indentation
explicit_indent: |2
    Indented by 2 spaces
    from the indicator


[OK] YAML EN PYTHON


# === LECTURE YAML ===

import yaml

# Lire depuis fichier
with open('config.yaml', 'r') as file:
    data = yaml.safe_load(file)

# Lire depuis string
yaml_string = """
name: John
age: 30
hobbies:
  - reading
  - coding
"""
data = yaml.safe_load(yaml_string)

# Lire tous les documents
with open('multi.yaml', 'r') as file:
    documents = yaml.safe_load_all(file)
    for doc in documents:
        print(doc)

# Load vs safe_load
data = yaml.load(file, Loader=yaml.FullLoader)     # Plus de types
data = yaml.safe_load(file)                        # Sécurisé (recommandé)
data = yaml.unsafe_load(file)                      # Dangereux (éviter)


# === ÉCRITURE YAML ===

import yaml

data = {
    'name': 'John',
    'age': 30,
    'hobbies': ['reading', 'coding'],
    'address': {
        'city': 'Paris',
        'country': 'France'
    }
}

# Écrire dans fichier
with open('output.yaml', 'w') as file:
    yaml.dump(data, file)

# Avec options de formatage
with open('output.yaml', 'w') as file:
    yaml.dump(
        data,
        file,
        default_flow_style=False,    # Style bloc (pas inline)
        sort_keys=False,              # Garde l'ordre
        allow_unicode=True,           # Supporte Unicode
        indent=2,                     # Indentation
        width=80                      # Largeur max ligne
    )

# Convertir en string
yaml_string = yaml.dump(data)
print(yaml_string)

# Dump multiple documents
with open('multi.yaml', 'w') as file:
    yaml.dump_all([doc1, doc2, doc3], file)


# === TYPES PYTHON -> YAML ===

import yaml
from datetime import datetime, date

data = {
    # Basiques
    'string': 'Hello',
    'integer': 42,
    'float': 3.14,
    'boolean': True,
    'none': None,
    
    # Collections
    'list': [1, 2, 3],
    'tuple': (1, 2, 3),        # -> liste en YAML
    'set': {1, 2, 3},          # -> liste en YAML
    'dict': {'key': 'value'},
    
    # Dates
    'date': date(2025, 11, 16),
    'datetime': datetime(2025, 11, 16, 14, 30),
    
    # Bytes
    'bytes': b'binary data'
}

yaml_output = yaml.dump(data)


# === CLASSES PERSONNALISÉES ===

import yaml

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# Représenter classe personnalisée
def person_representer(dumper, person):
    return dumper.represent_mapping('!Person', {
        'name': person.name,
        'age': person.age
    })

yaml.add_representer(Person, person_representer)

# Construire classe depuis YAML
def person_constructor(loader, node):
    values = loader.construct_mapping(node)
    return Person(**values)

yaml.add_constructor('!Person', person_constructor)

# Utilisation
person = Person('Alice', 30)
yaml_str = yaml.dump(person)
loaded = yaml.load(yaml_str, Loader=yaml.FullLoader)


# === PYYAML AVANCÉ ===

import yaml

# Custom Loader
class CustomLoader(yaml.SafeLoader):
    pass

# Custom Dumper
class CustomDumper(yaml.SafeDumper):
    pass

# Utiliser custom loader/dumper
data = yaml.load(file, Loader=CustomLoader)
yaml.dump(data, file, Dumper=CustomDumper)

# Représentation personnalisée des listes
def represent_list(dumper, data):
    if len(data) < 5:
        return dumper.represent_sequence('tag:yaml.org,2002:seq', data, flow_style=True)
    return dumper.represent_sequence('tag:yaml.org,2002:seq', data)

yaml.add_representer(list, represent_list)


[OK] BIBLIOTHÈQUES YAML PYTHON


# === PyYAML (Standard) ===

# Installation
pip install PyYAML

import yaml

# Safe operations (recommandé)
data = yaml.safe_load(file)
yaml.safe_dump(data, file)


# === ruamel.yaml (Recommandé pour préservation) ===

# Installation
pip install ruamel.yaml

from ruamel.yaml import YAML

yaml = YAML()
yaml.preserve_quotes = True
yaml.default_flow_style = False

# Lire
with open('config.yaml') as file:
    data = yaml.load(file)

# Écrire (préserve commentaires et style)
with open('config.yaml', 'w') as file:
    yaml.dump(data, file)

# Configuration
yaml.indent(mapping=2, sequence=2, offset=0)
yaml.width = 80
yaml.allow_unicode = True


# === strictyaml (Type-safe) ===

# Installation
pip install strictyaml

from strictyaml import load, Map, Str, Int, Seq

# Schéma
schema = Map({
    "name": Str(),
    "age": Int(),
    "hobbies": Seq(Str())
})

# Parse avec validation
yaml_string = """
name: John
age: 30
hobbies:
  - reading
  - coding
"""

data = load(yaml_string, schema)
print(data['name'])  # Type-safe access


# === yamllint (Validation) ===

# Installation
pip install yamllint

# CLI
yamllint config.yaml
yamllint -f parsable config.yaml
yamllint -d relaxed config.yaml

# En Python
from yamllint.config import YamlLintConfig
from yamllint.linter import run

config = YamlLintConfig('extends: default')
gen = run('config.yaml', config)
problems = list(gen)


[OK] VALIDATION ET SCHÉMAS


# === JSON Schema pour YAML ===

import yaml
import jsonschema

# Schéma
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer", "minimum": 0},
        "email": {"type": "string", "format": "email"}
    },
    "required": ["name", "age"]
}

# Valider
with open('data.yaml') as file:
    data = yaml.safe_load(file)
    jsonschema.validate(instance=data, schema=schema)


# === Pydantic avec YAML ===

from pydantic import BaseModel, EmailStr
import yaml

class Person(BaseModel):
    name: str
    age: int
    email: EmailStr

# Charger et valider
with open('person.yaml') as file:
    data = yaml.safe_load(file)
    person = Person(**data)


[OK] CONFIGURATIONS YAML COURANTES


# === Configuration Application ===

# config.yaml
application:
  name: MyApp
  version: 1.0.0
  environment: production
  
  server:
    host: 0.0.0.0
    port: 8000
    workers: 4
    timeout: 30
    
  database:
    engine: postgresql
    host: localhost
    port: 5432
    name: mydb
    pool_size: 10
    max_overflow: 20
    
  redis:
    host: localhost
    port: 6379
    db: 0
    
  logging:
    level: INFO
    format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    handlers:
      - type: console
      - type: file
        filename: app.log
        max_bytes: 10485760
        backup_count: 5
  
  features:
    authentication: true
    rate_limiting: true
    caching: true
    
  api:
    rate_limit: 1000
    timeout: 30
    allowed_origins:
      - https://example.com
      - https://www.example.com


# === Docker Compose ===

# docker-compose.yaml
version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./html:/usr/share/nginx/html
      - ./nginx.conf:/etc/nginx/nginx.conf
    environment:
      - NGINX_HOST=example.com
      - NGINX_PORT=80
    depends_on:
      - api
    networks:
      - frontend
    restart: unless-stopped
    
  api:
    build:
      context: ./api
      dockerfile: Dockerfile
      args:
        - PYTHON_VERSION=3.11
    ports:
      - "8000:8000"
    volumes:
      - ./api:/app
    environment:
      DATABASE_URL: postgresql://user:pass@db:5432/mydb
      REDIS_URL: redis://redis:6379/0
    depends_on:
      - db
      - redis
    networks:
      - frontend
      - backend
    restart: unless-stopped
    
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    networks:
      - backend
    restart: unless-stopped
    
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    networks:
      - backend
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

networks:
  frontend:
  backend:


# === Kubernetes ===

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
  labels:
    app: myapp
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
        version: v1
    spec:
      containers:
        - name: myapp
          image: myapp:1.0.0
          ports:
            - containerPort: 8000
              protocol: TCP
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: url
          resources:
            requests:
              memory: "256Mi"
              cpu: "500m"
            limits:
              memory: "512Mi"
              cpu: "1000m"
          livenessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 8000
            initialDelaySeconds: 5
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000
  type: LoadBalancer


# === GitHub Actions ===

# .github/workflows/ci.yaml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  PYTHON_VERSION: '3.11'
  NODE_VERSION: '18'

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11']
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
      
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -r requirements-dev.txt
      
      - name: Run tests
        run: |
          pytest --cov=src --cov-report=xml
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage.xml
  
  build:
    needs: test
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Build Docker image
        run: |
          docker build -t myapp:${{ github.sha }} .
      
      - name: Push to registry
        run: |
          echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
          docker push myapp:${{ github.sha }}


# === Ansible ===

# playbook.yaml
---
- name: Deploy Application
  hosts: webservers
  become: yes
  
  vars:
    app_name: myapp
    app_version: 1.0.0
    deploy_path: /opt/{{ app_name }}
    
  vars_files:
    - vars/{{ env }}.yaml
    
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600
      
    - name: Install dependencies
      apt:
        name:
          - python3
          - python3-pip
          - nginx
          - postgresql-client
        state: present
    
    - name: Create app directory
      file:
        path: "{{ deploy_path }}"
        state: directory
        owner: www-data
        group: www-data
        mode: '0755'
    
    - name: Copy application files
      copy:
        src: "{{ item }}"
        dest: "{{ deploy_path }}/"
        owner: www-data
        group: www-data
      loop:
        - app.py
        - requirements.txt
        - config.yaml
    
    - name: Install Python dependencies
      pip:
        requirements: "{{ deploy_path }}/requirements.txt"
        virtualenv: "{{ deploy_path }}/venv"
      
    - name: Configure nginx
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/{{ app_name }}
      notify: restart nginx
    
    - name: Enable site
      file:
        src: /etc/nginx/sites-available/{{ app_name }}
        dest: /etc/nginx/sites-enabled/{{ app_name }}
        state: link
      notify: restart nginx
  
  handlers:
    - name: restart nginx
      service:
        name: nginx
        state: restarted


[OK] BONNES PRATIQUES


# 1. INDENTATION
# [OK] Utiliser 2 espaces (convention)
# [X] Ne JAMAIS utiliser de tabs
# [OK] Être cohérent dans tout le fichier

person:
  name: John      # 2 espaces
  address:
    city: Paris   # 4 espaces (2 niveaux)


# 2. GUILLEMETS
# [OK] Pas de guillemets si pas nécessaire
# [OK] Guillemets doubles pour échappement
# [OK] Guillemets simples pour littéral

simple: Hello World
special: "Contains: colon"
literal: 'No \n escape'


# 3. COMMENTAIRES
# [OK] Commenter sections complexes
# [OK] Expliquer pourquoi, pas quoi
# [X] Éviter commentaires évidents

# Database configuration for production
# Uses connection pooling for better performance
database:
  host: localhost
  pool_size: 10


# 4. ORGANISATION
# [OK] Grouper éléments liés
# [OK] Ordre logique (général -> spécifique)
# [OK] Séparer sections avec lignes vides

# Application metadata
name: MyApp
version: 1.0.0

# Server configuration
server:
  host: 0.0.0.0
  port: 8000

# Database configuration  
database:
  host: localhost
  port: 5432


# 5. SÉCURITÉ
# [X] Ne JAMAIS commiter secrets
# [OK] Utiliser variables d'environnement
# [OK] Utiliser secrets management (Vault, etc.)

# [X] MAUVAIS
database:
  password: supersecret123

# [OK] BON
database:
  password: ${DB_PASSWORD}
  # ou
  password: !env DB_PASSWORD


# 6. VALIDATION
# [OK] Valider YAML avant deploy
# [OK] Utiliser schemas
# [OK] Tests automatisés

# yamllint
yamllint config.yaml

# Python validation
import yaml
with open('config.yaml') as f:
    yaml.safe_load(f)


# 7. ANCRES ET RÉUTILISATION
# [OK] DRY (Don't Repeat Yourself)
# [OK] Ancres pour configuration commune
# [X] Ne pas abuser (lisibilité)

# [OK] BON
defaults: &defaults
  timeout: 30
  retries: 3

prod:
  <<: *defaults
  host: prod.example.com


# 8. NOMMAGE
# [OK] snake_case pour clés
# [OK] Noms descriptifs
# [X] Éviter abréviations obscures

# [OK] BON
database_connection_timeout: 30
max_retry_attempts: 3

# [X] MAUVAIS
db_conn_to: 30
max_ret: 3


# 9. TYPES
# [OK] Laisser YAML inférer types simples
# [OK] Utiliser tags pour forcer type si nécessaire
# [OK] Être cohérent

age: 30                # Infère int
version: "1.2.3"       # Explicite string
enabled: true          # Infère bool


# 10. DOCUMENTATION
# [OK] README avec structure YAML
# [OK] Exemples de configuration
# [OK] Schéma si complexe

# Exemple minimal dans README:
# ```yaml
# application:
#   name: string        # Nom de l'application
#   port: integer       # Port (1024-65535)
#   debug: boolean      # Mode debug
# ```


[OK] ERREURS COURANTES


# === ERREUR 1: Indentation ===

# [X] MAUVAIS (tabs mélangés)
person:
	name: John      # Tab
  age: 30         # Espaces

# [OK] BON
person:
  name: John
  age: 30


# === ERREUR 2: Guillemets manquants ===

# [X] MAUVAIS
url: http://example.com:8080/path   # : cause problème

# [OK] BON
url: "http://example.com:8080/path"


# === ERREUR 3: Ancre non définie ===

# [X] MAUVAIS
config:
  <<: *undefined_anchor

# [OK] BON
defaults: &defaults
  timeout: 30

config:
  <<: *defaults


# === ERREUR 4: Type incorrect ===

# [X] MAUVAIS
port: "8000"      # String au lieu de int

# [OK] BON
port: 8000        # Integer


# === ERREUR 5: Liste mal formée ===

# [X] MAUVAIS
items:
- item1
  - nested        # Erreur d'indentation

# [OK] BON
items:
  - item1
  - item2
    - nested


# === ERREUR 6: Caractères spéciaux ===

# [X] MAUVAIS
description: This has a: colon      # Erreur de parsing

# [OK] BON
description: "This has a: colon"


# === ERREUR 7: Valeurs booléennes ===

# [X] ATTENTION (interprété comme bool)
norwegian: NO       # false en YAML!
swedish: YES        # true en YAML!

# [OK] BON
norwegian: "NO"
swedish: "YES"


# === ERREUR 8: Nombres commençant par 0 ===

# [X] ATTENTION
zip_code: 01234     # Octal = 668 en décimal!

# [OK] BON
zip_code: "01234"   # String


# === ERREUR 9: Duplication de clés ===

# [X] MAUVAIS (dernière valeur gagne)
person:
  name: John
  age: 30
  name: Jane        # Écrase "John"

# [OK] BON
person:
  first_name: John
  last_name: Doe
  age: 30


# === ERREUR 10: Multi-ligne mal formé ===

# [X] MAUVAIS
text: >
This is wrong       # Pas d'indentation

# [OK] BON
text: >
  This is correct


[OK] YAML vs JSON vs XML


# === MÊME DONNÉE EN 3 FORMATS ===

# YAML (plus concis et lisible)
person:
  name: John Doe
  age: 30
  active: true
  hobbies:
    - reading
    - coding
  address:
    city: Paris
    country: France

# JSON (plus verbeux)
{
  "person": {
    "name": "John Doe",
    "age": 30,
    "active": true,
    "hobbies": ["reading", "coding"],
    "address": {
      "city": "Paris",
      "country": "France"
    }
  }
}

# XML (le plus verbeux)
<person>
  <name>John Doe</name>
  <age>30</age>
  <active>true</active>
  <hobbies>
    <hobby>reading</hobby>
    <hobby>coding</hobby>
  </hobbies>
  <address>
    <city>Paris</city>
    <country>France</country>
  </address>
</person>


# === CONVERSION ===

# YAML -> JSON (Python)
import yaml
import json

with open('data.yaml') as f:
    data = yaml.safe_load(f)
    json_str = json.dumps(data, indent=2)

# JSON -> YAML (Python)
import json
import yaml

with open('data.json') as f:
    data = json.load(f)
    yaml_str = yaml.dump(data, default_flow_style=False)

# CLI (avec yq)
yq eval -o=json input.yaml > output.json
yq eval -P input.json > output.yaml


[OK] OUTILS ET UTILITAIRES


# === VALIDATION EN LIGNE DE COMMANDE ===

# yamllint (installation)
pip install yamllint

# Valider fichier
yamllint config.yaml

# Avec configuration personnalisée
yamllint -d "{extends: default, rules: {line-length: {max: 120}}}" config.yaml

# Configuration fichier (.yamllint)
---
extends: default
rules:
  line-length:
    max: 120
  indentation:
    spaces: 2
  comments:
    min-spaces-from-content: 1


# === YQ (Processeur YAML en CLI) ===

# Installation
# Linux: snap install yq
# Mac: brew install yq
# Ou: pip install yq

# Lire valeur
yq eval '.database.host' config.yaml

# Modifier valeur
yq eval '.database.port = 3306' -i config.yaml

# Filtrer liste
yq eval '.servers[] | select(.role == "web")' config.yaml

# Merge fichiers
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' base.yaml override.yaml

# Convertir JSON -> YAML
cat data.json | yq eval -P

# Convertir YAML -> JSON
yq eval -o=json config.yaml


# === YAML EN ÉDITEURS ===

# VSCode extensions
# - YAML (Red Hat)
# - YAML Language Support
# - YAML Sort

# Configuration VSCode (settings.json)
{
  "yaml.schemas": {
    "https://json.schemastore.org/github-workflow": ".github/workflows/*.yaml",
    "https://json.schemastore.org/docker-compose": "docker-compose.yaml"
  },
  "yaml.format.enable": true,
  "yaml.validate": true
}


# === PRE-COMMIT HOOKS ===

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/adrienverge/yamllint
    rev: v1.32.0
    hooks:
      - id: yamllint
        args: [--strict]

  - repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt
    rev: 0.2.3
    hooks:
      - id: yamlfmt


[OK] SCHÉMAS ET VALIDATION AVANCÉE


# === JSON SCHEMA POUR YAML ===

import yaml
import jsonschema
from jsonschema import validate

# Définir schéma
schema = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
        },
        "age": {
            "type": "integer",
            "minimum": 0,
            "maximum": 150
        },
        "email": {
            "type": "string",
            "format": "email"
        },
        "roles": {
            "type": "array",
            "items": {
                "type": "string",
                "enum": ["admin", "user", "guest"]
            },
            "minItems": 1,
            "uniqueItems": True
        },
        "settings": {
            "type": "object",
            "properties": {
                "notifications": {"type": "boolean"},
                "theme": {
                    "type": "string",
                    "enum": ["light", "dark"]
                }
            },
            "required": ["notifications"]
        }
    },
    "required": ["name", "age"],
    "additionalProperties": False
}

# Valider
with open('user.yaml') as f:
    data = yaml.safe_load(f)
    try:
        validate(instance=data, schema=schema)
        print("[OK] Valid")
    except jsonschema.exceptions.ValidationError as e:
        print(f"[X] Invalid: {e.message}")


# === PYDANTIC MODELS ===

from pydantic import BaseModel, Field, EmailStr, validator
from typing import List, Optional, Literal
import yaml

class Settings(BaseModel):
    notifications: bool
    theme: Literal['light', 'dark'] = 'light'

class User(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    age: int = Field(..., ge=0, le=150)
    email: EmailStr
    roles: List[Literal['admin', 'user', 'guest']] = Field(..., min_items=1)
    settings: Settings
    
    @validator('roles')
    def roles_must_be_unique(cls, v):
        if len(v) != len(set(v)):
            raise ValueError('roles must be unique')
        return v

# Charger et valider
with open('user.yaml') as f:
    data = yaml.safe_load(f)
    user = User(**data)
    print(user.json(indent=2))


# === DATACLASSES AVEC YAML ===

from dataclasses import dataclass, field
from typing import List
import yaml

@dataclass
class DatabaseConfig:
    host: str
    port: int = 5432
    name: str = "mydb"
    pool_size: int = 10

@dataclass
class AppConfig:
    name: str
    version: str
    database: DatabaseConfig
    features: List[str] = field(default_factory=list)

# Charger
with open('config.yaml') as f:
    data = yaml.safe_load(f)
    config = AppConfig(
        name=data['name'],
        version=data['version'],
        database=DatabaseConfig(**data['database']),
        features=data.get('features', [])
    )


[OK] YAML AVEC JINJA2 TEMPLATES


# === TEMPLATE YAML ===

# config.yaml.j2
application:
  name: {{ app_name }}
  environment: {{ env }}
  
  server:
    host: {{ server_host | default('0.0.0.0') }}
    port: {{ server_port | default(8000) }}
    workers: {{ workers | default(4) }}
  
  database:
    host: {{ db_host }}
    port: {{ db_port | default(5432) }}
    name: {{ db_name }}
    {% if db_ssl %}
    ssl: true
    ssl_cert: {{ db_ssl_cert }}
    {% endif %}
  
  features:
    {% for feature, enabled in features.items() %}
    {{ feature }}: {{ enabled | lower }}
    {% endfor %}
  
  {% if env == 'production' %}
  logging:
    level: WARNING
    sentry_dsn: {{ sentry_dsn }}
  {% else %}
  logging:
    level: DEBUG
  {% endif %}


# === GÉNÉRATION EN PYTHON ===

from jinja2 import Environment, FileSystemLoader
import yaml

# Configuration Jinja2
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('config.yaml.j2')

# Variables
context = {
    'app_name': 'MyApp',
    'env': 'production',
    'server_host': '0.0.0.0',
    'server_port': 8000,
    'workers': 8,
    'db_host': 'localhost',
    'db_port': 5432,
    'db_name': 'mydb',
    'db_ssl': True,
    'db_ssl_cert': '/path/to/cert',
    'features': {
        'authentication': True,
        'caching': True,
        'rate_limiting': False
    },
    'sentry_dsn': 'https://...'
}

# Générer YAML
yaml_content = template.render(context)

# Valider et sauvegarder
data = yaml.safe_load(yaml_content)
with open('config.yaml', 'w') as f:
    yaml.dump(data, f, default_flow_style=False)


[OK] YAML AVEC VARIABLES D'ENVIRONNEMENT


# === MÉTHODE 1: Substitution manuelle ===

import yaml
import os

# config.yaml
database:
  host: ${DB_HOST}
  port: ${DB_PORT}
  password: ${DB_PASSWORD}

# Charger et substituer
with open('config.yaml') as f:
    content = f.read()
    # Substituer variables
    for key, value in os.environ.items():
        content = content.replace(f'${{{key}}}', value)
    config = yaml.safe_load(content)


# === MÉTHODE 2: Classe personnalisée ===

import yaml
import os
import re

class EnvVarLoader(yaml.SafeLoader):
    pass

def env_var_constructor(loader, node):
    value = loader.construct_scalar(node)
    # Chercher ${VAR} ou ${VAR:default}
    pattern = r'\$\{([^}:]+)(?::([^}]+))?\}'
    
    def replacer(match):
        var_name = match.group(1)
        default_value = match.group(2)
        return os.environ.get(var_name, default_value or '')
    
    return re.sub(pattern, replacer, value)

# Enregistrer constructor
EnvVarLoader.add_constructor('!env', env_var_constructor)
EnvVarLoader.add_implicit_resolver('!env', re.compile(r'\$\{[^}]+\}'), None)

# Utiliser
with open('config.yaml') as f:
    config = yaml.load(f, Loader=EnvVarLoader)


# === MÉTHODE 3: Tag !env ===

# config.yaml
database:
  host: !env DB_HOST
  port: !env [DB_PORT, 5432]        # Avec défaut
  password: !env DB_PASSWORD

# Loader personnalisé
import yaml
import os

def env_constructor(loader, node):
    if isinstance(node, yaml.ScalarNode):
        # !env VAR
        var_name = loader.construct_scalar(node)
        value = os.environ.get(var_name)
        if value is None:
            raise ValueError(f"Environment variable {var_name} not set")
        # Tenter conversion type
        try:
            return int(value)
        except ValueError:
            try:
                return float(value)
            except ValueError:
                return value
    elif isinstance(node, yaml.SequenceNode):
        # !env [VAR, default]
        values = loader.construct_sequence(node)
        var_name = values[0]
        default = values[1] if len(values) > 1 else None
        value = os.environ.get(var_name, default)
        if value is None:
            raise ValueError(f"Environment variable {var_name} not set")
        return value

yaml.add_constructor('!env', env_constructor, Loader=yaml.SafeLoader)


[OK] PERFORMANCE ET OPTIMISATION


# === BENCHMARK: PyYAML vs ruamel.yaml ===

import time
import yaml
from ruamel.yaml import YAML

# Grande structure
large_data = {
    'items': [
        {'id': i, 'name': f'Item {i}', 'value': i * 10}
        for i in range(10000)
    ]
}

# PyYAML (plus rapide)
start = time.time()
yaml_str = yaml.dump(large_data)
data = yaml.safe_load(yaml_str)
print(f"PyYAML: {time.time() - start:.3f}s")

# ruamel.yaml (plus lent mais préserve format)
start = time.time()
yaml_handler = YAML()
from io import StringIO
stream = StringIO()
yaml_handler.dump(large_data, stream)
stream.seek(0)
data = yaml_handler.load(stream)
print(f"ruamel.yaml: {time.time() - start:.3f}s")


# === OPTIMISATION: Loader C ===

import yaml

# Utiliser CLoader pour parsing plus rapide (si disponible)
try:
    from yaml import CLoader as Loader, CDumper as Dumper
except ImportError:
    from yaml import Loader, Dumper

# Plus rapide
data = yaml.load(file, Loader=Loader)
yaml.dump(data, file, Dumper=Dumper)


# === STREAMING POUR GROS FICHIERS ===

import yaml

# Lire document par document
with open('large.yaml') as f:
    for doc in yaml.safe_load_all(f):
        process(doc)  # Traiter un doc à la fois

# Écrire progressivement
def data_generator():
    for i in range(1000):
        yield {'id': i, 'data': f'Data {i}'}

with open('output.yaml', 'w') as f:
    yaml.dump_all(data_generator(), f)


# === CACHE POUR FICHIERS FRÉQUENTS ===

from functools import lru_cache
import yaml

@lru_cache(maxsize=32)
def load_config(config_path):
    with open(config_path) as f:
        return yaml.safe_load(f)

# Première fois: charge depuis fichier
config = load_config('config.yaml')

# Fois suivantes: depuis cache
config = load_config('config.yaml')  # Instantané!


[OK] CAS D'USAGE AVANCÉS


# === CONFIGURATION PAR ENVIRONNEMENT ===

# config/base.yaml
application:
  name: MyApp
  version: 1.0.0

database: &database
  host: localhost
  port: 5432
  pool_size: 10

# config/development.yaml
<<: *base

database:
  <<: *database
  name: myapp_dev
  debug: true

logging:
  level: DEBUG

# config/production.yaml
<<: *base

database:
  <<: *database
  name: myapp_prod
  ssl: true

logging:
  level: WARNING
  sentry_enabled: true


# === CHARGEMENT MULTI-FICHIERS ===

import yaml
from pathlib import Path

def load_config(env='development'):
    config_dir = Path('config')
    
    # Charger base
    with open(config_dir / 'base.yaml') as f:
        config = yaml.safe_load(f)
    
    # Charger spécifique environnement
    env_file = config_dir / f'{env}.yaml'
    if env_file.exists():
        with open(env_file) as f:
            env_config = yaml.safe_load(f)
            # Merge profond
            config = deep_merge(config, env_config)
    
    # Charger local (non versionné)
    local_file = config_dir / 'local.yaml'
    if local_file.exists():
        with open(local_file) as f:
            local_config = yaml.safe_load(f)
            config = deep_merge(config, local_config)
    
    return config

def deep_merge(base, update):
    """Merge récursif de dictionnaires"""
    for key, value in update.items():
        if key in base and isinstance(base[key], dict) and isinstance(value, dict):
            base[key] = deep_merge(base[key], value)
        else:
            base[key] = value
    return base


# === CONFIGURATION DYNAMIQUE ===

import yaml
from datetime import datetime

class DynamicConfig:
    def __init__(self, config_file):
        self.config_file = config_file
        self._config = None
        self._last_modified = None
        self.load()
    
    def load(self):
        mtime = Path(self.config_file).stat().st_mtime
        if self._last_modified is None or mtime > self._last_modified:
            with open(self.config_file) as f:
                self._config = yaml.safe_load(f)
            self._last_modified = mtime
            print(f"Config reloaded at {datetime.now()}")
    
    def get(self, key, default=None):
        self.load()  # Recharge si modifié
        keys = key.split('.')
        value = self._config
        for k in keys:
            value = value.get(k, default)
            if value == default:
                break
        return value

# Usage
config = DynamicConfig('config.yaml')
db_host = config.get('database.host')
# Modifiez config.yaml...
db_host = config.get('database.host')  # Nouvelle valeur!


# === CONFIGURATION AVEC HÉRITAGE ===

# base-service.yaml
service: &service
  resources:
    limits:
      memory: 512Mi
      cpu: 500m
    requests:
      memory: 256Mi
      cpu: 250m
  
  env:
    - name: LOG_LEVEL
      value: info

# api-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  <<: *service
  replicas: 3
  env:
    - name: LOG_LEVEL
      value: debug
    - name: API_KEY
      valueFrom:
        secretKeyRef:
          name: api-secret

# worker-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: worker
spec:
  <<: *service
  replicas: 5
  resources:
    limits:
      memory: 1Gi      # Override


[OK] SÉCURITÉ YAML


# === ATTAQUE: YAML Bomb ===

# [X] DANGEREUX - Consomme énormément de mémoire
a: &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"]
b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]
c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]
d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]

# PROTECTION: Utiliser safe_load
data = yaml.safe_load(content)  # [OK] Protégé
# Éviter: yaml.load(content)    # [X] Vulnérable


# === ATTAQUE: Code Injection ===

# [X] DANGEREUX avec yaml.load()
malicious = """
!!python/object/apply:os.system
args: ['rm -rf /']
"""

# [X] NE JAMAIS FAIRE
data = yaml.load(malicious)  # Exécute le code!

# [OK] TOUJOURS UTILISER safe_load
data = yaml.safe_load(malicious)  # Erreur, n'exécute pas


# === BONNES PRATIQUES SÉCURITÉ ===

import yaml

# 1. Toujours safe_load pour données non fiables
with open('user_config.yaml') as f:
    config = yaml.safe_load(f)  # [OK]

# 2. Valider structure après chargement
from jsonschema import validate
validate(instance=config, schema=schema)

# 3. Limiter taille fichier
MAX_SIZE = 10 * 1024 * 1024  # 10MB
with open('config.yaml') as f:
    content = f.read(MAX_SIZE + 1)
    if len(content) > MAX_SIZE:
        raise ValueError("File too large")
    config = yaml.safe_load(content)

# 4. Sanitizer valeurs
def sanitize_config(config):
    """Retire clés sensibles avant log"""
    sensitive_keys = ['password', 'secret', 'token', 'key']
    if isinstance(config, dict):
        return {
            k: '***' if any(s in k.lower() for s in sensitive_keys)
            else sanitize_config(v)
            for k, v in config.items()
        }
    elif isinstance(config, list):
        return [sanitize_config(item) for item in config]
    return config

# 5. Chiffrer secrets
from cryptography.fernet import Fernet

def encrypt_secrets(config, cipher):
    if isinstance(config, dict):
        return {
            k: cipher.encrypt(v.encode()).decode()
            if 'secret' in k.lower() or 'password' in k.lower()
            else encrypt_secrets(v, cipher)
            for k, v in config.items()
        }
    return config


[OK] TESTS ET MOCKING


# === TESTER CHARGEMENT YAML ===

import pytest
import yaml
from pathlib import Path

def test_load_valid_config():
    """Test chargement config valide"""
    config_content = """
    application:
      name: TestApp
      port: 8000
    """
    config = yaml.safe_load(config_content)
    assert config['application']['name'] == 'TestApp'
    assert config['application']['port'] == 8000

def test_load_invalid_yaml():
    """Test YAML invalide"""
    invalid_content = """
    key: value
      invalid indentation
    """
    with pytest.raises(yaml.YAMLError):
        yaml.safe_load(invalid_content)

def test_config_schema():
    """Test validation schéma"""
    from jsonschema import validate, ValidationError
    
    schema = {
        "type": "object",
        "properties": {
            "port": {"type": "integer", "minimum": 1024}
        },
        "required": ["port"]
    }
    
    # Valide
    config = {"port": 8000}
    validate(instance=config, schema=schema)
    
    # Invalide
    with pytest.raises(ValidationError):
        config = {"port": 80}  # < 1024
        validate(instance=config, schema=schema)


# === FIXTURES PYTEST ===

import pytest
import yaml
import tempfile

@pytest.fixture
def temp_yaml_file():
    """Crée fichier YAML temporaire"""
    with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
        config = {
            'database': {
                'host': 'localhost',
                'port': 5432
            }
        }
        yaml.dump(config, f)
        yield f.name
    Path(f.name).unlink()

def test_with_temp_file(temp_yaml_file):
    """Test avec fichier temporaire"""
    with open(temp_yaml_file) as f:
        config = yaml.safe_load(f)
    assert config['database']['host'] == 'localhost'


# === MOCKING ===

from unittest.mock import patch, mock_open
import yaml

def load_config_from_file(filepath):
    with open(filepath) as f:
        return yaml.safe_load(f)

def test_load_config_mock():
    """Mock lecture fichier"""
    yaml_content = """
    app:
      name: MockApp
    """
    
    with patch('builtins.open', mock_open(read_data=yaml_content)):
        config = load_config_from_file('fake.yaml')
        assert config['app']['name'] == 'MockApp'


# === PROPERTY-BASED TESTING ===

from hypothesis import given, strategies as st
import yaml

@given(st.dictionaries(
    keys=st.text(min_size=1, max_size=10),
    values=st.one_of(st.integers(), st.text(), st.booleans())
))
def test_yaml_roundtrip(data):
    """Test que dump puis load préserve données"""
    yaml_str = yaml.dump(data)
    loaded = yaml.safe_load(yaml_str)
    assert loaded == data


[OK] DEBUGGING YAML


# === AFFICHER STRUCTURE ===

import yaml

def print_yaml_structure(obj, indent=0):
    """Affiche structure YAML de façon lisible"""
    if isinstance(obj, dict):
        for key, value in obj.items():
            print('  ' * indent + f'[DOSSIER] {key}:')
            print_yaml_structure(value, indent + 1)
    elif isinstance(obj, list):
        for i, item in enumerate(obj):
            print('  ' * indent + f'[FICHIER] [{i}]')
            print_yaml_structure(item, indent + 1)
    else:
        print('  ' * indent + f'[IMPORTANT] {type(obj).__name__}: {obj}')

# Usage
with open('config.yaml') as f:
    config = yaml.safe_load(f)
    print_yaml_structure(config)


# === DIFFÉRENCES ENTRE FICHIERS ===

import yaml
from deepdiff import DeepDiff

def compare_yaml_files(file1, file2):
    """Compare deux fichiers YAML"""
    with open(file1) as f1, open(file2) as f2:
        data1 = yaml.safe_load(f1)
        data2 = yaml.safe_load(f2)
    
    diff = DeepDiff(data1, data2, ignore_order=True)
    
    if diff:
        print("Différences trouvées:")
        for key, value in diff.items():
            print(f"\n{key}:")
            print(value)
    else:
        print("Fichiers identiques")
    
    return diff


# === TRACER PARSING ===

import yaml
import logging

logging.basicConfig(level=logging.DEBUG)

class DebugLoader(yaml.SafeLoader):
    def construct_object(self, node, deep=False):
        logging.debug(f"Constructing {node.tag}: {node.value[:50]}")
        return super().construct_object(node, deep)

# Usage
with open('config.yaml') as f:
    config = yaml.load(f, Loader=DebugLoader)


# === VALIDER AVANT PRODUCTION ===

#!/usr/bin/env python3
"""Script validation config YAML"""

import sys
import yaml
from pathlib import Path
from jsonschema import validate, ValidationError

def validate_yaml_file(filepath, schema=None):
    """Valide fichier YAML"""
    errors = []
    
    # Vérifier existence
    if not Path(filepath).exists():
        return [f"[X] Fichier introuvable: {filepath}"]
    
    # Vérifier parsing
    try:
        with open(filepath) as f:
            data = yaml.safe_load(f)
    except yaml.YAMLError as e:
        return [f"[X] Erreur parsing YAML: {e}"]
    
    # Vérifier non vide
    if data is None:
        return [f"[X] Fichier vide"]
    
    # Vérifier schéma
    if schema:
        try:
            validate(instance=data, schema=schema)
        except ValidationError as e:
            return [f"[X] Validation schéma: {e.message}"]
    
    return []

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("Usage: validate_yaml.py <file.yaml>")
        sys.exit(1)
    
    errors = validate_yaml_file(sys.argv[1])
    
    if errors:
        for error in errors:
            print(error)
        sys.exit(1)
    else:
        print("[OK] Validation réussie")
        sys.exit(0)


[OK] YAML AVEC AUTRES LANGAGES


# === JAVASCRIPT / NODE.JS ===

// Installation
npm install js-yaml

// Usage
const yaml = require('js-yaml');
const fs = require('fs');

// Lire
const config = yaml.load(fs.readFileSync('config.yaml', 'utf8'));

// Écrire
const data = {
  name: 'MyApp',
  version: '1.0.0'
};
fs.writeFileSync('output.yaml', yaml.dump(data));

// Options
yaml.dump(data, {
  indent: 2,
  lineWidth: 120,
  noRefs: true,
  sortKeys: true
});


# === GO ===

# Installation
go get gopkg.in/yaml.v3

// Usage
package main

import (
    "gopkg.in/yaml.v3"
    "io/ioutil"
    "log"
)

type Config struct {
    Name    string `yaml:"name"`
    Version string `yaml:"version"`
    Port    int    `yaml:"port"`
}

func main() {
    // Lire
    data, err := ioutil.ReadFile("config.yaml")
    if err != nil {
        log.Fatal(err)
    }
    
    var config Config
    err = yaml.Unmarshal(data, &config)
    if err != nil {
        log.Fatal(err)
    }
    
    // Écrire
    data, err = yaml.Marshal(&config)
    if err != nil {
        log.Fatal(err)
    }
    ioutil.WriteFile("output.yaml", data, 0644)
}


# === RUBY ===

# Installation (inclus par défaut)
require 'yaml'

# Lire
config = YAML.load_file('config.yaml')

# Écrire
File.write('output.yaml', YAML.dump(config))

# Safe load
config = YAML.safe_load(File.read('config.yaml'))


# === PHP ===

// Installation
composer require symfony/yaml

// Usage
use Symfony\Component\Yaml\Yaml;

// Lire
$config = Yaml::parseFile('config.yaml');

// Écrire
$yaml = Yaml::dump($config, 4, 2);
file_put_contents('output.yaml', $yaml);


# === RUST ===

# Installation (Cargo.toml)
[dependencies]
serde_yaml = "0.9"
serde = { version = "1.0", features = ["derive"] }

// Usage
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Config {
    name: String,
    version: String,
    port: u16,
}

fn main() {
    // Lire
    let f = std::fs::File::open("config.yaml").unwrap();
    let config: Config = serde_yaml::from_reader(f).unwrap();
    
    // Écrire
    let f = std::fs::File::create("output.yaml").unwrap();
    serde_yaml::to_writer(f, &config).unwrap();
}


[OK] YAML DANS CI/CD


# === GITLAB CI ===

# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"

# Templates (ancres)
.test_template: &test_template
  stage: test
  image: python:3.11
  before_script:
    - pip install -r requirements.txt
  cache:
    paths:
      - .cache/pip

# Jobs
test:python39:
  <<: *test_template
  image: python:3.9
  script:
    - pytest tests/

test:python311:
  <<: *test_template
  image: python:3.11
  script:
    - pytest tests/

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - main
    - develop

deploy:production:
  stage: deploy
  image: alpine:latest
  script:
    - apk add --no-cache curl
    - curl -X POST $DEPLOY_WEBHOOK
  only:
    - main
  when: manual
  environment:
    name: production
    url: https://example.com


# === AZURE PIPELINES ===

# azure-pipelines.yml
trigger:
  branches:
    include:
      - main
      - develop

pool:
  vmImage: 'ubuntu-latest'

variables:
  python.version: '3.11'

stages:
  - stage: Test
    jobs:
      - job: RunTests
        steps:
          - task: UsePythonVersion@0
            inputs:
              versionSpec: '$(python.version)'
          
          - script: |
              pip install -r requirements.txt
              pytest --cov=src
            displayName: 'Run tests'
          
          - task: PublishCodeCoverageResults@1
            inputs:
              codeCoverageTool: 'Cobertura'
              summaryFileLocation: 'coverage.xml'

  - stage: Build
    dependsOn: Test
    jobs:
      - job: BuildDocker
        steps:
          - task: Docker@2
            inputs:
              command: 'buildAndPush'
              repository: 'myapp'
              tags: '$(Build.BuildId)'


# === CIRCLE CI ===

# .circleci/config.yml
version: 2.1

executors:
  python-executor:
    docker:
      - image: cimg/python:3.11
    working_directory: ~/project

jobs:
  test:
    executor: python-executor
    steps:
      - checkout
      - restore_cache:
          keys:
            - deps-{{ checksum "requirements.txt" }}
      - run:
          name: Install dependencies
          command: pip install -r requirements.txt
      - save_cache:
          paths:
            - ~/.cache/pip
          key: deps-{{ checksum "requirements.txt" }}
      - run:
          name: Run tests
          command: pytest --junitxml=test-results/junit.xml
      - store_test_results:
          path: test-results

  build:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build Docker image
          command: |
            docker build -t myapp:${CIRCLE_SHA1} .
            docker push myapp:${CIRCLE_SHA1}

workflows:
  version: 2
  test-and-build:
    jobs:
      - test
      - build:
          requires:
            - test
          filters:
            branches:
              only: main


[OK] RESSOURCES ET DOCUMENTATION


# === SPÉCIFICATIONS ===

# YAML 1.2 Spec (officiel)
https://yaml.org/spec/1.2/spec.html

# YAML 1.1 Spec (ancien mais encore utilisé)
https://yaml.org/spec/1.1/

# YAML Type Repository
https://yaml.org/type/


# === OUTILS EN LIGNE ===

# Validateurs YAML
https://www.yamllint.com/
https://codebeautify.org/yaml-validator
https://jsonformatter.org/yaml-validator

# Convertisseurs
https://www.json2yaml.com/          # JSON <-> YAML
https://onlineyamltools.com/        # Divers outils

# Éditeurs
https://stackedit.io/               # Markdown + YAML front matter


# === DOCUMENTATION PYTHON ===

# PyYAML
https://pyyaml.org/wiki/PyYAMLDocumentation

# ruamel.yaml
https://yaml.readthedocs.io/

# strictyaml
https://hitchdev.com/strictyaml/


# === GUIDES ET TUTORIELS ===

# Learn YAML in Y minutes
https://learnxinyminutes.com/docs/yaml/

# YAML Tutorial (Tutorials Point)
https://www.tutorialspoint.com/yaml/

# Real Python - YAML
https://realpython.com/python-yaml/


# === SCHÉMAS JSON ===

# JSON Schema Store (schémas pour configs courantes)
https://www.schemastore.org/json/

# Exemples:
# - docker-compose.json
# - github-workflow.json
# - gitlab-ci.json
# - ansible-playbook.json
# - kubernetes.json


# === LINTERS ET FORMATTERS ===

# yamllint
https://github.com/adrienverge/yamllint

# prettier (avec plugin)
https://prettier.io/
npm install --save-dev prettier @prettier/plugin-yaml

# yamlfmt
https://github.com/google/yamlfmt


# === COMMUNAUTÉ ===

# Stack Overflow - Tag YAML
https://stackoverflow.com/questions/tagged/yaml

# Reddit
https://www.reddit.com/r/yaml/

# YAML Issues (GitHub)
https://github.com/yaml/yaml/issues


[OK] RÉCAPITULATIF RAPIDE


# === SYNTAXE ESSENTIELLE ===

# Scalaires
string: value
number: 42
float: 3.14
boolean: true
null: ~

# Collections
list:
  - item1
  - item2

dict:
  key: value
  nested:
    key: value

# Multi-ligne
literal: |
  Preserve
  newlines

folded: >
  Fold into
  single line

# Ancres
base: &anchor
  key: value

extended:
  <<: *anchor


# === COMMANDES PYTHON ===

import yaml

# Lire
with open('file.yaml') as f:
    data = yaml.safe_load(f)

# Écrire
with open('file.yaml', 'w') as f:
    yaml.dump(data, f, default_flow_style=False)

# Multiple documents
with open('file.yaml') as f:
    for doc in yaml.safe_load_all(f):
        process(doc)


# === CHECKLIST PRODUCTION ===

[OK] Utiliser yaml.safe_load() (pas load())
[OK] Valider avec schéma JSON
[OK] Tester parsing dans CI/CD
[OK] Versionner configs (Git)
[OK] Ne pas commiter secrets
[OK] Utiliser yamllint
[OK] Documenter structure
[OK] Environnements séparés (dev/prod)
[OK] Variables d'environnement pour secrets
[OK] Backup configs importantes


# === DÉBOGAGE RAPIDE ===

# Valider syntaxe
python -c "import yaml; yaml.safe_load(open('file.yaml'))"

# Afficher parsé
python -c "import yaml, json; print(json.dumps(yaml.safe_load(open('file.yaml')), indent=2))"

# Trouver ligne d'erreur
yamllint file.yaml

# Comparer avec JSON
yq eval -o=json file.yaml


# === ANTI-PATTERNS À ÉVITER ===

[X] Tabs pour indentation
[X] yaml.load() sur données non fiables
[X] Secrets en clair dans Git
[X] Structure trop profonde (>5 niveaux)
[X] Clés avec caractères spéciaux sans guillemets
[X] Fichiers YAML énormes (>1000 lignes)
[X] Duplication au lieu d'ancres
[X] Pas de validation en CI/CD
[X] Mélange de styles (bloc vs flux)
[X] Commentaires obsolètes