> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreweave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Modèles synchrones ou asynchrones

> Choisissez entre les modèles synchrones et asynchrones dans le SDK Python de sandbox CoreWeave.

Ces exemples utilisent un jeton d’accès à l’API CoreWeave défini dans `CWSANDBOX_API_KEY`. Pour l’authentification par clé API Forge, voir [Premiers pas](/fr/products/sandboxes/get-started). Pour les exigences relatives à CKS, voir [Modes de placement](/fr/products/sandboxes/placement).

Ce guide vous aide à choisir entre les modèles synchrones et asynchrones dans le SDK Python de sandbox CoreWeave (`cwsandbox`) et présente les variantes synchrone et asynchrone de chaque opération courante.

En interne, le SDK repose sur une implémentation asynchrone unique. Le choix entre synchrone et asynchrone tient uniquement à la manière dont vous consommez les résultats : utilisez `.result()` en mode synchrone et `await` en mode asynchrone.

<h2 id="quick-decision-guide">
  Guide de décision rapide
</h2>

| Cas d’utilisation | Approche | Raison |
| - | - | - |
| La plupart des opérations | Synchrone | Code plus simple, sans code passe-partout asyncio |
| Exécution parallèle | Synchrone | Les opérations sont conçues pour être non-blocking |
| Notebooks Jupyter | Synchrone | Pas besoin de `nest_asyncio` |
| Base de code asynchrone | Asynchrone | S’intègre au code asynchrone existant |

**Règle générale** : privilégiez l’approche synchrone (`.result()`) pour plus de simplicité. Ne passez à `await` que si vous travaillez déjà
dans une base de code asynchrone.

<h2 id="core-concept-operationref">
  Concept fondamental : OperationRef
</h2>

La plupart des méthodes du SDK renvoient `OperationRef[T]`, un encapsuleur qui prend en charge à la fois l’appel à `.result()` et `await`.
Les deux approches lèvent les mêmes exceptions.

```python theme={"system"}
ref = sandbox.read_file("/output/data.txt")

# Synchrone : bloquer jusqu’à l’obtention de la valeur
data = ref.result()

# Asynchrone : utiliser await à la place
data = await ref
```

`Sandbox.exec()` renvoie `Process`, qui étend `OperationRef[ProcessResult]` en y ajoutant la diffusion en continu
et stdin.

<Warning>
  N’utilisez jamais `.result()` dans un contexte asynchrone. Appeler `.result()` bloque le thread. Dans un contexte asynchrone, cela bloque la boucle d’événements et
  peut provoquer un interblocage de votre application. Utilisez plutôt `await`.
</Warning>

<h2 id="auto-start-behavior">
  Comportement de démarrage automatique
</h2>

`session.sandbox()` renvoie une sandbox non démarrée. La sandbox démarre automatiquement lors de la première opération
qui la nécessite :

**Déclenchent le démarrage automatique** : `exec()`, `read_file()`, `write_file()`, `wait()`, `wait_until_complete()`

**Ne déclenchent pas le démarrage automatique** : `get_status()` (lève `SandboxNotRunningError`), `stop()` (no-op si la sandbox n’a jamais démarré)

<h2 id="operations">
  Opérations
</h2>

<h3 id="create-sandboxes">
  Créer des Sandbox
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    from cwsandbox import Sandbox

    # Gestionnaire de contexte (recommandé)
    with Sandbox.run() as sb:
        result = sb.exec(["echo", "hello"]).result()
    # Arrêté automatiquement à la sortie
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    from cwsandbox import Sandbox

    # Instancier + await pour atteindre RUNNING, puis gestionnaire de contexte asynchrone pour le nettoyage
    sb = Sandbox()
    async with sb as sb:
        result = await sb.exec(["echo", "hello"])
    # Arrêté automatiquement à la sortie
    ```
  </Tab>
</Tabs>

<Note>
  `Sandbox.run()` appelle `start().result()` en interne, ce qui bloque la boucle d’événements.
  Dans du code asynchrone, instanciez l’objet avec `Sandbox(...)` et utilisez `async with` ou `await sandbox`
  pour atteindre le statut `RUNNING` sans bloquer l’exécution.
</Note>

<h3 id="start">
  start()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    sb = Sandbox()
    sb.start().result()  # Bloque jusqu'à ce que le backend accepte la requête
    print(sb.sandbox_id)
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    sb = Sandbox()
    await sb.start()  # Attend que le backend accepte la requête
    print(sb.sandbox_id)
    ```
  </Tab>
