From b8071b83186bcbbfcc3dace6da83142cfa8c336f Mon Sep 17 00:00:00 2001 From: CocoRoF Date: Mon, 23 Mar 2026 14:23:53 +0900 Subject: [PATCH 1/8] feat: Add smoke tests for package installation and import verification --- tests/__init__.py | 0 tests/test_smoke.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/test_smoke.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_smoke.py b/tests/test_smoke.py new file mode 100644 index 0000000..3ec18c8 --- /dev/null +++ b/tests/test_smoke.py @@ -0,0 +1,39 @@ +"""Smoke tests — verify the package installs and imports correctly. + +These tests don't require a browser and should pass in any CI environment. +""" + +import playleft + + +def test_version_exists(): + """__version__ should be a non-empty string.""" + assert isinstance(playleft.__version__, str) + assert len(playleft.__version__) > 0 + + +def test_version_format(): + """__version__ should look like semver (x.y.z).""" + parts = playleft.__version__.split(".") + assert len(parts) == 3 + for part in parts: + assert part.isdigit() + + +def test_all_public_classes_importable(): + """Every name in __all__ should be importable.""" + for name in playleft.__all__: + assert hasattr(playleft, name), f"{name} listed in __all__ but not found" + + +def test_playleft_instantiation(): + """PlaywLeft() should create an instance without errors.""" + pw = playleft.PlaywLeft() + assert pw is not None + + +def test_chromium_browser_type(): + """PlaywLeft().chromium() should return a BrowserType.""" + pw = playleft.PlaywLeft() + bt = pw.chromium() + assert isinstance(bt, playleft.BrowserType) From 8f3a3648a57c82504160e49b96c32eacd269ecb1 Mon Sep 17 00:00:00 2001 From: CocoRoF Date: Mon, 23 Mar 2026 15:21:24 +0900 Subject: [PATCH 2/8] fix: Update tokio-tungstenite and reqwest dependencies for native-tls support --- crates/core/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 2d5e5fe..6265459 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -15,11 +15,11 @@ tracing-subscriber = { workspace = true } futures-util = { workspace = true } url = { workspace = true } -# WebSocket client for CDP (rustls for cross-platform builds) -tokio-tungstenite = { version = "0.26", features = ["rustls-tls-webpki-roots"] } +# WebSocket client for CDP (native-tls for cross-platform compatibility) +tokio-tungstenite = { version = "0.26", features = ["native-tls"] } # HTTP client for browser discovery -reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls"] } # Process management which = "7" From 896a4d8371d13928033b5cf701082cffa010f3b0 Mon Sep 17 00:00:00 2001 From: CocoRoF Date: Mon, 23 Mar 2026 15:25:19 +0900 Subject: [PATCH 3/8] fix: Update tokio-tungstenite and reqwest dependencies for vendored OpenSSL support --- crates/core/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 6265459..b5a3cc5 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -15,11 +15,11 @@ tracing-subscriber = { workspace = true } futures-util = { workspace = true } url = { workspace = true } -# WebSocket client for CDP (native-tls for cross-platform compatibility) -tokio-tungstenite = { version = "0.26", features = ["native-tls"] } +# WebSocket client for CDP (vendored OpenSSL for cross-platform builds) +tokio-tungstenite = { version = "0.26", features = ["native-tls-vendored"] } # HTTP client for browser discovery -reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls-vendored"] } # Process management which = "7" From 10c4e830853066d0ae3ad98db9122403fb51cc75 Mon Sep 17 00:00:00 2001 From: CocoRoF Date: Mon, 23 Mar 2026 15:43:53 +0900 Subject: [PATCH 4/8] fix: Update OpenSSL installation steps and adjust tokio-tungstenite and reqwest features for native-tls support --- .github/workflows/ci.yml | 7 +++++++ .github/workflows/publish.yml | 12 ++++++++++++ crates/core/Cargo.toml | 6 +++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6550d90..0b37481 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install OpenSSL + run: sudo apt-get update && sudo apt-get install -y libssl-dev + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: @@ -108,6 +111,10 @@ jobs: restore-keys: | pip-${{ matrix.os }}-py${{ matrix.python-version }}- + - name: Install OpenSSL (Linux) + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y libssl-dev + - name: Install package and test deps run: | python -m pip install --upgrade pip diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 87749ca..5cef04a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -109,6 +109,9 @@ jobs: restore-keys: | pip-publish-py${{ matrix.python-version }}- + - name: Install OpenSSL + run: sudo apt-get update && sudo apt-get install -y libssl-dev + - name: Install package and test deps run: | python -m pip install --upgrade pip @@ -156,6 +159,15 @@ jobs: target: ${{ matrix.target }} args: --release --out dist --interpreter 3.10 3.11 3.12 3.13 manylinux: ${{ matrix.manylinux }} + before-script-linux: | + # Install OpenSSL development libraries for native-tls + if command -v dnf &> /dev/null; then + dnf install -y openssl-devel perl-IPC-Cmd + elif command -v yum &> /dev/null; then + yum install -y openssl-devel perl-IPC-Cmd + elif command -v apk &> /dev/null; then + apk add --no-cache openssl-dev + fi - name: Upload wheels uses: actions/upload-artifact@v4 diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index b5a3cc5..da3850f 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -15,11 +15,11 @@ tracing-subscriber = { workspace = true } futures-util = { workspace = true } url = { workspace = true } -# WebSocket client for CDP (vendored OpenSSL for cross-platform builds) -tokio-tungstenite = { version = "0.26", features = ["native-tls-vendored"] } +# WebSocket client for CDP +tokio-tungstenite = { version = "0.26", features = ["native-tls"] } # HTTP client for browser discovery -reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls-vendored"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls"] } # Process management which = "7" From 0e40b13ada29f2cbd3a4711ac548d77b67fb44fd Mon Sep 17 00:00:00 2001 From: CocoRoF Date: Mon, 23 Mar 2026 15:47:49 +0900 Subject: [PATCH 5/8] fix: Update manylinux version to 2_28 and streamline OpenSSL installation in publish workflow --- .github/workflows/publish.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5cef04a..92647e4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -131,11 +131,11 @@ jobs: # ── Linux x86_64 ── - os: ubuntu-latest target: x86_64-unknown-linux-gnu - manylinux: auto + manylinux: "2_28" # ── Linux aarch64 ── - os: ubuntu-latest target: aarch64-unknown-linux-gnu - manylinux: auto + manylinux: "2_28" # ── macOS x86_64 (Intel, cross-compiled on ARM) ── - os: macos-14 target: x86_64-apple-darwin @@ -161,13 +161,7 @@ jobs: manylinux: ${{ matrix.manylinux }} before-script-linux: | # Install OpenSSL development libraries for native-tls - if command -v dnf &> /dev/null; then - dnf install -y openssl-devel perl-IPC-Cmd - elif command -v yum &> /dev/null; then - yum install -y openssl-devel perl-IPC-Cmd - elif command -v apk &> /dev/null; then - apk add --no-cache openssl-dev - fi + dnf install -y openssl-devel - name: Upload wheels uses: actions/upload-artifact@v4 From 9159cf09a06c55eb0d3716df766b1ac0b675aa02 Mon Sep 17 00:00:00 2001 From: CocoRoF Date: Mon, 23 Mar 2026 16:46:04 +0900 Subject: [PATCH 6/8] fix: Enhance OpenSSL installation for cross-platform compatibility in publish workflow --- .github/workflows/publish.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 92647e4..94cb808 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -161,7 +161,13 @@ jobs: manylinux: ${{ matrix.manylinux }} before-script-linux: | # Install OpenSSL development libraries for native-tls - dnf install -y openssl-devel + if command -v dnf &> /dev/null; then + dnf install -y openssl-devel + elif command -v apt-get &> /dev/null; then + apt-get update && apt-get install -y libssl-dev pkg-config + elif command -v yum &> /dev/null; then + yum install -y openssl-devel + fi - name: Upload wheels uses: actions/upload-artifact@v4 From cee8f41c43970164828d9f3b8cf753b950ec2a55 Mon Sep 17 00:00:00 2001 From: CocoRoF Date: Mon, 23 Mar 2026 17:32:28 +0900 Subject: [PATCH 7/8] fix: Remove OpenSSL installation steps and update tokio-tungstenite and reqwest features for rustls support --- .github/workflows/ci.yml | 7 ------- .github/workflows/publish.yml | 12 ------------ crates/core/Cargo.toml | 6 +++--- 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b37481..6550d90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,9 +46,6 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install OpenSSL - run: sudo apt-get update && sudo apt-get install -y libssl-dev - - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: @@ -111,10 +108,6 @@ jobs: restore-keys: | pip-${{ matrix.os }}-py${{ matrix.python-version }}- - - name: Install OpenSSL (Linux) - if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install -y libssl-dev - - name: Install package and test deps run: | python -m pip install --upgrade pip diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 94cb808..b299ec6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -109,9 +109,6 @@ jobs: restore-keys: | pip-publish-py${{ matrix.python-version }}- - - name: Install OpenSSL - run: sudo apt-get update && sudo apt-get install -y libssl-dev - - name: Install package and test deps run: | python -m pip install --upgrade pip @@ -159,15 +156,6 @@ jobs: target: ${{ matrix.target }} args: --release --out dist --interpreter 3.10 3.11 3.12 3.13 manylinux: ${{ matrix.manylinux }} - before-script-linux: | - # Install OpenSSL development libraries for native-tls - if command -v dnf &> /dev/null; then - dnf install -y openssl-devel - elif command -v apt-get &> /dev/null; then - apt-get update && apt-get install -y libssl-dev pkg-config - elif command -v yum &> /dev/null; then - yum install -y openssl-devel - fi - name: Upload wheels uses: actions/upload-artifact@v4 diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index da3850f..d8409bb 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -15,11 +15,11 @@ tracing-subscriber = { workspace = true } futures-util = { workspace = true } url = { workspace = true } -# WebSocket client for CDP -tokio-tungstenite = { version = "0.26", features = ["native-tls"] } +# WebSocket client for CDP (rustls = pure Rust, no system dependencies) +tokio-tungstenite = { version = "0.26", features = ["rustls-tls-webpki-roots"] } # HTTP client for browser discovery -reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls-webpki-roots"] } # Process management which = "7" From 80ed6084199dfb43d1d3a0e92ac0bcd79679c804 Mon Sep 17 00:00:00 2001 From: CocoRoF Date: Tue, 24 Mar 2026 13:09:28 +0900 Subject: [PATCH 8/8] refactor: Rename package from playleft to playwleft and update related references --- .github/workflows/publish.yml | 8 +++++--- README.md | 4 ++-- examples/agent_workflow.py | 2 +- examples/basic_navigation.py | 2 +- examples/web_scraping.py | 2 +- pyproject.toml | 4 ++-- python/{playleft => playwleft}/__init__.py | 4 ++-- python/{playleft => playwleft}/_core.pdb | Bin python/{playleft => playwleft}/_core.pyi | 0 python/{playleft => playwleft}/playleft.pdb | Bin tests/test_smoke.py | 20 ++++++++++---------- 11 files changed, 24 insertions(+), 22 deletions(-) rename python/{playleft => playwleft}/__init__.py (91%) rename python/{playleft => playwleft}/_core.pdb (100%) rename python/{playleft => playwleft}/_core.pyi (100%) rename python/{playleft => playwleft}/playleft.pdb (100%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b299ec6..cdfdb46 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -203,8 +203,10 @@ jobs: - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 + with: + verbose: true # Uses Trusted Publisher (OIDC) — no API token needed. - # Register at: https://pypi.org/manage/project/playleft/settings/publishing/ + # Register at: https://pypi.org/manage/project/playwleft/settings/publishing/ # ── 6. Create GitHub Release ────────────────────────────── release: @@ -238,10 +240,10 @@ jobs: **Rust-powered release** — native compiled extensions for maximum performance. - Published to [PyPI](https://pypi.org/project/playleft/${{ needs.check-version.outputs.new_version }}/). + Published to [PyPI](https://pypi.org/project/playwleft/${{ needs.check-version.outputs.new_version }}/). ```bash - pip install playleft==${{ needs.check-version.outputs.new_version }} + pip install playwleft==${{ needs.check-version.outputs.new_version }} ``` ### Platforms diff --git a/README.md b/README.md index a84c648..251add7 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ of visual tooling. ```python import asyncio -from playleft import PlaywLeft +from playwleft import PlaywLeft async def main(): async with PlaywLeft() as pw: @@ -76,7 +76,7 @@ asyncio.run(main()) ## Installation ```bash -pip install playleft +pip install playwleft ``` ## Class Hierarchy diff --git a/examples/agent_workflow.py b/examples/agent_workflow.py index 259eed3..0ddb0b3 100644 --- a/examples/agent_workflow.py +++ b/examples/agent_workflow.py @@ -2,7 +2,7 @@ import asyncio import json -from playleft import PlaywLeft +from playwleft import PlaywLeft async def search_and_extract(query: str) -> dict: diff --git a/examples/basic_navigation.py b/examples/basic_navigation.py index d051539..9b27dfa 100644 --- a/examples/basic_navigation.py +++ b/examples/basic_navigation.py @@ -1,7 +1,7 @@ """Basic navigation example for playwLeft.""" import asyncio -from playleft import PlaywLeft +from playwleft import PlaywLeft async def main(): diff --git a/examples/web_scraping.py b/examples/web_scraping.py index 84217fd..b5d0373 100644 --- a/examples/web_scraping.py +++ b/examples/web_scraping.py @@ -2,7 +2,7 @@ import asyncio import json -from playleft import PlaywLeft +from playwleft import PlaywLeft async def main(): diff --git a/pyproject.toml b/pyproject.toml index 3ebdb89..11a163c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["maturin>=1.7,<2.0"] build-backend = "maturin" [project] -name = "playleft" +name = "playwleft" version = "0.1.0" description = "Agent-first browser automation toolkit — Playwright alternative built in Rust" readme = "README.md" @@ -46,7 +46,7 @@ Issues = "https://github.com/CocoRoF/playwLeft/issues" [tool.maturin] manifest-path = "crates/python/Cargo.toml" -module-name = "playleft._core" +module-name = "playwleft._core" python-source = "python" features = ["pyo3/extension-module"] diff --git a/python/playleft/__init__.py b/python/playwleft/__init__.py similarity index 91% rename from python/playleft/__init__.py rename to python/playwleft/__init__.py index b645fe4..04b1d1c 100644 --- a/python/playleft/__init__.py +++ b/python/playwleft/__init__.py @@ -6,7 +6,7 @@ Usage: import asyncio - from playleft import PlaywLeft + from playwleft import PlaywLeft async def main(): pw = PlaywLeft() @@ -20,7 +20,7 @@ async def main(): asyncio.run(main()) """ -from playleft._core import ( +from playwleft._core import ( PlaywLeft, BrowserType, Browser, diff --git a/python/playleft/_core.pdb b/python/playwleft/_core.pdb similarity index 100% rename from python/playleft/_core.pdb rename to python/playwleft/_core.pdb diff --git a/python/playleft/_core.pyi b/python/playwleft/_core.pyi similarity index 100% rename from python/playleft/_core.pyi rename to python/playwleft/_core.pyi diff --git a/python/playleft/playleft.pdb b/python/playwleft/playleft.pdb similarity index 100% rename from python/playleft/playleft.pdb rename to python/playwleft/playleft.pdb diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 3ec18c8..28b6acb 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -3,18 +3,18 @@ These tests don't require a browser and should pass in any CI environment. """ -import playleft +import playwleft def test_version_exists(): """__version__ should be a non-empty string.""" - assert isinstance(playleft.__version__, str) - assert len(playleft.__version__) > 0 + assert isinstance(playwleft.__version__, str) + assert len(playwleft.__version__) > 0 def test_version_format(): """__version__ should look like semver (x.y.z).""" - parts = playleft.__version__.split(".") + parts = playwleft.__version__.split(".") assert len(parts) == 3 for part in parts: assert part.isdigit() @@ -22,18 +22,18 @@ def test_version_format(): def test_all_public_classes_importable(): """Every name in __all__ should be importable.""" - for name in playleft.__all__: - assert hasattr(playleft, name), f"{name} listed in __all__ but not found" + for name in playwleft.__all__: + assert hasattr(playwleft, name), f"{name} listed in __all__ but not found" -def test_playleft_instantiation(): +def test_playwleft_instantiation(): """PlaywLeft() should create an instance without errors.""" - pw = playleft.PlaywLeft() + pw = playwleft.PlaywLeft() assert pw is not None def test_chromium_browser_type(): """PlaywLeft().chromium() should return a BrowserType.""" - pw = playleft.PlaywLeft() + pw = playwleft.PlaywLeft() bt = pw.chromium() - assert isinstance(bt, playleft.BrowserType) + assert isinstance(bt, playwleft.BrowserType)