Skip to main content

Run asynchronous commands in WebDrivers

Project description

Caqui

Python application PyPI Downloads

Caqui is a Python library for browser, mobile, and desktop automation that works with any driver that exposes a WebDriver-style REST API. It lets you send commands synchronously or asynchronously, and you don’t need to think about which underlying driver you’re using.

Caqui is designed for developers who want a unified automation API that can run:

  • WebDriver (Chrome, Firefox, Opera, Edge)
  • Appium (Android, iOS)
  • Winium / WinAppDriver (Windows desktop applications)
  • Any remote WebDriver-compatible server

Caqui runs seamlessly on a local machine or across remote hosts, and supports both multitasking with asyncio and multiprocessing for high-throughput use cases such as parallel testing, web scraping, or distributed automation.


Supported Drivers

WebDriver Version Remote* If remote
Appium 2.0.0+ Y Accepts remote calls by default. Tested with Appium in Docker
Firefox (geckodriver) 113+ Y Requires defining the host IP, e.g. --host 123.45.6.78
Google Chrome 113+ Y Requires allowed IPs, e.g. --allowed-ips=123.45.6.78
Opera 99+ Y Same restrictions as Chrome
WinAppDriver 1.2.1+ Y Requires host IP, e.g. WinApppage.exe 10.0.0.10 4723
Winium Desktop 1.6.0+ Y Accepts remote calls by default

*Remote = can accept REST requests when running as a server.


Installation

pip install caqui

Using Caqui 2.0.0+

From version 2.0.0+, Caqui includes a high-level API that mirrors Selenium’s object model and exposes async methods for browser, mobile, and desktop automation. Full documentation:

Example:

from os import getcwd
from pytest import mark, fixture
from caqui.easy.drivers import AsyncDriver
from caqui.easy.capabilities import ChromeCapabilitiesBuilder
from caqui.by import By
from caqui.easy.server import LocalServer

BASE_DIR = getcwd()
PAGE_URL = f"file:///{BASE_DIR}/html/playground.html"
SERVER_PORT = 9999
SERVER_URL = f"http://localhost:{SERVER_PORT}"


@fixture(autouse=True, scope="session")
def setup_server():
    server = LocalServer(port=SERVER_PORT)
    server.start_chrome()
    yield
    server.dispose(delay=3)


@fixture
def caqui_driver():
    server_url = SERVER_URL
    capabilities = (
        ChromeCapabilitiesBuilder().accept_insecure_certs(True).args(["headless"])
    )
    page = AsyncDriver(server_url, capabilities)
    yield page
    page.quit()


@mark.asyncio
async def test_switch_to_parent_frame_and_click_alert(caqui_driver: AsyncDriver):
    await caqui_driver.get(PAGE_URL)
    element_frame = await caqui_driver.find_element(By.ID, "my-iframe")
    assert await caqui_driver.switch_to.frame(element_frame)

    alert_button_frame = await caqui_driver.find_element(By.ID, "alert-button-iframe")
    await alert_button_frame.click()
    await caqui_driver.switch_to.alert.dismiss()

    await caqui_driver.switch_to.default_content()
    alert_button_parent = await caqui_driver.find_element(By.ID, "alert-button")
    assert await alert_button_parent.get_attribute("any") == "any"
    await alert_button_parent.click()

Running Tests with Multitasking

Caqui supports asyncio out of the box. To run multiple async tests concurrently, use pytest-async-cooperative:

@mark.asyncio_cooperative
async def test_save_screenshot(caqui_driver: AsyncDriver):
    await caqui_driver.get(PAGE_URL)
    assert await caqui_driver.save_screenshot("/tmp/test.png")


@mark.asyncio_cooperative
async def test_click(caqui_driver: AsyncDriver):
    await caqui_driver.get(PAGE_URL)
    element = await caqui_driver.find_element(By.XPATH, "//button")
    await element.click()

Running tests this way significantly reduces execution time, especially when interacting with multiple drivers or sessions.


Running Tests with Multiprocessing

If your workloads benefit from multiple processes, Caqui also works with pytest-xdist. This approach is often faster than cooperative multitasking.

A guide to optimizing performance (including a real benchmark): Speed up your web crawlers at 90%

Example:

@mark.asyncio
async def test_save_screenshot(caqui_driver: AsyncDriver):
    await caqui_driver.get(PAGE_URL)
    assert await caqui_driver.save_screenshot("/tmp/test.png")


@mark.asyncio
async def test_click(caqui_driver: AsyncDriver):
    await caqui_driver.get(PAGE_URL)
    element = await caqui_driver.find_element(By.XPATH, "//button")
    await element.click()

Running a Driver as a Server

If you use external drivers such as Appium, Winium, or a standalone ChromeDriver, run them as servers and point Caqui to their URL.

Example for ChromeDriver on port 9999:

$ ./chromedriver --port=9999
Starting ChromeDriver 94.0.4606.61 (418b78f5838ed0b1c69bb4e51ea0252171854915-refs/branch-heads/4606@{#1204}) on port 9999
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.

WebDriver Manager

Caqui’s LocalServer class uses Webdriver Manager. The tool comes with its own constraints. Check its documentation for details if you need custom driver handling.


Contributing

Before submitting a pull request, review the project guidelines: Code of Conduct: CODE OF CONDUCT

Contribution Guide: CONTRIBUTING

Contributions, issue reports, and performance feedback are welcome.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

caqui-4.0.0.tar.gz (34.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

caqui-4.0.0-py3-none-any.whl (35.8 kB view details)

Uploaded Python 3

File details

Details for the file caqui-4.0.0.tar.gz.

File metadata

  • Download URL: caqui-4.0.0.tar.gz
  • Upload date:
  • Size: 34.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.25

File hashes

Hashes for caqui-4.0.0.tar.gz
Algorithm Hash digest
SHA256 4ab1b552b65d7000514aa196e70eb74f061da2e2a10a12f3c3c2c3318ec2dba0
MD5 be9c9eaf5c22b15297fddf7b05196a3a
BLAKE2b-256 9a9e0395b31a5f26e78ffbcb72288c80f683bc2bf57a5a16ca6705d9f66af365

See more details on using hashes here.

File details

Details for the file caqui-4.0.0-py3-none-any.whl.

File metadata

  • Download URL: caqui-4.0.0-py3-none-any.whl
  • Upload date:
  • Size: 35.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.25

File hashes

Hashes for caqui-4.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3198be6458d1a508cd2ff22c2084768908fed003965c3bb84fba9a9bf1dfb417
MD5 428f93e33445b6f96b7864c4f19d0838
BLAKE2b-256 0fecb0be2b23d37fdd6ffcf6f4da74e48018878be1d2413645bdbea69f50fb8e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page