</Tabs>

<h3 id="exec">
  exec()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    result = sb.exec(["echo", "hello"]).result()
    print(result.stdout)  # "hello\n"

    # Lever une exception si le code de sortie est non nul
    result = sb.exec(["python", "-c", "exit(1)"], check=True).result()
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    result = await sb.exec(["echo", "hello"])
    print(result.stdout)  # "hello\n"

    # Lever une exception si le code de sortie est non nul
    result = await sb.exec(["python", "-c", "exit(1)"], check=True)
    ```
  </Tab>
</Tabs>

<h3 id="read_file">
  read\_file()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    data = sb.read_file("/output/result.txt").result()
    print(data.decode())
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    data = await sb.read_file("/output/result.txt")
    print(data.decode())
    ```
  </Tab>
</Tabs>

<h3 id="write_file">
  write\_file()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    sb.write_file("/input/data.txt", b"content").result()
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    await sb.write_file("/input/data.txt", b"content")
    ```
  </Tab>
</Tabs>

<h3 id="stop">
  stop()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    sb.stop().result()

    # Ignorer si déjà supprimé
    sb.stop(missing_ok=True).result()
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    await sb.stop()

    # Ignorer si déjà supprimé
    await sb.stop(missing_ok=True)
    ```
  </Tab>
</Tabs>

<h3 id="wait">
  wait()
</h3>

`wait()` bloque l'exécution jusqu'à ce que la sandbox atteigne le statut `RUNNING`. Cette méthode est sync-only, car le blocage est
précisément le but recherché. Dans du code asynchrone, `await sandbox` produit le même effet.

```python theme={"system"}
# Synchrone : bloque jusqu’à l’état RUNNING
sb = Sandbox.run().wait()
result = sb.exec(["echo", "ready"]).result()
```

```python theme={"system"}
# Asynchrone : attendre directement la sandbox
sb = Sandbox()
await sb  # Attendre l’état RUNNING
result = await sb.exec(["echo", "ready"])
```

<h3 id="wait_until_complete">
  wait\_until\_complete()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    sb = Sandbox.run("python", "-c", "print('done')")
    sb.wait_until_complete().result()
    print(f"Exit code: {sb.returncode}")

    # Gérer les bacs à sable arrêtés de l'extérieur sans lever d'exception
    sb.wait_until_complete(raise_on_termination=False).result()
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    sb = Sandbox(command="python", args=["-c", "print('done')"])
    await sb.wait_until_complete()
    print(f"Exit code: {sb.returncode}")

    # Gérer les bacs à sable arrêtés de l'extérieur sans lever d'exception
    await sb.wait_until_complete(raise_on_termination=False)
    ```
  </Tab>
</Tabs>

<h3 id="get_status">
  get\_status()
</h3>

`get_status()` est sync-only. Cette méthode récupère le statut actuel auprès de l’API.

```python theme={"system"}
status = sb.get_status()
print(f"Sandbox is {status}")  # par ex. SandboxStatus.RUNNING
```

<h3 id="sandboxlist">
  Sandbox.list()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    sandboxes = Sandbox.list(tags=["my-job"]).result()
    for sb in sandboxes:
        print(f"{sb.sandbox_id}: {sb.status}")
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    sandboxes = await Sandbox.list(tags=["my-job"])
    for sb in sandboxes:
        print(f"{sb.sandbox_id}: {sb.status}")
    ```
  </Tab>
</Tabs>

