ArchaeonSeq
ArchaeonSeq

Reputation: 21

Playwright Sync API inside the asyncio loop

So this is my first time with Playwright so I thought to try out the examples only to find none of the work and the errors dont make sense:

I have tried all the examples in the docs and on the github pages and keep getting errors that I simply dont understand. CODE:

`


from playwright.sync_api import sync_playwright

playwright = sync_playwright().start()

browser = playwright.chromium.launch()
page = browser.new_page()
page.goto("https://playwright.dev/")
page.screenshot(path="example.png")
browser.close()

playwright.stop()

` **ERROR: **

---------------------------------------------------------------------------
Error                                     Traceback (most recent call last)
Cell In[23], line 3
      1 from playwright.sync_api import sync_playwright
----> 3 playwright = sync_playwright().start()
      5 browser = playwright.chromium.launch()
      6 page = browser.new_page()

File ~/Downloads/Packaging Industry Anomaly DEtection (PIADE)/venv/lib/python3.12/site-packages/playwright/sync_api/_context_manager.py:84, in PlaywrightContextManager.start(self)
     83 def start(self) -> SyncPlaywright:
---> 84     return self.__enter__()

File ~/Downloads/Packaging Industry Anomaly DEtection (PIADE)/venv/lib/python3.12/site-packages/playwright/sync_api/_context_manager.py:47, in PlaywrightContextManager.__enter__(self)
     45             self._own_loop = True
     46         if self._loop.is_running():
---> 47             raise Error(
     48                 """It looks like you are using Playwright Sync API inside the asyncio loop.
     49 Please use the Async API instead."""
     50             )
     52         # Create a new fiber for the protocol dispatcher. It will be pumping events
     53         # until the end of times. We will pass control to that fiber every time we
     54         # block while waiting for a response.
     55         def greenlet_main() -> None:

Error: It looks like you are using Playwright Sync API inside the asyncio loop.
Please use the Async API instead.

Upvotes: 2

Views: 3195

Answers (2)

Sean McCarthy
Sean McCarthy

Reputation: 5578

Same thing happens to me when I run these two very simple examples from the documentation!

See fix at very bottom of this answer...

import re

import pytest
from playwright.sync_api import Page, expect


def test_has_title(page: Page):
    """
    Demo test from Playwright documentation
    https://playwright.dev/python/docs/intro#running-the-example-test
    """
    page.goto("https://playwright.dev/")

    # Expect a title "to contain" a substring.
    expect(page).to_have_title(re.compile("Playwright"))


def test_get_started_link(page: Page):
    """
    Demo test from Playwright documentation
    https://playwright.dev/python/docs/intro#running-the-example-test
    """
    page.goto("https://playwright.dev/")

    # Click the get started link.
    page.get_by_role("link", name="Get started").click()

    # Expects page to have a heading with the name of Installation.
    expect(page.get_by_role("heading", name="Installation")).to_be_visible()

Here's the long error message....

