name: Test Python SDK on: push: branches: [master, next] paths: - 'python/**/*.py' - '.github/workflows/py.test.yml' - '.github/actions/setup-python-uv/action.yml' - 'mise.toml' - 'mise.lock' - 'toolchain-versions.json' - 'python/pyproject.toml' - 'python/requirements*.txt' pull_request: branches: [master, next] paths: - 'python/**/*.py' - '.github/workflows/py.test.yml' - '.github/actions/setup-python-uv/action.yml' - 'mise.toml' - 'mise.lock' - 'toolchain-versions.json' - 'python/pyproject.toml' - 'python/requirements*.txt' concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: contents: read jobs: toolchain-versions: name: Read toolchain test matrix runs-on: ubuntu-latest outputs: python: ${{ steps.versions.outputs.python }} steps: - name: Checkout Code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Read versions id: versions run: echo "python=$(jq -c '.python' toolchain-versions.json)" >> "$GITHUB_OUTPUT" test: name: Test Python SDK needs: toolchain-versions runs-on: ubuntu-latest strategy: matrix: python-version: ${{ fromJSON(needs.toolchain-versions.outputs.python) }} steps: - name: Checkout Code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ matrix.python-version }} - name: Cache uv dependencies uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/uv key: uv-${{ matrix.python-version }}-${{ hashFiles('python/pyproject.toml', 'mise.toml') }} restore-keys: | uv-${{ matrix.python-version }}- - name: Install UV run: | UV_VERSION="$(sed -n 's/^uv[[:space:]]*=[[:space:]]*"\([^"]*\)"/\1/p' mise.toml)" pip install "uv==${UV_VERSION}" - name: Install Dependencies run: | cd python/ uv venv --python ${{ matrix.python-version }} source .venv/bin/activate uv pip install -e . uv pip install -e providers/langchain uv pip install -e providers/autogen uv pip install pytest pytest-mock - name: Run Import Tests run: | cd python/ source .venv/bin/activate python -c "from composio import Composio; print('✓ Basic import successful')" # Guards against the autogen provider becoming un-importable on a fresh # install (e.g. a broken framework dependency). A hard import here fails # loudly, unlike the importorskip-guarded unit tests, which would skip. python -c "import composio_autogen; print('✓ Autogen provider import successful')" - name: Run Unit Tests run: | cd python/ source .venv/bin/activate python -m pytest tests/ -v --tb=short - name: Test Package Installation run: | cd python/ # Test that the package can be installed and imported in a fresh environment uv venv test-env --python ${{ matrix.python-version }} source test-env/bin/activate uv pip install -e . python -c " from composio import Composio, ToolkitVersionParam from composio.types import Modifiers from composio.core.types import ToolkitVersion print('✓ All critical imports successful') " test-circular-imports: name: Test Circular Import Prevention runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Python with UV uses: ./.github/actions/setup-python-uv - name: Install Dependencies run: | cd python/ uv venv source .venv/bin/activate uv pip install -e . - name: Test Circular Import Prevention run: | cd python/ source .venv/bin/activate python -c " # Test various import combinations that previously caused circular imports import sys # Test 1: Basic imports from composio import Composio print('✓ Basic Composio import') # Test 2: Import toolkit version types from composio import ToolkitVersionParam from composio.core.types import ToolkitVersion print('✓ Toolkit version types import') # Test 3: Import from types module from composio.types import Modifiers print('✓ Types module import') # Test 4: Import core models from composio.core.models.tools import Tools print('✓ Core models import') # Test 5: Cross-module imports that caused the original circular import from composio.core.models.tools import Modifiers as CoreModifiers from composio.types import Modifiers as TypesModifiers assert CoreModifiers is TypesModifiers print('✓ Cross-module import consistency') print('✓ All circular import prevention tests passed!') " lint-and-format: name: Lint and Format Check runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Python with UV uses: ./.github/actions/setup-python-uv - name: Install Dependencies run: | cd python/ uv venv source .venv/bin/activate uv pip install -e . uv pip install ruff - name: Run Ruff Linter run: | cd python/ source .venv/bin/activate ruff check --config config/ruff.toml composio/ tests/ - name: Run Ruff Formatter Check run: | cd python/ source .venv/bin/activate ruff format --check --config config/ruff.toml composio/ tests/ integration-tests: name: Integration Tests runs-on: ubuntu-22.04 # Use Ubuntu 22.04 for stability permissions: read-all env: COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }} COMPOSIO_BASE_URL: ${{ secrets.COMPOSIO_BASE_URL_STAGING }} steps: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Install UV id: setup-python uses: ./.github/actions/setup-python-uv - name: Cache UV dependencies uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/uv key: uv-${{ hashFiles('mise.lock') }}-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('python/pyproject.toml') }} restore-keys: | uv-${{ hashFiles('mise.lock') }}-py${{ steps.setup-python.outputs.python-version }}- - name: Install Python Dependencies working-directory: ./python run: | # Let UV manage Python version to satisfy workspace requirements uv sync --group dev - name: Check API key id: api-key run: | if [ -z "$COMPOSIO_API_KEY" ]; then echo "available=false" >> "$GITHUB_OUTPUT" if [ "$GITHUB_ACTOR" = "dependabot[bot]" ]; then echo "::notice::Skipping secret-backed Python integration tests for Dependabot; missing COMPOSIO_API_KEY." exit 0 fi echo "::error::COMPOSIO_API_KEY is not set" exit 1 fi echo "available=true" >> "$GITHUB_OUTPUT" echo "✅ API key is configured" - name: Run Integration Tests if: steps.api-key.outputs.available == 'true' working-directory: ./python run: | echo "🧪 Running Integration Tests..." # Run with per-test timeout of 2 minutes and overall timeout # pytest-timeout should be installed via pyproject.toml dev dependencies uv run pytest composio/integration_test/ -v --tb=short --maxfail=3 --timeout=120 --timeout-method=thread timeout-minutes: 10 - name: Upload Test Results if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: integration-test-results path: | python/composio/integration_test/ retention-days: 7