<h3 id="sandboxfrom_id">
  Sandbox.from\_id()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    sb = Sandbox.from_id("sandbox-abc123").result()
    result = sb.exec(["echo", "reconnected"]).result()
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    sb = await Sandbox.from_id("sandbox-abc123")
    result = await sb.exec(["echo", "reconnected"])
    ```
  </Tab>
</Tabs>

<h3 id="sandboxdelete">
  Sandbox.delete()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    Sandbox.delete("sandbox-abc123").result()

    # Ignorer si déjà supprimé
    Sandbox.delete("sandbox-abc123", missing_ok=True).result()
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    await Sandbox.delete("sandbox-abc123")

    # Ignorer si déjà supprimé
    await Sandbox.delete("sandbox-abc123", missing_ok=True)
    ```
  </Tab>
</Tabs>

<h3 id="sessionlist">
  session.list()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    sandboxes = session.list().result()
    for sb in sandboxes:
        print(f"{sb.sandbox_id}: {sb.status}")
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    sandboxes = await session.list()
    for sb in sandboxes:
        print(f"{sb.sandbox_id}: {sb.status}")
    ```
  </Tab>
</Tabs>

<h3 id="sessionfrom_id">
  session.from\_id()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    sb = session.from_id("sandbox-abc123").result()
    result = sb.exec(["echo", "adopted"]).result()
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    sb = await session.from_id("sandbox-abc123")
    result = await sb.exec(["echo", "adopted"])
    ```
  </Tab>
</Tabs>

<h3 id="stream-stdout">
  Diffuser la sortie stdout en continu
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    process = sb.exec(["python", "-c", "import time; [print(i) or time.sleep(0.1) for i in range(5)]"])
    for line in process.stdout:
        print(line, end="")
    result = process.result()
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    process = sb.exec(["python", "-c", "import time; [print(i) or time.sleep(0.1) for i in range(5)]"])
    async for line in process.stdout:
        print(line, end="")
    result = await process
    ```
  </Tab>
</Tabs>

<h3 id="stream-stdin">
  Transmettre stdin en flux
</h3>

Activez stdin avec `stdin=True`. Utilisez `write()` pour les octets bruts, `writeline()` pour les lignes de texte
et `close()` pour signaler la fin de fichier (EOF).

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    process = sb.exec(["cat"], stdin=True)
    process.stdin.write(b"hello ").result()
    process.stdin.writeline("world").result()
    process.stdin.close().result()  # Signale la fin de fichier (EOF) au processus
    result = process.result()
    print(result.stdout)  # "hello world\n"
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    process = sb.exec(["cat"], stdin=True)
    await process.stdin.write(b"hello ")
    await process.stdin.writeline("world")
    await process.stdin.close()  # Signale la fin de fichier (EOF) au processus
    result = await process
    print(result.stdout)  # "hello world\n"
    ```
  </Tab>
</Tabs>

<h3 id="cwsandboxresults">
  cwsandbox.results()
</h3>

`cwsandbox.results()` est un utilitaire de traitement par lot sync-only. Il appelle `.result()` sur un ou plusieurs objets `OperationRef`.

```python theme={"system"}
import cwsandbox

# Réf. unique
data = cwsandbox.results(sandbox.read_file("/path"))

# Réf. multiples
all_data = cwsandbox.results([sb.read_file(f) for f in files])
```

<h3 id="cwsandboxwait">
  cwsandbox.wait()
</h3>

`cwsandbox.wait()` est sync-only. Cette fonction attend une séquence d’objets `Sandbox`, `OperationRef` ou `Process`
et renvoie `(done, pending)`. Une Sandbox est résolue dès qu’elle atteint le statut `RUNNING`, et non à son
achèvement.

```python theme={"system"}
import cwsandbox

# Attendre que toutes les sandboxes atteignent l’état RUNNING
sandboxes = [Sandbox.run() for _ in range(5)]
done, pending = cwsandbox.wait(sandboxes)

# Attendre la fin des 2 premières opérations
refs = [sb.read_file(f) for f in files]
done, pending = cwsandbox.wait(refs, num_returns=2)

# Attendre avec un délai d’expiration
done, pending = cwsandbox.wait(procs, timeout=30.0)
```

