Skip to content

feat(checks): add a safe run_sync() convenience method #2782

Description

@kevinmessiaen

Problem

The public checks API is async-only. In a normal Python script, users must write asyncio.run(scenario.run()) (and the same applies to Suite and TestCase). This is boilerplate for a common synchronous entry point.

Proposed change

Add run_sync(...) to the public, user-facing runnable types in giskard-checks:

  • Scenario
  • Suite
  • TestCase

Keep the existing async run(...) API unchanged. Use one shared internal helper or mixin so the loop-detection behavior and error message stay consistent.

Example:

result = scenario.run_sync()
results = suite.run_sync(parallel=True, verbose=False)

Required behavior

  • In a plain synchronous script or synchronous test, run_sync(*args, **kwargs) returns the same result as asyncio.run(self.run(*args, **kwargs)).
  • It forwards all positional and keyword arguments exactly to run.
  • It must work from a non-main thread that has no running event loop.
  • If called while an asyncio event loop is already running (Jupyter/IPython notebooks, async pytest tests, async web handlers, or an async def function), it must raise a clear RuntimeError that tells the user to call await obj.run(...) instead. It must not patch, nest, stop, or run the existing loop in another thread.
  • Do not add nest_asyncio or another runtime dependency.

Docs and tests

  • Update the checks README next to the current async script example. Show both forms:

    # Script / no running event loop
    result = scenario.run_sync()
    
    # Notebook, pytest async test, or async function
    result = await scenario.run()
  • Add unit tests for each supported type: successful synchronous execution, argument forwarding, result/exception propagation, and the active-loop error path.

Out of scope

  • Do not change run() to be synchronous.
  • Do not try to make synchronous blocking calls work inside a running loop.
  • Do not expand this issue to low-level Check.run() or agent workflow/tool APIs; those may need a separate API-design decision.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions