Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Python Runtime Tests

on:
pull_request:
push:
branches:
- main
workflow_dispatch:

concurrency:
group: python-tests-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
python-unit-tests:
name: Python Unit Tests (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.12', '3.13']
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Run Python internal unit tests
run: |
python3 -m unittest discover -s src/pyodide/internal -p "test_*.py"

- name: Check git diff and whitespace hygiene
run: |
git diff --check

docker-tests:
name: Containerized Test Suite (Docker)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run tests inside Docker container
run: |
docker run --rm -v "$GITHUB_WORKSPACE":/workerd -w /workerd python:3.12-slim \
python3 -m unittest discover -s src/pyodide/internal -p "test_*.py"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ MODULE.bazel.lock
/.cache/

/.bazel-cache
/.context/
/artifacts
/.gitlab-ci-ca-file
/.gitlab-ci-gitconfig
Expand Down
14 changes: 14 additions & 0 deletions src/pyodide/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,17 @@ py_test(
main = "internal/test_frozen_sdk.py",
target_compatible_with = ["@platforms//os:linux"],
)

py_test(
name = "unit-test-entropy-patches",
srcs = [
"internal/test_entropy_patches.py",
"internal/topLevelEntropy/__init__.py",
"internal/topLevelEntropy/allow_entropy.py",
"internal/topLevelEntropy/entropy_import_context.py",
"internal/topLevelEntropy/entropy_import_context_packages.py",
"internal/topLevelEntropy/entropy_patches.py",
"internal/topLevelEntropy/import_patch_manager.py",
],
main = "internal/test_entropy_patches.py",
)
44 changes: 44 additions & 0 deletions src/pyodide/internal/test_entropy_patches.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright (c) 2026 Cloudflare, Inc.
# Licensed under the Apache 2.0 license found in the LICENSE file or at:
# https://opensource.org/licenses/Apache-2.0

import sys
import unittest
from pathlib import Path
from types import SimpleNamespace

DIR = Path(__file__).parent
sys.path.insert(0, str(DIR))

from topLevelEntropy.import_patch_manager import patches
import topLevelEntropy.entropy_import_context_packages # noqa: F401


class TestEntropyPatches(unittest.TestCase):
def test_httpx_user_agent_patch(self):
"""Ensure httpx._transports.emscripten has HEADERS_TO_IGNORE cleared."""
self.assertIn("httpx._transports.emscripten", patches)
mock_httpx = SimpleNamespace(HEADERS_TO_IGNORE=("user-agent",))
with patches["httpx._transports.emscripten"].exec(mock_httpx):
pass
self.assertEqual(
mock_httpx.HEADERS_TO_IGNORE,
(),
"Expected HEADERS_TO_IGNORE to be cleared for httpx",
)

def test_httpx2_user_agent_patch(self):
"""Ensure httpx2_jsfetch has HEADERS_TO_IGNORE cleared."""
self.assertIn("httpx2_jsfetch", patches)
mock_httpx2 = SimpleNamespace(HEADERS_TO_IGNORE=("user-agent",))
with patches["httpx2_jsfetch"].exec(mock_httpx2):
pass
self.assertEqual(
mock_httpx2.HEADERS_TO_IGNORE,
(),
"Expected HEADERS_TO_IGNORE to be cleared for httpx2_jsfetch",
)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,21 @@ def langchain_openai_chat_models_base_context(module):
yield
else:
yield


@register_exec_patch("httpx._transports.emscripten")
@contextmanager
def httpx_emscripten_context(module):
yield
# In browser environments, User-Agent is a forbidden request header and causes console warnings.
# In workerd, fetch() natively supports custom User-Agent headers without restriction.
module.HEADERS_TO_IGNORE = ()


@register_exec_patch("httpx2_jsfetch")
@contextmanager
def httpx2_jsfetch_context(module):
yield
# In browser environments, User-Agent is a forbidden request header and causes console warnings.
# In workerd, fetch() natively supports custom User-Agent headers without restriction.
module.HEADERS_TO_IGNORE = ()
Loading