<h3 id="sessionfunction">
  @session.function()
</h3>

<Tabs>
  <Tab title="Synchrone">
    ```python theme={"system"}
    with Session(defaults) as session:
        @session.function()
        def compute(x: int, y: int) -> int:
            return x + y

        result = compute.remote(2, 3).result()
        print(result)  # 5

        # Map en parallèle
        refs = compute.map([(1, 2), (3, 4), (5, 6)])
        results = [r.result() for r in refs]  # [3, 7, 11]
    ```
  </Tab>

  <Tab title="Asynchrone">
    ```python theme={"system"}
    async with Session(defaults) as session:
        @session.function()
        def compute(x: int, y: int) -> int:
            return x + y

        result = await compute.remote(2, 3)
        print(result)  # 5

        # Map en parallèle
        refs = compute.map([(1, 2), (3, 4), (5, 6)])
        results = [await r for r in refs]  # [3, 7, 11]
    ```
  </Tab>
</Tabs>

<h2 id="parallel-execution">
  Exécution parallèle
</h2>

L’API synchrone prend en charge l’exécution parallèle, car les opérations sont **non-blocking dès la conception**. Les méthodes
telles que `exec()`, `read_file()` et `write_file()` rendent la main immédiatement. Le blocage n’intervient que lorsque vous appelez
`.result()`.

`Sandbox.run()` reste bloquante jusqu’à ce que le backend accepte la requête de démarrage (elle appelle `start().result()`
en interne). Pour un démarrage parallèle, utilisez `session.sandbox()` (démarrage automatique à la première opération) ou
collectez les réf. renvoyées par `start()` :

```python theme={"system"}
from cwsandbox import Sandbox, Session, SandboxDefaults

# Option 1 : session.sandbox() - les sandboxes démarrent automatiquement au premier appel à exec()
with Session(SandboxDefaults()) as session:
    sandboxes = [session.sandbox() for _ in range(3)]
    processes = [sb.exec(["echo", f"sb-{i}"]) for i, sb in enumerate(sandboxes)]
    results = [p.result() for p in processes]

# Option 2 : Sandbox() + collecte des réf. renvoyées par start()
sandboxes = [Sandbox() for _ in range(3)]
start_refs = [sb.start() for sb in sandboxes]
for ref in start_refs:
    ref.result()  # Tous les démarrages se déroulent en parallèle

processes = [sb.exec(["echo", f"sb-{i}"]) for i, sb in enumerate(sandboxes)]
results = [p.result() for p in processes]
for sb in sandboxes:
    sb.stop().result()
```

<h2 id="jupyter-notebooks">
  Notebooks Jupyter
</h2>

L’API synchrone fonctionne dans Jupyter sans `nest_asyncio`, car le SDK exécute sa propre boucle d’événements
en arrière-plan dans un thread démon :

```python theme={"system"}
# Cellule 1 - Créer la sandbox
from cwsandbox import Sandbox
sandbox = Sandbox.run()
sandbox.wait()  # Attendre l’état RUNNING

# Cellule 2 - Exécuter des commandes
result = sandbox.exec(["python", "-c", "print(1+1)"]).result()
print(result.stdout)

# Cellule 3 - Nettoyage
sandbox.stop().result()
```

Pour le code asynchrone dans Jupyter, vous pouvez utiliser `await` directement, car Jupyter dispose d’une boucle d’événements intégrée :

```python theme={"system"}
# Fonctionne dans Jupyter sans asyncio.run()
sandboxes = await Sandbox.list(tags=["notebook"])
```

<h2 id="error-handling">
  Gestion des erreurs
</h2>

Les modes synchrone et asynchrone lèvent les mêmes exceptions. `.result()` relance toute exception
issue de l’opération sous-jacente, tout comme `await`. Consultez [Que signifient les messages d’erreur courants du SDK Sandbox ?](/support/sandboxes/articles/what-do-common-sandbox-sdk-errors-mean)
pour connaître la hiérarchie complète des exceptions et les stratégies de récupération.