Exception has occurred: Error       (note: full exception trace is shown but execution is paused at: _run_module_as_main)
It looks like you are using Playwright Sync API inside the asyncio loop.
Please use the Async API instead.
  File "/workspace/.venv/lib/python3.12/site-packages/playwright/sync_api/_context_manager.py", line 47, in __enter__
    raise Error(
  File "/workspace/tests/conftest.py", line 897, in browser_session_scope
    with sync_playwright() as p:
         ^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/fixtures.py", line 891, in call_fixture_func
    fixture_result = next(generator)
                     ^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/fixtures.py", line 1140, in pytest_fixture_setup
    result = call_fixture_func(fixturefunc, request, kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/setuponly.py", line 36, in pytest_fixture_setup
    return (yield)
            ^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/fixtures.py", line 1091, in execute
    result = ihook.pytest_fixture_setup(fixturedef=self, request=request)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/fixtures.py", line 617, in _get_active_fixturedef
    fixturedef.execute(request=subrequest)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/fixtures.py", line 532, in getfixturevalue
    fixturedef = self._get_active_fixturedef(argname)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/fixtures.py", line 697, in _fillfixtures
    item.funcargs[argname] = self.getfixturevalue(argname)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/python.py", line 1630, in setup
    self._request._fillfixtures()
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/runner.py", line 514, in setup
    col.setup()
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/runner.py", line 160, in pytest_runtest_setup
    item.session._setupstate.setup(item)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/threadexception.py", line 68, in thread_exception_runtest_hook
    yield
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/threadexception.py", line 87, in pytest_runtest_setup
    yield from thread_exception_runtest_hook()
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/capture.py", line 875, in pytest_runtest_setup
    return (yield)
            ^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/logging.py", line 829, in _runtest_for
    yield
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/logging.py", line 840, in pytest_runtest_setup
    yield from self._runtest_for(item, "setup")
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/unraisableexception.py", line 70, in unraisable_exception_runtest_hook
    yield
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/unraisableexception.py", line 90, in pytest_runtest_setup
    yield from unraisable_exception_runtest_hook()
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/runner.py", line 242, in <lambda>
    lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/runner.py", line 341, in from_call
    result: TResult | None = func()
                             ^^^^^^
  File "/workspace/tests/conftest.py", line 92, in pytest_exception_interact
    raise call.excinfo.value
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/runner.py", line 248, in call_and_report
    ihook.pytest_exception_interact(node=item, call=call, report=report)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/runner.py", line 126, in runtestprotocol
    rep = call_and_report(item, "setup", log)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/runner.py", line 113, in pytest_runtest_protocol
    runtestprotocol(item, nextitem=nextitem)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/faulthandler.py", line 88, in pytest_runtest_protocol
    return (yield)
            ^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/unittest.py", line 429, in pytest_runtest_protocol
    res = yield
          ^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py", line 176, in pytest_runtest_protocol
    return (yield)
            ^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/warnings.py", line 112, in pytest_runtest_protocol
    return (yield)
            ^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/main.py", line 362, in pytest_runtestloop
    item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/terminal.py", line 673, in pytest_runtestloop
    result = yield
             ^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/logging.py", line 803, in pytest_runtestloop
    return (yield)  # Run all the tests.
            ^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/main.py", line 337, in _main
    config.hook.pytest_runtestloop(session=session)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/main.py", line 303, in wrap_session
    config.notify_exception(excinfo, config.option)
  File "/workspace/tests/conftest.py", line 96, in pytest_internalerror
    raise excinfo.value
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 1173, in notify_exception
    res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/main.py", line 303, in wrap_session
    config.notify_exception(excinfo, config.option)
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/main.py", line 330, in pytest_cmdline_main
    return wrap_session(config, _main)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 175, in main
    ret: ExitCode | int = config.hook.pytest_cmdline_main(config=config)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 201, in console_main
    code = main()
           ^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/pytest/__main__.py", line 9, in <module>
    raise SystemExit(pytest.console_main())
                     ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/runpy.py", line 88, in _run_code
    exec(code, run_globals)
  File "/usr/local/lib/python3.12/runpy.py", line 198, in _run_module_as_main (Current frame)
    return _run_code(code, main_globals, None,
playwright._impl._errors.Error: It looks like you are using Playwright Sync API inside the asyncio loop.
Please use the Async API instead.

Here's the Dockerfile inside which I'm running Pytest:

FROM nikolaik/python-nodejs:python3.12-nodejs22-slim

# Use Docker BuildKit for better caching and faster builds
ARG DOCKER_BUILDKIT=1
ARG BUILDKIT_INLINE_CACHE=1
# Enable BuildKit for Docker-Compose
ARG COMPOSE_DOCKER_CLI_BUILD=1
ARG POETRY_VERSION=1.8.5

ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

# Configure apt and install packages
# I had to add --insecure since curl didn't work...
RUN apt-get update && \
    apt-get install -y --no-install-recommends docker.io docker-compose apt-utils build-essential dialog \
    curl netcat-traditional iputils-ping unzip dos2unix gcc 2>&1 && \
    # Verify git, process tools, lsb-release (common in install instructions for CLIs) installed
    apt-get install -y --no-install-recommends sudo git redis-server libpq-dev sass \
    procps iproute2 lsb-release gnupg apt-transport-https \
    # For display testing
    xvfb xserver-xephyr tigervnc-standalone-server xfonts-base \
    g++ protobuf-compiler libprotobuf-dev && \
    # Clean up
    apt-get autoremove -y && \
    apt-get clean -y && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /workspace

# Install Poetry with the root user
# Ensure pip is the latest version
RUN pip install --upgrade pip poetry==$POETRY_VERSION && \
    # Create a virtualenv at .venv in /workspace directory so we can easily see the site-packages
    poetry config virtualenvs.create true && \
    poetry config virtualenvs.in-project true && \
    poetry config virtualenvs.path "/workspace/.venv"

# # Install Playwright for testing (must install the Python package first)
# RUN playwright install-deps && \
#     playwright install

# BEFORE installing packages with Poetry (so "user" has permissions in future),
# set the user so nobody can run as root on the Docker host (security)
# USER $USER_UID_OLD
# USER $USER_UID_NEW
# ARG TEST=testing

# Make port 5000 available to the world outside this container
EXPOSE 5000

# Copy my preferred .bashrc to /root/ so that it's automatically "sourced" when the container starts
COPY .bashrc /root/

# Copy the entrypoint script to install the Python and Node dependencies if the .venv or node_modules directories don't exist
COPY entrypoint.dev.sh entrypoint.dev.local.sh /
RUN chmod +x /entrypoint.dev.sh /entrypoint.dev.local.sh
# ENTRYPOINT ["/entrypoint.dev.sh"]

Here are my dependencies in my "pyproject.toml":


[tool.poetry.dependencies]
python = ">=3.11,<3.14"
dash = {extras = ["compress", "testing"], version = "^2.18.2"}
dash-bootstrap-components = "^1.6.0"
dash-table = "^5.0.0"
dash-mantine-components = "^0.15.1"
dash-ag-grid = "^31.2.0"
scikit-learn = "^1.5.2"
pandas = "^2.2.1"
Flask = "^3.0.3"
flask-login = "^0.6.3"
flask-caching = "^2.1.0"
flask-sqlalchemy = "^3.1.1"
flask-redis = "^0.4.0"
flask-bootstrap4 = "^4.0.2"
Flask-FlatPages = "^0.8.2"
Flask-WTF = "^1.2.1"
flask-restx = "^1.3.0"
flask-dance = "^7.1.0"
joblib = "^1.2.0"
boto3 = "^1.35.72"
urllib3 = "1.26.18"
gunicorn = "^22.0.0"
python-dotenv = "^1.0.1"
requests = "^2.31.0"
pyjwt = "1.7.1"
sqlalchemy = "^2.0.30"
colour = "^0.1.5"
psycopg2-binary = "^2.9.9"
twilio = "^6.54.0"
openpyxl = "^3.0.7"
phonenumbers = "^8.12.29"
celery = "^5.4.0"
Flask-HTMLmin = "^2.2.1"
ipinfo = "^4.2.1"
Werkzeug = "^3.0.1"
Pillow = "^10.4.0"
gcld3 = "^3.0.13"
plotly = "^5.22.0"
numpy = "^1.26.4"
dash-signature = "^0.1.9"
webauthn = "^2.2.0"
flask-cors = "^5.0.0"
protobuf = "^5.28.2"
pyright = "^1.1.392.post0"
rich = "^13.9.1"
alembic = "^1.13.3"
flask-migrate = "^4.0.7"
botocore = "^1.35.75"
gevent = "^24.11.1"
psycogreen = "^1.0.2"
flask-admin = "2.0.0a3"
email-validator = "^2.2.0"
paho-mqtt = "^2.1.0"
google-cloud-recaptcha-enterprise = "^1.26.1"

[tool.poetry.group.dev.dependencies]
pytest = "^8.3.2"
pytest-mock = "^3.14.0"
tabulate = "^0.8.7"
kaleido = "0.2.0"
beautifulsoup4 = "^4.9.3"
pre-commit = "^3.4.0"
ruff = "^0.5.5"
make-responsive-images = "^0.1.17"
wtforms-alchemy = "^0.18.0"
djlint = "^1.35.2"
playwright = "^1.49.1"
pytest-playwright = "^0.6.2"
pyvirtualdisplay = "^3.0"

To fix it, I made my own pytest fixtures:


# @pytest.fixture(scope="function")
@pytest.fixture(scope="session")
def auth_page(
    # dash_thread_server: ThreadedRunner,
    dash_process_server: BackgroundProcessRunner,
    auth_context: BrowserContext,
    base_url: str,
) -> Generator[Page, None, None]:
    """
    Provide an authenticated page for each test
    """
    page = auth_context.new_page()
    # setattr(page, "server", dash_thread_server)
    setattr(page, "server", dash_process_server)

    try:
        # Go to the login page
        page.goto(f"{base_url}/login/")
        perform_login_sequence(page)

        # Verify login success
        # page.wait_for_load_state("networkidle")
        if "Authenticate" in page.title():
            raise AuthenticationError("Failed to authenticate - still on login page")

        # Store authentication state
        auth_context.storage_state(path="auth.json")

        yield page

    except Exception as err:
        logger.error("Auth page creation failed: %s", err)
        playwright_screenshot(page, "auth_page_failure")
        raise err
    finally:
        page.close()



@pytest.fixture(scope="session")
def auth_context(
    browser_session_scope: Browser,
) -> Generator[BrowserContext, None, None]:
    """
    Create and maintain an authenticated browser context for the test session
    """
    # Create a persistent context
    context = browser_session_scope.new_context()
    try:
        yield context
    finally:
        context.close()



def main_url():
    """Get the main URL for the application."""
    return "http://localhost:5000"


@pytest.fixture(scope="session")
def base_url():
    """Get the base URL for the application."""
    return main_url()


@pytest.fixture(scope="session")
def dash_process_server(
    # dash_app_complex_session_scope: Dash,
    # dash_app_simple_session_scope: Dash,
    base_url: str,
) -> Generator[BackgroundProcessRunner, None, None]:
    """Start a local dash server in a new process."""

    with BackgroundProcessRunner(
        keep_open=True,  # Keep server running for all tests
        stop_timeout=5,
    ) as starter:
        # Start the server
        set_testing_environment_vars()
        try:
            time_start: float = time.time()
            starter.start(
                # app_module=str(PROJECT_FOLDER.joinpath("wsgi.py")),
                app_module="wsgi",
                application_name="flask_app",
                host=os.getenv("FLASK_RUN_HOST", "0.0.0.0"),
                port=int(os.getenv("FLASK_RUN_PORT", 5000)),
                # raw_command="gunicorn --chdir /project --config /project/app/config_gunicorn.py --bind 0.0.0.0:5000 wsgi:flask_app",
                # raw_command="flask run --no-reload --no-debugger --host 0.0.0.0 --port 5000 --with-threads",
                start_timeout=20,
            )
            time_end: float = time.time()
            seconds_taken: float = time_end - time_start
            logger.info(f"Server started in {seconds_taken:.2f} seconds")
        except Exception as e:
            logger.error(f"Failed to start server: {e}")
            starter.stop()
            raise e

        if not verify_server_running(base_url):
            raise RuntimeError("Server failed to start and be accessible")

        try:
            yield starter
        finally:
            # Stop the server
            starter.stop()




@pytest.fixture(scope="session")
def auth_context(
    browser_session_scope: Browser,
) -> Generator[BrowserContext, None, None]:
    """
    Create and maintain an authenticated browser context for the test session
    """
    # Create a persistent context
    context = browser_session_scope.new_context()
    try:
        yield context
    finally:
        context.close()




@pytest.fixture(scope="session")
def browser_session_scope() -> Generator[Browser, None, None]:
    """
    Create a browser instance for testing
    """
    # # Display is active
    # disp = Display().start()
    # Start the display with Xvfb
    # with Display(visible=os.getenv("FLASK_ENV") == "development", size=(1920, 1080)):
    with sync_playwright() as p:
        options = get_browser_launch_options()
        # Launch browser with WebSockets enabled
        # browser = await p.chromium.connect("ws://127.0.0.1:5000/")
        browser: Browser = p.chromium.launch(**options)
        try:
            yield browser
        finally:
            browser.close()
            # # Display is stopped
            # disp.stop()

Then I changed the tests to use my own auth_page fixture:



def test_has_title(auth_page: Page):
    """
    Demo test from Playwright documentation
    https://playwright.dev/python/docs/intro#running-the-example-test
    """
    auth_page.goto("https://playwright.dev/")

    # Expect a title "to contain" a substring.
    expect(auth_page).to_have_title(re.compile("Playwright"))


def test_get_started_link(auth_page: Page):
    """
    Demo test from Playwright documentation
    https://playwright.dev/python/docs/intro#running-the-example-test
    """
    auth_page.goto("https://playwright.dev/")

    # Click the get started link.
    auth_page.get_by_role("link", name="Get started").click()

    # Expects page to have a heading with the name of Installation.
    expect(auth_page.get_by_role("heading", name="Installation")).to_be_visible()

Upvotes: 1

Nailuree
Nailuree

Reputation: 51

I assume you're running this code in a notebook kernel. This is not possible to use the sync API in this context since there is a running asyncio eventloop.

Use the async API or run your code outside of a notebook kernel.

Upvotes: 5

Related Questions