Hacktoberfest 2026 : les issues que les mainteneurs ont marquées pour octobre, ouvertes et accessibles aux débutants. Parcourir les issues Hacktoberfest

Workflow sandbox re-imports annotated_types, so pydantic silently drops constraints

Ouverte Adaptée aux débutants
#1,897 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

Personne n'a encore pris cette issue.

Évaluation

Difficulté
2/5
Temps estimé
1-3 heures
Accessibilité débutants
84/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
Active
Stack technique
python
Domaine
tooling

Piste de recherche

Commencez dans temporalio/worker/workflow_sandbox/_restrictions.py, au niveau de passthrough_modules_with_temporal, puis examinez les patterns de l’importateur dans tests/worker/workflow_sandbox/test_importer.py. Utilisez la reproduction fournie de pydantic et annotated_types pour ajouter un test de régression ; le travail est terminé lorsque la contrainte reste appliquée dans les cas host-first et sandbox-first, sans l’avertissement d’importation du sandbox.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

Summary

SandboxRestrictions.passthrough_modules_with_temporal passes pydantic and, since 1.33.0 (#1834), pydantic_core through, but not annotated_types. pydantic imports annotated_types inside functions, so each sandbox gets its own copy, and pydantic's lru_cached class → constraint map (_get_at_to_constraint_map) only knows the copy that filled it first. A constraint built from the other copy falls through to "ignore any other unknown metadata", with no error:

  • The host builds a constrained model first: a workflow's Annotated[int, annotated_types.Ge(1)] is not enforced.
  • A workflow builds one first: host-side Field(ge=0) and conint(ge=0) are not enforced for the rest of the process, activities included.

Each sandbox also pays a fresh import: a median 4.7 ms here, against 0.003 ms passed through. When that import happens during an activation, the sandbox warns Module annotated_types was imported after initial workflow load.

Repro

temporalio 1.33.0, pydantic 2.13.4, annotated-types 0.7.0, Python 3.12.3, macOS 26 arm64. It drives the sandbox's Importer the way tests/worker/workflow_sandbox/test_importer.py does.

"""pydantic drops constraints built from the sandbox's annotated_types copy, or the host's."""

import subprocess
import sys
import tempfile
from pathlib import Path

MODELS = """
from typing import Annotated
import annotated_types
from pydantic import BaseModel

class Positive(BaseModel):
    x: Annotated[int, annotated_types.Ge(1)]
"""


def case(first, passthrough):
    from pydantic import BaseModel, Field, ValidationError
    from temporalio.worker.workflow_sandbox import SandboxRestrictions
    from temporalio.worker.workflow_sandbox._importer import Importer
    from temporalio.worker.workflow_sandbox._restrictions import RestrictionContext

    restrictions = SandboxRestrictions.default
    if passthrough:
        restrictions = restrictions.with_passthrough_modules("annotated_types")

    def in_sandbox():
        with Importer(restrictions, RestrictionContext()).applied():
            import sandboxed_models
        return sandboxed_models.Positive

    def on_host():
        class Host(BaseModel):
            n: int = Field(ge=0)

        return Host

    if first == "host":
        host, sandboxed = on_host(), in_sandbox()
    else:
        sandboxed, host = in_sandbox(), on_host()

    def result(model, **values):
        try:
            model(**values)
        except ValidationError:
            return "enforced"
        return "DROPPED"

    return f"sandbox Ge(1): {result(sandboxed, x=0)}, host ge=0: {result(host, n=-1)}"


if __name__ == "__main__":
    if len(sys.argv) == 4:  # one case per fresh process: pydantic's cache is process-wide
        sys.path.insert(0, sys.argv[3])
        print(case(sys.argv[1], sys.argv[2] == "passthrough"))
    else:
        models = tempfile.mkdtemp()
        Path(models, "sandboxed_models.py").write_text(MODELS)
        for first in ("host", "sandbox"):
            for mode in ("default", "passthrough"):
                out = subprocess.run(
                    [sys.executable, __file__, first, mode, models],
                    capture_output=True,
                    text=True,
                    check=True,
                ).stdout.strip()
                print(f"{first + ' first,':<14} {mode + ':':<13} {out}")
host first,    default:      sandbox Ge(1): DROPPED, host ge=0: enforced
host first,    passthrough:  sandbox Ge(1): enforced, host ge=0: enforced
sandbox first, default:      sandbox Ge(1): enforced, host ge=0: DROPPED
sandbox first, passthrough:  sandbox Ge(1): enforced, host ge=0: enforced
Proposal

Add "annotated_types" next to "pydantic" and "pydantic_core" in passthrough_modules_with_temporal. It is a pure-Python module of constraint dataclasses that imports only the standard library and typing_extensions, with no I/O, time or randomness. temporalio's own StrandsPlugin and GoogleGenAIPlugin already pass it through; other users need a runner override such as SandboxRestrictions.default.with_passthrough_modules("annotated_types").

I can open the PR: the one-line change, a CHANGELOG entry, and a regression test in tests/worker/workflow_sandbox/test_importer.py built from the repro.

Langage dominant
Python
Étoiles
1.2k
Forks
245
Merge moyen
2 j 17 h
PR mergées (30 j)
38

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de temporalio/sdk-python

Toutes les issues de temporalio/sdk-python

Issues similaires

Plus d'issues Python

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.