diff --git a/.github/actions/setup-appium-server/action.yml b/.github/actions/setup-appium-server/action.yml index d866ce451..a9177d4e8 100644 --- a/.github/actions/setup-appium-server/action.yml +++ b/.github/actions/setup-appium-server/action.yml @@ -28,8 +28,6 @@ runs: steps: - name: Start Appium server shell: bash - env: - LOG_FILE: ${{ inputs.log_file }} run: | nohup appium server \ --port=${{ inputs.port }} \ @@ -38,12 +36,10 @@ runs: --log-timestamp \ --keep-alive-timeout 1200 \ ${{ inputs.server_args }} \ - > "$LOG_FILE" 2>&1 & + 2>&1 > ${{ inputs.log_file }} & - name: Wait for Appium server to start shell: bash - env: - LOG_FILE: ${{ inputs.log_file }} run: | TIMEOUT_SEC=${{ inputs.timeout }} INTERVAL_SEC=1 @@ -55,13 +51,12 @@ runs: if nc -z ${{ inputs.host }} ${{ inputs.port }}; then echo "Appium server is running after $elapsed seconds" - cat "$LOG_FILE" + cat ${{ inputs.log_file }} exit 0 fi if [[ "$elapsed" -ge "$TIMEOUT_SEC" ]]; then echo "${elapsed} seconds timeout reached: Appium server is NOT running" - cat "$LOG_FILE" exit 1 fi diff --git a/.github/workflows/functional-test.yml b/.github/workflows/functional-test.yml index b297dfe77..d388e450f 100644 --- a/.github/workflows/functional-test.yml +++ b/.github/workflows/functional-test.yml @@ -14,11 +14,9 @@ env: APPIUM_TEST_SERVER_PORT: '4723' APPIUM_TEST_SERVER_HOST: '127.0.0.1' PYTHONUNBUFFERED: 1 - UV_LOCKED: 'true' jobs: ios_test: - timeout-minutes: 60 strategy: fail-fast: false matrix: @@ -78,41 +76,43 @@ jobs: run: xcrun --sdk iphonesimulator --show-sdk-version - name: Set up Python - uses: actions/setup-python@v7 + uses: actions/setup-python@v6 with: python-version: 3.14 - - name: Set up uv - uses: astral-sh/setup-uv@v10.1.0 + - name: Cache uv modules + uses: actions/cache@v6 with: - enable-cache: true - python-version: '3.14' + path: | + ~/.cache/uv + .venv + key: ${{ runner.os }}-uv-shared-${{ hashFiles('**/uv.lock') }} + restore-keys: | + ${{ runner.os }}-uv-shared- + + - name: Install uv + run: make install-uv - name: Run Tests run: | uv run pytest -v ${{ matrix.test_targets.target}} \ --doctest-modules \ --junitxml=junit/test-results.xml \ - --cov=appium \ + --cov=com \ --cov-report=xml \ --cov-report=html env: LOCAL_PREBUILT_WDA: ${{ env.PREBUILT_WDA_PATH }} - - name: Save test diagnostics + - name: Save server output if: ${{ always() }} uses: actions/upload-artifact@master with: - name: appium-ios-${{matrix.test_targets.name}}-diagnostics - path: | - appium.log - junit/ - coverage.xml - htmlcov/ + name: appium-ios-${{matrix.test_targets.name}}.log + path: appium.log android_test: - timeout-minutes: 60 strategy: fail-fast: false matrix: @@ -129,7 +129,7 @@ jobs: steps: - uses: actions/checkout@v7 - - uses: actions/setup-java@v6.0.1 + - uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '17' @@ -185,15 +185,19 @@ jobs: script: echo "Generated AVD snapshot for caching." - name: Set up Python 3.14 - uses: actions/setup-python@v7 + uses: actions/setup-python@v6 with: python-version: 3.14 - - name: Set up uv - uses: astral-sh/setup-uv@v10.1.0 + - name: Cache uv modules + uses: actions/cache@v6 with: - enable-cache: true - python-version: '3.14' + path: | + ~/.cache/uv + .venv + key: ${{ runner.os }}-uv-shared-${{ hashFiles('**/uv.lock') }} + restore-keys: | + ${{ runner.os }}-uv-shared- - name: Run tests uses: reactivecircus/android-emulator-runner@v2 @@ -201,7 +205,8 @@ jobs: api-level: ${{ env.API_LEVEL }} arch: ${{ env.ARCH }} script: | - uv run pytest -v ${{ matrix.test_targets.target}} --doctest-modules --junitxml=junit/test-results.xml --cov=appium --cov-report=xml --cov-report=html + make install-uv + uv run pytest -v ${{ matrix.test_targets.target}} --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html target: google_apis profile: Nexus 5X disable-spellchecker: true @@ -213,19 +218,14 @@ jobs: IGNORE_VERSION_SKIP: true CI: true - - name: Save test diagnostics + - name: Save server output if: ${{ always() }} uses: actions/upload-artifact@master with: - name: appium-android-${{matrix.test_targets.name}}-diagnostics - path: | - appium.log - junit/ - coverage.xml - htmlcov/ + name: appium-android-${{matrix.test_targets.name}}.log + path: appium.log flutter_e2e_test: - timeout-minutes: 60 # These flutter integration driver tests are maintained by: MummanaSubramanya strategy: fail-fast: false @@ -245,15 +245,15 @@ jobs: XCODE_VERSION: 16.4 IOS_VERSION: 18.5 IPHONE_MODEL: iPhone 16 - FLUTTER_ANDROID_APP: "https://github.com/AppiumTestDistribution/appium-flutter-server/releases/download/0.0.33/app-debug.apk" - FLUTTER_IOS_APP: "https://github.com/AppiumTestDistribution/appium-flutter-server/releases/download/0.0.33/ios.zip" + FLUTTER_ANDROID_APP: "https://github.com/AppiumTestDistribution/appium-flutter-server/releases/download/0.0.32/app-debug.apk" + FLUTTER_IOS_APP: "https://github.com/AppiumTestDistribution/appium-flutter-server/releases/download/0.0.32/ios.zip" PREBUILT_WDA_PATH: ${{ github.workspace }}/wda/WebDriverAgentRunner-Runner.app steps: - uses: actions/checkout@v7 - - uses: actions/setup-java@v6.0.1 + - uses: actions/setup-java@v5 if: matrix.e2e-tests == 'flutter-android' with: distribution: 'zulu' @@ -267,7 +267,7 @@ jobs: sudo udevadm trigger --name-match=kvm - name: Set up Python 3.14 - uses: actions/setup-python@v7 + uses: actions/setup-python@v6 with: python-version: 3.14 @@ -305,11 +305,15 @@ jobs: server_args: '--relaxed-security' log_file: appium-${{ matrix.e2e-tests }}.log - - name: Set up uv - uses: astral-sh/setup-uv@v10.1.0 + - name: Cache uv modules + uses: actions/cache@v6 with: - enable-cache: true - python-version: '3.14' + path: | + ~/.cache/uv + .venv + key: ${{ runner.os }}-uv-shared-${{ hashFiles('**/uv.lock') }} + restore-keys: | + ${{ runner.os }}-uv-shared- - name: Run Android tests if: matrix.e2e-tests == 'flutter-android' @@ -317,7 +321,8 @@ jobs: with: api-level: ${{ env.API_LEVEL }} script: | - uv run pytest -v test/functional/flutter_integration/*_test.py --doctest-modules --junitxml=junit/test-results.xml --cov=appium --cov-report=xml --cov-report=html + make install-uv + uv run pytest -v test/functional/flutter_integration/*_test.py --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html target: default disable-spellchecker: true disable-animations: true @@ -344,23 +349,20 @@ jobs: - name: Run IOS tests if: matrix.e2e-tests == 'flutter-ios' run: | + make install-uv export PLATFORM=ios uv run pytest -v test/functional/flutter_integration/*_test.py \ --doctest-modules \ --junitxml=junit/test-results.xml \ - --cov=appium \ + --cov=com \ --cov-report=xml \ --cov-report=html env: LOCAL_PREBUILT_WDA: ${{ env.PREBUILT_WDA_PATH }} - - name: Save test diagnostics + - name: Save server output if: ${{ always() }} uses: actions/upload-artifact@master with: - name: appium-${{ matrix.e2e-tests }}-diagnostics - path: | - appium-${{ matrix.e2e-tests }}.log - junit/ - coverage.xml - htmlcov/ + name: appium-${{ matrix.e2e-tests }}.log + path: appium-${{ matrix.e2e-tests }}.log diff --git a/.github/workflows/lock-update.yml b/.github/workflows/lock-update.yml new file mode 100644 index 000000000..552acc468 --- /dev/null +++ b/.github/workflows/lock-update.yml @@ -0,0 +1,31 @@ +name: Update uv.lock + +on: + pull_request: + types: [ opened, synchronize ] + paths: + - 'pyproject.toml' + +permissions: + contents: write + pull-requests: write + +jobs: + uv-lock-update: + if: github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.head_ref }} + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.12' + - name: Install uv + run: make install-uv + - run: uv lock + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "chore: Refresh uv.lock" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8df3ca7f3..5646ab891 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,8 +2,8 @@ name: Release on: workflow_dispatch: - push: - branches: [ master ] + # push: + # branches: [ master ] permissions: contents: write @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v7 - name: Set up Python - uses: actions/setup-python@v7 + uses: actions/setup-python@v6 with: python-version: '3.12' diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 3fb51951f..4cc39b597 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -7,51 +7,30 @@ on: pull_request: branches: [ master ] -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -env: - UV_LOCKED: 'true' - jobs: - lint: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - uses: actions/checkout@v7 - - name: Set up Python - uses: actions/setup-python@v7 - with: - python-version: '3.14' - - name: Set up uv - uses: astral-sh/setup-uv@v10.1.0 - with: - enable-cache: true - python-version: '3.14' - - name: Check lint and formatting - run: | - uv run ruff check . - make check-format - test-ubuntu: runs-on: ubuntu-latest - timeout-minutes: 20 strategy: matrix: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v7 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v7 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - - name: Set up uv - uses: astral-sh/setup-uv@v10.1.0 + - name: Cache uv modules + uses: actions/cache@v6 with: - enable-cache: true - python-version: ${{ matrix.python-version }} - - name: Check types - run: uv run mypy appium + path: | + ~/.cache/uv + .venv + key: ${{ runner.os }}-uv-shared-${{ hashFiles('**/uv.lock') }} + restore-keys: | + ${{ runner.os }}-uv-shared- + - name: Install uv + run: make install-uv + - name: Run Checks + run: make check - name: Run Unit Tests run: make unittest diff --git a/.ruff.toml b/.ruff.toml index 46e769858..09d820222 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -3,13 +3,12 @@ indent-width = 4 [lint] select = [ - "F", # Pyflakes - "PL", # Pylint - "I", # isort - "PERF", # perflint - "FURB", # Refurb - "SIM", # Simplify - "UP", # Upgrade + # Pyflakes + "F", + # Pylint + "PL", + # isort + "I", ] [lint.per-file-ignores] @@ -23,6 +22,7 @@ select = [ ] [lint.pylint] + max-args = 6 [format] diff --git a/CHANGELOG.md b/CHANGELOG.md index 26f3a524f..2f3043b43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,427 +2,6 @@ -## v6.0.6 (2026-09-12) - -### Bug Fixes - -- Preserve capability alternatives from option lists - ([#1296](https://github.com/appium/python-client/pull/1296), - [`aa4492e`](https://github.com/appium/python-client/commit/aa4492ef25d8bee4b8ef763f088f7745d6a4d07e)) - -### Continuous Integration - -- **deps**: Bump actions/setup-java from 6.0.0 to 6.0.1 - ([#1301](https://github.com/appium/python-client/pull/1301), - [`d555d77`](https://github.com/appium/python-client/commit/d555d779a63a74d1c03d92a15daca31a5e2e4e79)) - -### Documentation - -- Add 2026 year in copyright and actualization packageVersion - ([#1300](https://github.com/appium/python-client/pull/1300), - [`392dd39`](https://github.com/appium/python-client/commit/392dd3947453c99d2b62917cd09562d2f21d9d97)) - -### Testing - -- Cover W3C alternatives from option lists - ([#1296](https://github.com/appium/python-client/pull/1296), - [`aa4492e`](https://github.com/appium/python-client/commit/aa4492ef25d8bee4b8ef763f088f7745d6a4d07e)) - - -## v6.0.5 (2026-09-10) - -### Bug Fixes - -- Preserve default locator strategy in WebDriver type hints - ([#1298](https://github.com/appium/python-client/pull/1298), - [`0bbe39c`](https://github.com/appium/python-client/commit/0bbe39cf6f2139d85d7d1535efc4bef507c9a9fb)) - -### Code Style - -- Preserve existing source line endings ([#1298](https://github.com/appium/python-client/pull/1298), - [`0bbe39c`](https://github.com/appium/python-client/commit/0bbe39cf6f2139d85d7d1535efc4bef507c9a9fb)) - -### Refactoring - -- Import By under TYPE_CHECKING and drop type-hint test - ([#1298](https://github.com/appium/python-client/pull/1298), - [`0bbe39c`](https://github.com/appium/python-client/commit/0bbe39cf6f2139d85d7d1535efc4bef507c9a9fb)) - -### Testing - -- Cover default and explicit WebDriver locator type hints - ([#1298](https://github.com/appium/python-client/pull/1298), - [`0bbe39c`](https://github.com/appium/python-client/commit/0bbe39cf6f2139d85d7d1535efc4bef507c9a9fb)) - - -## v6.0.4 (2026-09-09) - -### Bug Fixes - -- Keep user client config when switching to direct connect endpoint - ([#1291](https://github.com/appium/python-client/pull/1291), - [`d47b730`](https://github.com/appium/python-client/commit/d47b730ea5d769368bfdca1f92948ae459a344d8)) - -### Continuous Integration - -- **deps**: Bump gitpython from 3.1.58 to 3.1.59 - ([#1299](https://github.com/appium/python-client/pull/1299), - [`2edf2d7`](https://github.com/appium/python-client/commit/2edf2d7026725b1216c98d9e1e3375052f4a0817)) - -- **deps-dev**: Bump ruff from 0.16.5 to 0.16.6 - ([#1292](https://github.com/appium/python-client/pull/1292), - [`f8997fd`](https://github.com/appium/python-client/commit/f8997fd373dc26e0e2c340431d997a4de1cc7644)) - - -## v6.0.3 (2026-09-09) - -### Bug Fixes - -- Bracket IPv6 hosts in service status URLs - ([#1295](https://github.com/appium/python-client/pull/1295), - [`a47ddd0`](https://github.com/appium/python-client/commit/a47ddd06938de8cce586727ddcbc8d4c27f8c802)) - -- Validate IPv6 service hosts with ipaddress - ([#1295](https://github.com/appium/python-client/pull/1295), - [`a47ddd0`](https://github.com/appium/python-client/commit/a47ddd06938de8cce586727ddcbc8d4c27f8c802)) - -### Testing - -- Cover IPv6 service status URL formatting - ([#1295](https://github.com/appium/python-client/pull/1295), - [`a47ddd0`](https://github.com/appium/python-client/commit/a47ddd06938de8cce586727ddcbc8d4c27f8c802)) - -- Cover scoped and invalid IPv6 service hosts - ([#1295](https://github.com/appium/python-client/pull/1295), - [`a47ddd0`](https://github.com/appium/python-client/commit/a47ddd06938de8cce586727ddcbc8d4c27f8c802)) - - -## v6.0.2 (2026-09-08) - -### Bug Fixes - -- Parse inline Appium service arguments ([#1294](https://github.com/appium/python-client/pull/1294), - [`2f3c83f`](https://github.com/appium/python-client/commit/2f3c83f1f717baa6455908338acf12666a9cd476)) - -### Chores - -- Check up to 5 sec ([#1297](https://github.com/appium/python-client/pull/1297), - [`d931a8c`](https://github.com/appium/python-client/commit/d931a8c63a98e16bbb00577f32618e899f4930a8)) - -### Testing - -- Cover inline service argument status URLs - ([#1294](https://github.com/appium/python-client/pull/1294), - [`2f3c83f`](https://github.com/appium/python-client/commit/2f3c83f1f717baa6455908338acf12666a9cd476)) - - -## v6.0.1 (2026-09-08) - -### Bug Fixes - -- Use seconds for service listening polling timeout - ([#1293](https://github.com/appium/python-client/pull/1293), - [`c440732`](https://github.com/appium/python-client/commit/c4407328be30d84e1587e5868d6cddf323ee657a)) - -### Chores - -- Add PERF, FURB, SIM ruff rules ([#1275](https://github.com/appium/python-client/pull/1275), - [`c36240e`](https://github.com/appium/python-client/commit/c36240eeaa69e61c56d617fc98c90dc213deb2e3)) - -- Add UP ruff rules and delete old typings - ([#1278](https://github.com/appium/python-client/pull/1278), - [`9622c1e`](https://github.com/appium/python-client/commit/9622c1e2fb92e67a7983b1b5bb96f674201bbb47)) - -- Up minimum selenium_version and doc it - ([#1273](https://github.com/appium/python-client/pull/1273), - [`a7f909c`](https://github.com/appium/python-client/commit/a7f909ca26872cb635ef33b9a0f36e6466d233b4)) - -- **deps**: Bump gitpython from 3.1.57 to 3.1.58 - ([#1271](https://github.com/appium/python-client/pull/1271), - [`7c11ab0`](https://github.com/appium/python-client/commit/7c11ab08e0bb4eada90d34d067cd3b3a90caa6e1)) - -### Continuous Integration - -- Enable auto release again - ([`4f7e38d`](https://github.com/appium/python-client/commit/4f7e38d217a88b225205e5a21580556f079caf1b)) - -- Fix url for the flutter test app ([#1281](https://github.com/appium/python-client/pull/1281), - [`2d5edbe`](https://github.com/appium/python-client/commit/2d5edbe3dba1816d109f882257db327e6bb47b7b)) - -- **deps**: Bump actions/setup-java from 5.6.0 to 5.7.0 - ([#1272](https://github.com/appium/python-client/pull/1272), - [`534fe7d`](https://github.com/appium/python-client/commit/534fe7df33cf9b3716becaaa93c420ab5498168b)) - -- **deps**: Bump actions/setup-java from 5.7.0 to 6.0.0 - ([#1285](https://github.com/appium/python-client/pull/1285), - [`54251be`](https://github.com/appium/python-client/commit/54251be816c7e0a306581a94db04f7b1ff24b1b0)) - -- **deps**: Bump selenium from 4.46.0 to 4.47.0 - ([#1277](https://github.com/appium/python-client/pull/1277), - [`a6dd793`](https://github.com/appium/python-client/commit/a6dd7936a916017bead677e8c6cb7008a67dcacb)) - -- **deps**: Bump selenium from 4.47.0 to 4.48.0 - ([#1287](https://github.com/appium/python-client/pull/1287), - [`cdaf108`](https://github.com/appium/python-client/commit/cdaf10881fa2786f70ef0dee05b30fd301a7814f)) - -- **deps-dev**: Bump mypy from 2.3.0 to 2.3.1 - ([#1280](https://github.com/appium/python-client/pull/1280), - [`41b6158`](https://github.com/appium/python-client/commit/41b6158f36a9c9eea5fcfaead1e1bb091a747426)) - -- **deps-dev**: Bump python-semantic-release from 10.6.1 to 10.6.2 - ([#1288](https://github.com/appium/python-client/pull/1288), - [`6d775af`](https://github.com/appium/python-client/commit/6d775af39a06ca7b3755b50f3c840926e70c80e8)) - -- **deps-dev**: Bump ruff from 0.16.1 to 0.16.2 - ([#1276](https://github.com/appium/python-client/pull/1276), - [`abdb347`](https://github.com/appium/python-client/commit/abdb347cbbab4d63ccd1aa96f6541de7375b2058)) - -- **deps-dev**: Bump ruff from 0.16.2 to 0.16.3 - ([#1279](https://github.com/appium/python-client/pull/1279), - [`4cd8822`](https://github.com/appium/python-client/commit/4cd882294a4c1e077f04f572beebf6bbcdd32a66)) - -- **deps-dev**: Bump ruff from 0.16.3 to 0.16.4 - ([#1284](https://github.com/appium/python-client/pull/1284), - [`8f32c8b`](https://github.com/appium/python-client/commit/8f32c8b21a659010b7f82cdffc52bf90ec325edc)) - -- **deps-dev**: Bump ruff from 0.16.4 to 0.16.5 - ([#1286](https://github.com/appium/python-client/pull/1286), - [`bca93b5`](https://github.com/appium/python-client/commit/bca93b59222089bba79ff137c6e22782eba04a21)) - -- **deps-dev**: Bump types-python-dateutil - ([#1274](https://github.com/appium/python-client/pull/1274), - [`a974755`](https://github.com/appium/python-client/commit/a974755b56e2576bd606eefb31189a7c2ad7620b)) - -### Documentation - -- Add breaking change note in the changelog - ([`5096f41`](https://github.com/appium/python-client/commit/5096f4103bee0aba8a321c084d23626ac8107d63)) - -- Add usage examples for screen recording methods - ([#1290](https://github.com/appium/python-client/pull/1290), - [`0fe6d91`](https://github.com/appium/python-client/commit/0fe6d914bc17d7a383347a96b3948d1291b57fcb)) - -### Testing - -- Cover the service listening polling budget - ([#1293](https://github.com/appium/python-client/pull/1293), - [`c440732`](https://github.com/appium/python-client/commit/c4407328be30d84e1587e5868d6cddf323ee657a)) - - -## v6.0.0 (2026-08-08) - -### Breaking Changes - -- Removed legacy Appium endpoint calls from commands which can be replaced with extensions - - This should not affect anything for users who use newer Appium such as Appium v2/3 based drivers - - References: [#1258](https://github.com/appium/python-client/pull/1258), [#1233](https://github.com/appium/python-client/pull/1233), [#1258](https://github.com/appium/python-client/pull/1258), [#1224](https://github.com/appium/python-client/pull/1224), [#1220](https://github.com/appium/python-client/pull/1220), [#1225](https://github.com/appium/python-client/pull/1225), [#1267](https://github.com/appium/python-client/pull/1267) -- Removed CanRememberExtensionPresence used only for the legacy endpoint support [#1269](https://github.com/appium/python-client/pull/1269) - -### Bug Fixes - -- Return int bitmask from set_network_connection - ([#1258](https://github.com/appium/python-client/pull/1258), - [`9097a5a`](https://github.com/appium/python-client/commit/9097a5ab864140b5374d800cf9e60036a1e63a68)) - -### Chores - -- Add a blank line before version v5.2.7 in CHANGELOG.md - ([`dc47ec4`](https://github.com/appium/python-client/commit/dc47ec414f4ae0ffadfdeb02147458f2fe90260a)) - -- Remove setup.py ([#1218](https://github.com/appium/python-client/pull/1218), - [`0f549c3`](https://github.com/appium/python-client/commit/0f549c3d2addbf0c8cc2853731de6e17a85e522f)) - -- Unify ruff version in pyproject.toml and pre-commit-config.yaml - ([#1241](https://github.com/appium/python-client/pull/1241), - [`16a3429`](https://github.com/appium/python-client/commit/16a34294af3fad32fc2bd5578b6331a6bf918f92)) - -- **deps**: Bump gitpython from 3.1.46 to 3.1.47 - ([#1222](https://github.com/appium/python-client/pull/1222), - [`a712427`](https://github.com/appium/python-client/commit/a71242723b1847c53c8d99fc477afa85bff35df4)) - -- **deps**: Bump gitpython from 3.1.54 to 3.1.57 - ([#1270](https://github.com/appium/python-client/pull/1270), - [`ad193d1`](https://github.com/appium/python-client/commit/ad193d168f151d16ca74dc1a0a028138d15b58b0)) - -- **deps**: Bump idna from 3.11 to 3.15 ([#1229](https://github.com/appium/python-client/pull/1229), - [`f268e84`](https://github.com/appium/python-client/commit/f268e84784289c7490a68b4ccd4dbe019e652930)) - -- **deps**: Bump pygments from 2.19.2 to 2.20.0 - ([#1213](https://github.com/appium/python-client/pull/1213), - [`064aeaf`](https://github.com/appium/python-client/commit/064aeaf03b1f287debfdf744c9e6ecf3a07a01c1)) - -- **deps-dev**: Bump ruff from 0.15.11 to 0.15.12 - ([#1223](https://github.com/appium/python-client/pull/1223), - [`15b94bd`](https://github.com/appium/python-client/commit/15b94bd450af92b1abb30850752150edaab52eab)) - -- **deps-dev**: Bump ruff from 0.15.12 to 0.15.13 - ([#1228](https://github.com/appium/python-client/pull/1228), - [`afbface`](https://github.com/appium/python-client/commit/afbface1ea1494d7f0a4bbb6330926b70bd8b216)) - -- **deps-dev**: Bump ruff from 0.15.13 to 0.15.14 - ([#1231](https://github.com/appium/python-client/pull/1231), - [`e374b78`](https://github.com/appium/python-client/commit/e374b78068b6165b68e80c6e0499dff2a2e37da3)) - -- **deps-dev**: Bump ruff from 0.15.14 to 0.15.15 - ([#1232](https://github.com/appium/python-client/pull/1232), - [`2a05afc`](https://github.com/appium/python-client/commit/2a05afce980e5cf4465f9d8705669780962f2469)) - -- **deps-dev**: Bump ruff from 0.15.15 to 0.15.16 - ([#1234](https://github.com/appium/python-client/pull/1234), - [`5389384`](https://github.com/appium/python-client/commit/53893843f75fc66edfe3c71555d1fe3bc9a60249)) - -- **deps-dev**: Bump ruff from 0.15.16 to 0.15.17 - ([#1235](https://github.com/appium/python-client/pull/1235), - [`00325db`](https://github.com/appium/python-client/commit/00325dbcfa29620a818929e69f764ce6fc16cb37)) - -- **deps-dev**: Bump ruff from 0.16.0 to 0.16.1 - ([#1268](https://github.com/appium/python-client/pull/1268), - [`a65dc2b`](https://github.com/appium/python-client/commit/a65dc2b5372dab3cce8ac7db87f9eda45a0342b8)) - -### Continuous Integration - -- Disable auto release to release breaking changes in one release - ([`9034ad6`](https://github.com/appium/python-client/commit/9034ad65517b2659f4df1c8aa82eaa2e848a4814)) - -- **deps**: Bump actions/cache from 5 to 6 - ([#1240](https://github.com/appium/python-client/pull/1240), - [`e4e7a79`](https://github.com/appium/python-client/commit/e4e7a798e281dae431f91cd4f21a682fbed788d8)) - -- **deps**: Bump actions/checkout from 6 to 7 - ([#1237](https://github.com/appium/python-client/pull/1237), - [`c3c844f`](https://github.com/appium/python-client/commit/c3c844f01ace39089be6559c1f84c3fa259fe074)) - -- **deps**: Bump actions/setup-java from 5 to 5.6.0 - ([#1265](https://github.com/appium/python-client/pull/1265), - [`e3cf4f5`](https://github.com/appium/python-client/commit/e3cf4f507976817a23dfc54aaa06cef722e92543)) - -- **deps**: Bump actions/setup-node from 6 to 7 - ([#1250](https://github.com/appium/python-client/pull/1250), - [`48201f7`](https://github.com/appium/python-client/commit/48201f753c80cab0e79bcf7fd64cc2f7d77535ca)) - -- **deps**: Bump actions/setup-python from 6 to 7 - ([#1264](https://github.com/appium/python-client/pull/1264), - [`247bfba`](https://github.com/appium/python-client/commit/247bfbaf39106c79fb347861a354283129767873)) - -- **deps**: Bump gitpython from 3.1.47 to 3.1.54 - ([#1262](https://github.com/appium/python-client/pull/1262), - [`3b0ae42`](https://github.com/appium/python-client/commit/3b0ae42c85930df775c23808fae47cbf8671ce02)) - -- **deps**: Bump requests from 2.32.5 to 2.33.0 - ([#1266](https://github.com/appium/python-client/pull/1266), - [`f119477`](https://github.com/appium/python-client/commit/f119477ec64323bec2d32e107a25bac7f4734c70)) - -- **deps**: Bump selenium from 4.40.0 to 4.46.0 - ([#1251](https://github.com/appium/python-client/pull/1251), - [`37b5118`](https://github.com/appium/python-client/commit/37b51180de2f49deb4dc160b5e3f36879e441d97)) - -- **deps**: Bump setuptools from 80.10.1 to 83.0.0 - ([#1263](https://github.com/appium/python-client/pull/1263), - [`f92311d`](https://github.com/appium/python-client/commit/f92311d26df3b14fcff5cd0ed18d41b5e468d7df)) - -- **deps**: Bump typing-extensions from 4.15.0 to 4.16.0 - ([#1242](https://github.com/appium/python-client/pull/1242), - [`ceb843a`](https://github.com/appium/python-client/commit/ceb843af03ce0158179252d7ed97d8c83d3924a0)) - -- **deps**: Bump urllib3 from 2.6.3 to 2.7.0 - ([#1257](https://github.com/appium/python-client/pull/1257), - [`e155a73`](https://github.com/appium/python-client/commit/e155a736982e9890471b04f2ce8fe016732017d6)) - -- **deps-dev**: Bump mypy from 1.19.1 to 2.3.0 - ([#1253](https://github.com/appium/python-client/pull/1253), - [`21f73eb`](https://github.com/appium/python-client/commit/21f73ebbb3f0ff6ae3145c58ccae1433a2a1c03a)) - -- **deps-dev**: Bump pre-commit from 4.5.1 to 4.6.0 - ([#1252](https://github.com/appium/python-client/pull/1252), - [`8480f9c`](https://github.com/appium/python-client/commit/8480f9c817605d16202f223230f8ff9e3c643ce7)) - -- **deps-dev**: Bump pre-commit from 4.6.0 to 4.6.1 - ([#1259](https://github.com/appium/python-client/pull/1259), - [`3439428`](https://github.com/appium/python-client/commit/34394283e9befc559e63fe470a20f361e975e576)) - -- **deps-dev**: Bump pytest from 8.4.2 to 9.1.1 - ([#1255](https://github.com/appium/python-client/pull/1255), - [`9e76329`](https://github.com/appium/python-client/commit/9e76329cd0a4d2924512449c8c6210239a64b87a)) - -- **deps-dev**: Bump python-semantic-release from 10.5.3 to 10.6.1 - ([#1244](https://github.com/appium/python-client/pull/1244), - [`0c14656`](https://github.com/appium/python-client/commit/0c146565685436bd32ba729012bc650f87b09f71)) - -- **deps-dev**: Bump ruff from 0.15.17 to 0.15.19 - ([#1238](https://github.com/appium/python-client/pull/1238), - [`b989a00`](https://github.com/appium/python-client/commit/b989a003a8dc8f0e51c0e3240c316fac87ffcc9a)) - -- **deps-dev**: Bump ruff from 0.15.19 to 0.15.20 - ([#1239](https://github.com/appium/python-client/pull/1239), - [`ef6b6f2`](https://github.com/appium/python-client/commit/ef6b6f2824ebe095ff3b50ae1e9613be90eabdb4)) - -- **deps-dev**: Bump ruff from 0.15.20 to 0.15.21 - ([#1245](https://github.com/appium/python-client/pull/1245), - [`bafdbf7`](https://github.com/appium/python-client/commit/bafdbf72ddfba0fc2d69841938462297b4b26bff)) - -- **deps-dev**: Bump ruff from 0.15.21 to 0.15.22 - ([#1249](https://github.com/appium/python-client/pull/1249), - [`136b1e4`](https://github.com/appium/python-client/commit/136b1e46abf1ee7c57d4ab29113e20424549cc7c)) - -- **deps-dev**: Bump ruff from 0.15.22 to 0.16.0 - ([#1261](https://github.com/appium/python-client/pull/1261), - [`7c741e6`](https://github.com/appium/python-client/commit/7c741e68a05dad0ee4a2d7adf932aee2a00b0fa7)) - -- **deps-dev**: Bump types-python-dateutil - ([#1254](https://github.com/appium/python-client/pull/1254), - [`c7c1240`](https://github.com/appium/python-client/commit/c7c1240a38594508e2814217626a70ce379e8df0)) - -### Documentation - -- Drop appiumpro links as the top domain no longer maintained - ([`34111e0`](https://github.com/appium/python-client/commit/34111e02d6fa1ce5466335363f490ff671e876d5)) - -- Move http -> https in Homepage url ([#1256](https://github.com/appium/python-client/pull/1256), - [`f1d0f1c`](https://github.com/appium/python-client/commit/f1d0f1c6aee4e4257e6f2b1d9c6af6e903c2a881)) - -### Features - -- Drop CanRememberExtensionPresence ([#1269](https://github.com/appium/python-client/pull/1269), - [`e33851f`](https://github.com/appium/python-client/commit/e33851fcfae8f3aa96b1bd4a067ca5cf8a6d9c7c)) - -### Refactoring - -- Network extension to remove legacy command fallbacks - ([#1258](https://github.com/appium/python-client/pull/1258), - [`9097a5a`](https://github.com/appium/python-client/commit/9097a5ab864140b5374d800cf9e60036a1e63a68)) - -- Remove legacy command fallbacks in Applications extension - ([#1233](https://github.com/appium/python-client/pull/1233), - [`4707ff7`](https://github.com/appium/python-client/commit/4707ff7a9a362b4ff39b85a600432d8cfb3bc4a5)) - -- Remove legacy fallback for `mobile: getAppStrings` - ([`dc47ec4`](https://github.com/appium/python-client/commit/dc47ec414f4ae0ffadfdeb02147458f2fe90260a)) - -- Remove legacy fallback for network commands - ([#1258](https://github.com/appium/python-client/pull/1258), - [`9097a5a`](https://github.com/appium/python-client/commit/9097a5ab864140b5374d800cf9e60036a1e63a68)) - -- Remove legacy fallback in toggle_location_services - ([#1224](https://github.com/appium/python-client/pull/1224), - [`ed9eaff`](https://github.com/appium/python-client/commit/ed9eaff74dba57f9c700674bfde35bdca4096529)) - -- Remove legacy fallback logic for android extensions - ([#1220](https://github.com/appium/python-client/pull/1220), - [`23278d9`](https://github.com/appium/python-client/commit/23278d9571a5c301dfa607d6dd91172fb18e9b02)) - -- Remove legacy gsm fallback logic ([#1225](https://github.com/appium/python-client/pull/1225), - [`3b91340`](https://github.com/appium/python-client/commit/3b91340f0080003c82dbf22ec6e4de4c14594bdf)) - -- Remove legacy implementation ([#1267](https://github.com/appium/python-client/pull/1267), - [`5b7f352`](https://github.com/appium/python-client/commit/5b7f3527f7dcb2886c3af6ba5fa36d471b6aeb5b)) - -### Testing - -- Remove (object) inheritance to align with pytest style (#644) - ([#1246](https://github.com/appium/python-client/pull/1246), - [`d9b5cc9`](https://github.com/appium/python-client/commit/d9b5cc98c11ec188bf4578c65ee298462dfce8c5)) - - ## v5.3.1 (2026-04-19) ### Bug Fixes diff --git a/README.md b/README.md index 3b98d3b7c..3268cb54a 100644 --- a/README.md +++ b/README.md @@ -39,18 +39,17 @@ download and unarchive the source tarball (Appium-Python-Client-X.X.tar.gz). ## Compatibility Matrix -| Appium Python Client | Selenium binding | Python version | -|----------------------|---------------------|----------------| -| `6.0.0`+ | `4.37.0`+ | 3.10+ | -| `5.1.1` - `5.3.1` | `4.26.0` - `4.46.0` | 3.9+ | -| `4.5.0` - `5.1.0` | `4.26.0` - `4.31.0` | 3.9+ | -| `4.3.0` - `4.4.0` | `4.26.0` - `4.31.0` | 3.8+ | -| `3.0.0` - `4.2.1` | `4.12.0` - `4.25.0` | 3.8+ | -| `2.10.0` - `2.11.1` | `4.1.0` - `4.11.2` | 3.7+ | -| `2.2.0` - `2.9.0` | `4.1.0` - `4.9.0` | 3.7+ | -| `2.0.0` - `2.1.4` | `4.0.0` | 3.7+ | -| `1.0.0` - `1.3.0` | `3.x` | 3.7+ | -| `0.52` and below | `3.x` | 2.7, 3.4 - 3.7 | +|Appium Python Client| Selenium binding| Python version | +|----|----|----| +|`5.1.1`+|`4.26.0`+ | 3.9+ | +|`4.5.0` - `5.1.0`|`4.26.0` - `4.31.0` | 3.9+ | +|`4.3.0` - `4.4.0`|`4.26.0` - `4.31.0` | 3.8+ | +|`3.0.0` - `4.2.1` |`4.12.0` - `4.25.0` | 3.8+ | +|`2.10.0` - `2.11.1` |`4.1.0` - `4.11.2` | 3.7+ | +|`2.2.0` - `2.9.0` |`4.1.0` - `4.9.0` | 3.7+ | +|`2.0.0` - `2.1.4` |`4.0.0` | 3.7+ | +|`1.0.0` - `1.3.0` |`3.x`| 3.7+ | +|`0.52` and below|`3.x`| 2.7, 3.4 - 3.7 | The Appium Python Client depends on [Selenium Python binding](https://pypi.org/project/selenium/), thus the Selenium Python binding update might affect the Appium Python Client behavior. @@ -72,19 +71,21 @@ For example, some changes in the Selenium binding could break the Appium client. options=UiAutomator2Options().load_capabilities(desired_caps), direct_connection=True, keep_alive=False, - strict_ssl=False, + strict_ssl=False ) # after from appium.webdriver.client_config import AppiumClientConfig - client_config = AppiumClientConfig( remote_server_addr=SERVER_URL_BASE, direct_connection=True, keep_alive=False, ignore_certificates=True, ) - driver = webdriver.Remote(options=UiAutomator2Options().load_capabilities(desired_caps), client_config=client_config) + driver = webdriver.Remote( + options=UiAutomator2Options().load_capabilities(desired_caps), + client_config=client_config + ) ``` - Note that you can keep using `webdriver.Remote(url, options=options, client_config=client_config)` format as well. In such case the `remote_server_addr` argument of `AppiumClientConfig` constructor would have priority over the `url` argument of `webdriver.Remote` constructor. @@ -137,7 +138,7 @@ from selenium.webdriver.common.actions.pointer_input import PointerInput actions = ActionChains(driver) # override as 'touch' pointer action -actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, 'touch')) +actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch")) actions.w3c_actions.pointer_action.move_to_location(start_x, start_y) actions.w3c_actions.pointer_action.pointer_down() actions.w3c_actions.pointer_action.pause(2) @@ -172,7 +173,6 @@ environment: import pytest from appium import webdriver - # Options are only available since client version 2.3.0 # If you use an older client then switch to desired_capabilities # instead: https://github.com/appium/python-client/pull/720 @@ -200,7 +200,7 @@ def appium_service(): service.stop() -def create_ios_driver(custom_opts=None): +def create_ios_driver(custom_opts = None): options = XCUITestOptions() options.platformVersion = '13.4' options.udid = '123456789ABC' @@ -210,7 +210,7 @@ def create_ios_driver(custom_opts=None): return webdriver.Remote(f'http://{APPIUM_HOST}:{APPIUM_PORT}', options=options) -def create_android_driver(custom_opts=None): +def create_android_driver(custom_opts = None): options = UiAutomator2Options() options.platformVersion = '10' options.udid = '123456789ABC' @@ -249,7 +249,9 @@ def android_driver(): def test_ios_click(appium_service, ios_driver_factory): # Usage of the context manager ensures the driver session is closed properly # after the test completes. Otherwise, make sure to call `driver.quit()` on teardown. - with ios_driver_factory({'appium:app': '/path/to/app/UICatalog.app.zip'}) as driver: + with ios_driver_factory({ + 'appium:app': '/path/to/app/UICatalog.app.zip' + }) as driver: el = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='item') el.click() @@ -257,12 +259,10 @@ def test_ios_click(appium_service, ios_driver_factory): def test_android_click(appium_service, android_driver_factory): # Usage of the context manager ensures the driver session is closed properly # after the test completes. Otherwise, make sure to call `driver.quit()` on teardown. - with android_driver_factory( - { - 'appium:app': '/path/to/app/test-app.apk', - 'appium:udid': '567890', - } - ) as driver: + with android_driver_factory({ + 'appium:app': '/path/to/app/test-app.apk', + 'appium:udid': '567890', + }) as driver: el = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='item') el.click() ``` @@ -300,7 +300,6 @@ Then python client will switch its endpoint to the one specified by the values o ```python from appium import webdriver - # Options are only available since client version 2.3.0 # If you use an older client then switch to desired_capabilities # instead: https://github.com/appium/python-client/pull/720 @@ -309,35 +308,31 @@ from appium.webdriver.client_config import AppiumClientConfig # load_capabilities API could be used to # load options mapping stored in a dictionary -options = XCUITestOptions().load_capabilities( - { - 'platformVersion': '13.4', - 'deviceName': 'iPhone Simulator', - 'app': '/full/path/to/app/UICatalog.app.zip', - } +options = XCUITestOptions().load_capabilities({ + 'platformVersion': '13.4', + 'deviceName': 'iPhone Simulator', + 'app': '/full/path/to/app/UICatalog.app.zip', +}) + +client_config = AppiumClientConfig( + remote_server_addr='http://127.0.0.1:4723', + direct_connection=True ) -client_config = AppiumClientConfig(remote_server_addr='http://127.0.0.1:4723', direct_connection=True) - driver = webdriver.Remote( # Appium1 points to http://127.0.0.1:4723/wd/hub by default 'http://127.0.0.1:4723', options=options, - client_config=client_config, + client_config=client_config ) ``` -Only the endpoint the client talks to is replaced. The rest of the given `client_config`, -such as `timeout`, `init_args_for_pool_manager`, proxy, certificates and authentication -settings, keeps being applied to the requests sent to the direct connect endpoint. - ## Relax SSL validation `strict_ssl` option allows you to send commands to an invalid certificate host like a self-signed one. ```python from appium import webdriver - # Options are only available since client version 2.3.0 # If you use an older client then switch to desired_capabilities # instead: https://github.com/appium/python-client/pull/720 @@ -362,7 +357,10 @@ from appium import webdriver from selenium.webdriver.remote.client_config import ClientConfig -client_config = ClientConfig(remote_server_addr='http://127.0.0.1:4723', ignore_certificates=True) +client_config = ClientConfig( + remote_server_addr='http://127.0.0.1:4723', + ignore_certificates=True +) driver = webdriver.Remote(client_config.remote_server_addr, options=options, client_config=client_config) ``` @@ -380,9 +378,12 @@ import urllib3 from appium.webdriver.appium_connection import AppiumConnection # Retry connection error up to 3 times. -init_args_for_pool_manage = {'retries': urllib3.util.retry.Retry(total=3, connect=3, read=False)} +init_args_for_pool_manage = { + 'retries': urllib3.util.retry.Retry(total=3, connect=3, read=False) +} appium_executor = AppiumConnection( - remote_server_addr='http://127.0.0.1:4723', init_args_for_pool_manage=init_args_for_pool_manage + remote_server_addr='http://127.0.0.1:4723', + init_args_for_pool_manage=init_args_for_pool_manage ) options = XCUITestOptions() @@ -401,22 +402,19 @@ from appium.options.ios import XCUITestOptions from appium.webdriver.appium_connection import AppiumConnection - class CustomAppiumConnection(AppiumConnection): # Can add your own methods for the custom class pass - custom_executor = CustomAppiumConnection(remote_server_addr='http://127.0.0.1:4723') -options = XCUITestOptions().load_capabilities( - { - 'platformVersion': '13.4', - 'deviceName': 'iPhone Simulator', - 'app': '/full/path/to/app/UICatalog.app.zip', - } -) +options = XCUITestOptions().load_capabilities({ + 'platformVersion': '13.4', + 'deviceName': 'iPhone Simulator', + 'app': '/full/path/to/app/UICatalog.app.zip', +}) driver = webdriver.Remote(custom_executor, options=options) + ``` The `AppiumConnection` can set `selenium.webdriver.remote.client_config.ClientConfig` as well. diff --git a/appium/common/helper.py b/appium/common/helper.py index 821b7c9f8..1565b96e9 100644 --- a/appium/common/helper.py +++ b/appium/common/helper.py @@ -13,12 +13,12 @@ # limitations under the License. import base64 -from typing import Any +from typing import Any, Dict from appium import version as appium_version -def extract_const_attributes(cls: type) -> dict[str, Any]: +def extract_const_attributes(cls: type) -> Dict[str, Any]: """Return dict with constants attributes and values in the class(e.g. {'VAL1': 1, 'VAL2': 2}) Args: diff --git a/appium/options/android/common/adb/adb_exec_timeout_option.py b/appium/options/android/common/adb/adb_exec_timeout_option.py index f89b140a6..f0ea5b802 100644 --- a/appium/options/android/common/adb/adb_exec_timeout_option.py +++ b/appium/options/android/common/adb/adb_exec_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class AdbExecTimeoutOption(SupportsCapabilities): @property - def adb_exec_timeout(self) -> timedelta | None: + def adb_exec_timeout(self) -> Optional[timedelta]: """ Maximum time to wait until single ADB command is executed. """ @@ -32,7 +33,7 @@ def adb_exec_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @adb_exec_timeout.setter - def adb_exec_timeout(self, value: timedelta | int) -> None: + def adb_exec_timeout(self, value: Union[timedelta, int]) -> None: """ Maximum time to wait until single ADB command is executed. 20000 ms by default. diff --git a/appium/options/android/common/adb/adb_port_option.py b/appium/options/android/common/adb/adb_port_option.py index 515d0ae6a..4685c7a0f 100644 --- a/appium/options/android/common/adb/adb_port_option.py +++ b/appium/options/android/common/adb/adb_port_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AdbPortOption(SupportsCapabilities): @property - def adb_port(self) -> int | None: + def adb_port(self) -> Optional[int]: """ Number of the port where ADB is running. """ diff --git a/appium/options/android/common/adb/allow_delay_adb_option.py b/appium/options/android/common/adb/allow_delay_adb_option.py index 578552699..cafd6ff3c 100644 --- a/appium/options/android/common/adb/allow_delay_adb_option.py +++ b/appium/options/android/common/adb/allow_delay_adb_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AllowDelayAdbOption(SupportsCapabilities): @property - def allow_delay_adb(self) -> bool | None: + def allow_delay_adb(self) -> Optional[bool]: """ Whether to prevent the emulator to use -delay-adb feature. """ diff --git a/appium/options/android/common/adb/build_tools_version_option.py b/appium/options/android/common/adb/build_tools_version_option.py index 6c1172eac..a5cd96c71 100644 --- a/appium/options/android/common/adb/build_tools_version_option.py +++ b/appium/options/android/common/adb/build_tools_version_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class BuildToolsVersionOption(SupportsCapabilities): @property - def build_tools_version(self) -> str | None: + def build_tools_version(self) -> Optional[str]: """ Version of Android build tools to use. """ diff --git a/appium/options/android/common/adb/clear_device_logs_on_start_option.py b/appium/options/android/common/adb/clear_device_logs_on_start_option.py index 42212dc6a..050cc6a7d 100644 --- a/appium/options/android/common/adb/clear_device_logs_on_start_option.py +++ b/appium/options/android/common/adb/clear_device_logs_on_start_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ClearDeviceLogsOnStartOption(SupportsCapabilities): @property - def clear_device_logs_on_start(self) -> bool | None: + def clear_device_logs_on_start(self) -> Optional[bool]: """ Makes the driver to delete all the existing logs in the device buffer before starting a new test. diff --git a/appium/options/android/common/adb/ignore_hidden_api_policy_error_option.py b/appium/options/android/common/adb/ignore_hidden_api_policy_error_option.py index 6f77685a7..4813ed246 100644 --- a/appium/options/android/common/adb/ignore_hidden_api_policy_error_option.py +++ b/appium/options/android/common/adb/ignore_hidden_api_policy_error_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class IgnoreHiddenApiPolicyErrorOption(SupportsCapabilities): @property - def ignore_hidden_api_policy_error(self) -> bool | None: + def ignore_hidden_api_policy_error(self) -> Optional[bool]: """ Whether to ignore a failure while changing hidden API access policies. """ diff --git a/appium/options/android/common/adb/logcat_filter_specs_option.py b/appium/options/android/common/adb/logcat_filter_specs_option.py index 16b9dd97c..8a464d350 100644 --- a/appium/options/android/common/adb/logcat_filter_specs_option.py +++ b/appium/options/android/common/adb/logcat_filter_specs_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class LogcatFilterSpecsOption(SupportsCapabilities): @property - def logcat_filter_specs(self) -> str | None: + def logcat_filter_specs(self) -> Optional[str]: """ Logcat filter format. """ diff --git a/appium/options/android/common/adb/logcat_format_option.py b/appium/options/android/common/adb/logcat_format_option.py index 74ba8fcfe..363a6f5a7 100644 --- a/appium/options/android/common/adb/logcat_format_option.py +++ b/appium/options/android/common/adb/logcat_format_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class LogcatFormatOption(SupportsCapabilities): @property - def logcat_format(self) -> str | None: + def logcat_format(self) -> Optional[str]: """ Log print format. """ diff --git a/appium/options/android/common/adb/mock_location_app_option.py b/appium/options/android/common/adb/mock_location_app_option.py index aa919ade5..d263ea0e6 100644 --- a/appium/options/android/common/adb/mock_location_app_option.py +++ b/appium/options/android/common/adb/mock_location_app_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class MockLocationAppOption(SupportsCapabilities): @property - def mock_location_app(self) -> str | None: + def mock_location_app(self) -> Optional[str]: """ Identifier of the app, which is used as a system mock location provider. """ diff --git a/appium/options/android/common/adb/remote_adb_host_option.py b/appium/options/android/common/adb/remote_adb_host_option.py index 3a7940565..1656e9bd5 100644 --- a/appium/options/android/common/adb/remote_adb_host_option.py +++ b/appium/options/android/common/adb/remote_adb_host_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class RemoteAdbHostOption(SupportsCapabilities): @property - def remote_adb_host(self) -> str | None: + def remote_adb_host(self) -> Optional[str]: """ Address of the host where ADB is running. """ diff --git a/appium/options/android/common/adb/skip_logcat_capture_option.py b/appium/options/android/common/adb/skip_logcat_capture_option.py index a6998ca11..2f9f9074f 100644 --- a/appium/options/android/common/adb/skip_logcat_capture_option.py +++ b/appium/options/android/common/adb/skip_logcat_capture_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SkipLogcatCaptureOption(SupportsCapabilities): @property - def skip_logcat_capture(self) -> bool | None: + def skip_logcat_capture(self) -> Optional[bool]: """ Whether to delete all the existing logs in the device buffer before starting a new test. diff --git a/appium/options/android/common/adb/suppress_kill_server_option.py b/appium/options/android/common/adb/suppress_kill_server_option.py index ed54e99b4..fb7738994 100644 --- a/appium/options/android/common/adb/suppress_kill_server_option.py +++ b/appium/options/android/common/adb/suppress_kill_server_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SuppressKillServerOption(SupportsCapabilities): @property - def suppress_kill_server(self) -> bool | None: + def suppress_kill_server(self) -> Optional[bool]: """ Prevents the driver from ever killing the ADB server explicitly. """ diff --git a/appium/options/android/common/app/allow_test_packages_option.py b/appium/options/android/common/app/allow_test_packages_option.py index ea66db61f..b09136932 100644 --- a/appium/options/android/common/app/allow_test_packages_option.py +++ b/appium/options/android/common/app/allow_test_packages_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AllowTestPackagesOption(SupportsCapabilities): @property - def allow_test_packages(self) -> bool | None: + def allow_test_packages(self) -> Optional[bool]: """ Whether it is possible to use packages built with the test flag for the automated testing (literally adds -t flag to the adb install command). diff --git a/appium/options/android/common/app/android_install_timeout_option.py b/appium/options/android/common/app/android_install_timeout_option.py index cb53c833a..38a997648 100644 --- a/appium/options/android/common/app/android_install_timeout_option.py +++ b/appium/options/android/common/app/android_install_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class AndroidInstallTimeoutOption(SupportsCapabilities): @property - def android_install_timeout(self) -> timedelta | None: + def android_install_timeout(self) -> Optional[timedelta]: """ Maximum amount of time to wait until the application under test is installed. """ @@ -32,7 +33,7 @@ def android_install_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @android_install_timeout.setter - def android_install_timeout(self, value: timedelta | int) -> None: + def android_install_timeout(self, value: Union[timedelta, int]) -> None: """ Maximum amount of time to wait until the application under test is installed. 90000 ms by default diff --git a/appium/options/android/common/app/app_activity_option.py b/appium/options/android/common/app/app_activity_option.py index 1df540a95..41f66eed8 100644 --- a/appium/options/android/common/app/app_activity_option.py +++ b/appium/options/android/common/app/app_activity_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppActivityOption(SupportsCapabilities): @property - def app_activity(self) -> str | None: + def app_activity(self) -> Optional[str]: """ Name of the main app activity. """ diff --git a/appium/options/android/common/app/app_package_option.py b/appium/options/android/common/app/app_package_option.py index ffcb1ad8d..a8c7b065c 100644 --- a/appium/options/android/common/app/app_package_option.py +++ b/appium/options/android/common/app/app_package_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppPackageOption(SupportsCapabilities): @property - def app_package(self) -> str | None: + def app_package(self) -> Optional[str]: """ App package identifier. """ diff --git a/appium/options/android/common/app/app_wait_activity_option.py b/appium/options/android/common/app/app_wait_activity_option.py index bbf726274..5d72f311d 100644 --- a/appium/options/android/common/app/app_wait_activity_option.py +++ b/appium/options/android/common/app/app_wait_activity_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppWaitActivityOption(SupportsCapabilities): @property - def app_wait_activity(self) -> str | None: + def app_wait_activity(self) -> Optional[str]: """ Name of the app activity to wait for. """ diff --git a/appium/options/android/common/app/app_wait_duration_option.py b/appium/options/android/common/app/app_wait_duration_option.py index 428dd5df3..362cb3725 100644 --- a/appium/options/android/common/app/app_wait_duration_option.py +++ b/appium/options/android/common/app/app_wait_duration_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class AppWaitDurationOption(SupportsCapabilities): @property - def app_wait_duration(self) -> timedelta | None: + def app_wait_duration(self) -> Optional[timedelta]: """ Identifier of the app package to wait for. """ @@ -32,7 +33,7 @@ def app_wait_duration(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @app_wait_duration.setter - def app_wait_duration(self, value: timedelta | int) -> None: + def app_wait_duration(self, value: Union[timedelta, int]) -> None: """ Maximum amount of time to wait until the application under test is started (e.g. an activity returns the control to the caller). 20000 ms by default. diff --git a/appium/options/android/common/app/app_wait_for_launch_option.py b/appium/options/android/common/app/app_wait_for_launch_option.py index 3a34b4fb5..5f5b48c51 100644 --- a/appium/options/android/common/app/app_wait_for_launch_option.py +++ b/appium/options/android/common/app/app_wait_for_launch_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppWaitForLaunchOption(SupportsCapabilities): @property - def app_wait_for_launch(self) -> bool | None: + def app_wait_for_launch(self) -> Optional[bool]: """ Whether to block until the app under test returns the control to the caller after its activity has been started by Activity Manager. diff --git a/appium/options/android/common/app/app_wait_package_option.py b/appium/options/android/common/app/app_wait_package_option.py index 8ba087205..9df4fa0d0 100644 --- a/appium/options/android/common/app/app_wait_package_option.py +++ b/appium/options/android/common/app/app_wait_package_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppWaitPackageOption(SupportsCapabilities): @property - def app_wait_package(self) -> str | None: + def app_wait_package(self) -> Optional[str]: """ Identifier of the app package to wait for. """ diff --git a/appium/options/android/common/app/auto_grant_premissions_option.py b/appium/options/android/common/app/auto_grant_premissions_option.py index 2aa7e459b..e276e7da4 100644 --- a/appium/options/android/common/app/auto_grant_premissions_option.py +++ b/appium/options/android/common/app/auto_grant_premissions_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AutoGrantPermissionsOption(SupportsCapabilities): @property - def auto_grant_permissions(self) -> bool | None: + def auto_grant_permissions(self) -> Optional[bool]: """ Whether to grant all the requested application permissions automatically when a test starts. diff --git a/appium/options/android/common/app/enforce_app_install_option.py b/appium/options/android/common/app/enforce_app_install_option.py index 0056924b6..85d128ed7 100644 --- a/appium/options/android/common/app/enforce_app_install_option.py +++ b/appium/options/android/common/app/enforce_app_install_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class EnforceAppInstallOption(SupportsCapabilities): @property - def enforce_app_install(self) -> bool | None: + def enforce_app_install(self) -> Optional[bool]: """ Whether the application under test is always reinstalled even if a newer version of it already exists on the device under test. diff --git a/appium/options/android/common/app/intent_action_option.py b/appium/options/android/common/app/intent_action_option.py index b44dfe781..8019bcc54 100644 --- a/appium/options/android/common/app/intent_action_option.py +++ b/appium/options/android/common/app/intent_action_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class IntentActionOption(SupportsCapabilities): @property - def intent_action(self) -> str | None: + def intent_action(self) -> Optional[str]: """ Intent action to be applied when starting the given appActivity by Activity Manager. diff --git a/appium/options/android/common/app/intent_category_option.py b/appium/options/android/common/app/intent_category_option.py index b39ef5131..f6f568c20 100644 --- a/appium/options/android/common/app/intent_category_option.py +++ b/appium/options/android/common/app/intent_category_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class IntentCategoryOption(SupportsCapabilities): @property - def intent_category(self) -> str | None: + def intent_category(self) -> Optional[str]: """ Intent category to be applied when starting the given appActivity by Activity Manager. diff --git a/appium/options/android/common/app/intent_flags_option.py b/appium/options/android/common/app/intent_flags_option.py index 4cff2dc87..141e51c30 100644 --- a/appium/options/android/common/app/intent_flags_option.py +++ b/appium/options/android/common/app/intent_flags_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class IntentFlagsOption(SupportsCapabilities): @property - def intent_flags(self) -> str | None: + def intent_flags(self) -> Optional[str]: """ Intent flags to be applied when starting the given appActivity by Activity Manager. diff --git a/appium/options/android/common/app/optional_intent_arguments_option.py b/appium/options/android/common/app/optional_intent_arguments_option.py index 66dcf2448..3411fe96c 100644 --- a/appium/options/android/common/app/optional_intent_arguments_option.py +++ b/appium/options/android/common/app/optional_intent_arguments_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class OptionalIntentArgumentsOption(SupportsCapabilities): @property - def optional_intent_arguments(self) -> str | None: + def optional_intent_arguments(self) -> Optional[str]: """ Intent arguments to be applied when starting the given appActivity by Activity Manager. diff --git a/appium/options/android/common/app/remote_apps_cache_limit_option.py b/appium/options/android/common/app/remote_apps_cache_limit_option.py index a5bd612f5..0f21ec782 100644 --- a/appium/options/android/common/app/remote_apps_cache_limit_option.py +++ b/appium/options/android/common/app/remote_apps_cache_limit_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class RemoteAppsCacheLimitOption(SupportsCapabilities): @property - def remote_apps_cache_limit(self) -> int | None: + def remote_apps_cache_limit(self) -> Optional[int]: """ Maximum amount of apps that could be cached on the remote device. """ diff --git a/appium/options/android/common/app/uninstall_other_packages_option.py b/appium/options/android/common/app/uninstall_other_packages_option.py index 0ec72aa13..2cd05cdf5 100644 --- a/appium/options/android/common/app/uninstall_other_packages_option.py +++ b/appium/options/android/common/app/uninstall_other_packages_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UninstallOtherPackagesOption(SupportsCapabilities): @property - def uninstall_other_packages(self) -> str | None: + def uninstall_other_packages(self) -> Optional[str]: """ Identifiers of packages to be uninstalled from the device before a test starts. """ diff --git a/appium/options/android/common/avd/avd_args_option.py b/appium/options/android/common/avd/avd_args_option.py index 43e3542ef..a8b7b7506 100644 --- a/appium/options/android/common/avd/avd_args_option.py +++ b/appium/options/android/common/avd/avd_args_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AvdArgsOption(SupportsCapabilities): @property - def avd_args(self) -> str | None: + def avd_args(self) -> Optional[str]: """ Emulator command line arguments. """ diff --git a/appium/options/android/common/avd/avd_env_option.py b/appium/options/android/common/avd/avd_env_option.py index 06447da22..a673a95fd 100644 --- a/appium/options/android/common/avd/avd_env_option.py +++ b/appium/options/android/common/avd/avd_env_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,14 +24,14 @@ class AvdEnvOption(SupportsCapabilities): @property - def avd_env(self) -> dict[str, str] | None: + def avd_env(self) -> Optional[Dict[str, str]]: """ Mapping of emulator environment variables. """ return self.get_capability(AVD_ENV) @avd_env.setter - def avd_env(self, value: dict[str, str]) -> None: + def avd_env(self, value: Dict[str, str]) -> None: """ Set the mapping of emulator environment variables. """ diff --git a/appium/options/android/common/avd/avd_launch_timeout_option.py b/appium/options/android/common/avd/avd_launch_timeout_option.py index 94abb5d3a..017396ef0 100644 --- a/appium/options/android/common/avd/avd_launch_timeout_option.py +++ b/appium/options/android/common/avd/avd_launch_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class AvdLaunchTimeoutOption(SupportsCapabilities): @property - def avd_launch_timeout(self) -> timedelta | None: + def avd_launch_timeout(self) -> Optional[timedelta]: """ Timeout to wait until Android Emulator is started. """ @@ -32,7 +33,7 @@ def avd_launch_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @avd_launch_timeout.setter - def avd_launch_timeout(self, value: timedelta | int) -> None: + def avd_launch_timeout(self, value: Union[timedelta, int]) -> None: """ Maximum timeout to wait until Android Emulator is started. 60000 ms by default. diff --git a/appium/options/android/common/avd/avd_option.py b/appium/options/android/common/avd/avd_option.py index e263a2fcc..42c543fc8 100644 --- a/appium/options/android/common/avd/avd_option.py +++ b/appium/options/android/common/avd/avd_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AvdOption(SupportsCapabilities): @property - def avd(self) -> str | None: + def avd(self) -> Optional[str]: """ Name of Android emulator to run the test on. """ diff --git a/appium/options/android/common/avd/avd_ready_timeout_option.py b/appium/options/android/common/avd/avd_ready_timeout_option.py index 7148cda20..c074b3190 100644 --- a/appium/options/android/common/avd/avd_ready_timeout_option.py +++ b/appium/options/android/common/avd/avd_ready_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class AvdReadyTimeoutOption(SupportsCapabilities): @property - def avd_ready_timeout(self) -> timedelta | None: + def avd_ready_timeout(self) -> Optional[timedelta]: """ Timeout to wait until Android Emulator is fully booted and is ready for usage. """ @@ -32,7 +33,7 @@ def avd_ready_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @avd_ready_timeout.setter - def avd_ready_timeout(self, value: timedelta | int) -> None: + def avd_ready_timeout(self, value: Union[timedelta, int]) -> None: """ Maximum timeout to wait until Android Emulator is fully booted and is ready for usage. 60000 ms by default diff --git a/appium/options/android/common/avd/gps_enabled_option.py b/appium/options/android/common/avd/gps_enabled_option.py index 73d0f3cbe..c9b3c60b1 100644 --- a/appium/options/android/common/avd/gps_enabled_option.py +++ b/appium/options/android/common/avd/gps_enabled_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class GpsEnabledOption(SupportsCapabilities): @property - def gps_enabled(self) -> bool | None: + def gps_enabled(self) -> Optional[bool]: """ State of the GPS service on emulator. """ diff --git a/appium/options/android/common/avd/network_speed_option.py b/appium/options/android/common/avd/network_speed_option.py index b748300c4..1ab6c1cba 100644 --- a/appium/options/android/common/avd/network_speed_option.py +++ b/appium/options/android/common/avd/network_speed_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class NetworkSpeedOption(SupportsCapabilities): @property - def network_speed(self) -> str | None: + def network_speed(self) -> Optional[str]: """ Desired network speed limit for the emulator. """ diff --git a/appium/options/android/common/context/auto_webview_timeout_option.py b/appium/options/android/common/context/auto_webview_timeout_option.py index 6dd854ee5..774574313 100644 --- a/appium/options/android/common/context/auto_webview_timeout_option.py +++ b/appium/options/android/common/context/auto_webview_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class AutoWebviewTimeoutOption(SupportsCapabilities): @property - def auto_webview_timeout(self) -> timedelta | None: + def auto_webview_timeout(self) -> Optional[timedelta]: """ Set the maximum timeout to wait until a web view is available if autoWebview capability is set to true. 2000 ms by default. @@ -33,7 +34,7 @@ def auto_webview_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @auto_webview_timeout.setter - def auto_webview_timeout(self, value: timedelta | int) -> None: + def auto_webview_timeout(self, value: Union[timedelta, int]) -> None: """ Timeout to wait until a web view is available. """ diff --git a/appium/options/android/common/context/chrome_logging_prefs_option.py b/appium/options/android/common/context/chrome_logging_prefs_option.py index 80dcf35d3..0c2b69c4d 100644 --- a/appium/options/android/common/context/chrome_logging_prefs_option.py +++ b/appium/options/android/common/context/chrome_logging_prefs_option.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -from typing import Any +from typing import Any, Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,14 +24,14 @@ class ChromeLoggingPrefsOption(SupportsCapabilities): @property - def chrome_logging_prefs(self) -> dict[str, Any] | None: + def chrome_logging_prefs(self) -> Optional[Dict[str, Any]]: """ Chrome logging preferences. """ return self.get_capability(CHROME_LOGGING_PREFS) @chrome_logging_prefs.setter - def chrome_logging_prefs(self, value: dict[str, Any]) -> None: + def chrome_logging_prefs(self, value: Dict[str, Any]) -> None: """ Chrome logging preferences mapping. Basically the same as [goog:loggingPrefs](https://newbedev.com/ diff --git a/appium/options/android/common/context/chrome_options_option.py b/appium/options/android/common/context/chrome_options_option.py index 8cb0200c0..3adb9cebb 100644 --- a/appium/options/android/common/context/chrome_options_option.py +++ b/appium/options/android/common/context/chrome_options_option.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -from typing import Any +from typing import Any, Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,14 +24,14 @@ class ChromeOptionsOption(SupportsCapabilities): @property - def chrome_options(self) -> dict[str, Any] | None: + def chrome_options(self) -> Optional[Dict[str, Any]]: """ Chrome options. """ return self.get_capability(CHROME_OPTIONS) @chrome_options.setter - def chrome_options(self, value: dict[str, Any]) -> None: + def chrome_options(self, value: Dict[str, Any]) -> None: """ A mapping, that allows to customize chromedriver options. See https://chromedriver.chromium.org/capabilities for the list diff --git a/appium/options/android/common/context/chromedriver_args_option.py b/appium/options/android/common/context/chromedriver_args_option.py index 7fa181301..b3633b105 100644 --- a/appium/options/android/common/context/chromedriver_args_option.py +++ b/appium/options/android/common/context/chromedriver_args_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import List, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,14 +24,14 @@ class ChromedriverArgsOption(SupportsCapabilities): @property - def chromedriver_args(self) -> list[str] | None: + def chromedriver_args(self) -> Optional[List[str]]: """ Array of chromedriver CLI arguments. """ return self.get_capability(CHROMEDRIVER_ARGS) @chromedriver_args.setter - def chromedriver_args(self, value: list[str]) -> None: + def chromedriver_args(self, value: List[str]) -> None: """ Array of chromedriver [command line arguments](http://www.assertselenium.com/java/list-of-chrome-driver-command-line-arguments/). diff --git a/appium/options/android/common/context/chromedriver_chrome_mapping_file_option.py b/appium/options/android/common/context/chromedriver_chrome_mapping_file_option.py index fddfa23b6..cc84bed1b 100644 --- a/appium/options/android/common/context/chromedriver_chrome_mapping_file_option.py +++ b/appium/options/android/common/context/chromedriver_chrome_mapping_file_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ChromedriverChromeMappingFileOption(SupportsCapabilities): @property - def chromedriver_chrome_mapping_file(self) -> str | None: + def chromedriver_chrome_mapping_file(self) -> Optional[str]: """ Full path to the chromedrivers mapping file is located. """ diff --git a/appium/options/android/common/context/chromedriver_disable_build_check_option.py b/appium/options/android/common/context/chromedriver_disable_build_check_option.py index e9831e70f..16886ab1c 100644 --- a/appium/options/android/common/context/chromedriver_disable_build_check_option.py +++ b/appium/options/android/common/context/chromedriver_disable_build_check_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ChromedriverDisableBuildCheckOption(SupportsCapabilities): @property - def chromedriver_disable_build_check(self) -> bool | None: + def chromedriver_disable_build_check(self) -> Optional[bool]: """ Whether to disable the compatibility validation between the current chromedriver and the destination browser/web view. diff --git a/appium/options/android/common/context/chromedriver_executable_dir_option.py b/appium/options/android/common/context/chromedriver_executable_dir_option.py index e57eace0b..64e96c98e 100644 --- a/appium/options/android/common/context/chromedriver_executable_dir_option.py +++ b/appium/options/android/common/context/chromedriver_executable_dir_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ChromedriverExecutableDirOption(SupportsCapabilities): @property - def chromedriver_executable_dir(self) -> str | None: + def chromedriver_executable_dir(self) -> Optional[str]: """ Full path to the folder where chromedriver executables are located. """ diff --git a/appium/options/android/common/context/chromedriver_executable_option.py b/appium/options/android/common/context/chromedriver_executable_option.py index f6e25f360..79f30ca14 100644 --- a/appium/options/android/common/context/chromedriver_executable_option.py +++ b/appium/options/android/common/context/chromedriver_executable_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ChromedriverExecutableOption(SupportsCapabilities): @property - def chromedriver_executable(self) -> str | None: + def chromedriver_executable(self) -> Optional[str]: """ Path to the chromedriver executable on the server file system. """ diff --git a/appium/options/android/common/context/chromedriver_port_option.py b/appium/options/android/common/context/chromedriver_port_option.py index fc6867487..8a7ef8ab7 100644 --- a/appium/options/android/common/context/chromedriver_port_option.py +++ b/appium/options/android/common/context/chromedriver_port_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ChromedriverPortOption(SupportsCapabilities): @property - def chromedriver_port(self) -> int | None: + def chromedriver_port(self) -> Optional[int]: """ Local port number to use for Chromedriver communication. """ diff --git a/appium/options/android/common/context/chromedriver_ports_option.py b/appium/options/android/common/context/chromedriver_ports_option.py index 84fb30fab..5e13c987b 100644 --- a/appium/options/android/common/context/chromedriver_ports_option.py +++ b/appium/options/android/common/context/chromedriver_ports_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import List, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,14 +24,14 @@ class ChromedriverPortsOption(SupportsCapabilities): @property - def chromedriver_ports(self) -> list[int] | None: + def chromedriver_ports(self) -> Optional[List[int]]: """ Local port numbers to use for Chromedriver communication. """ return self.get_capability(CHROMEDRIVER_PORTS) @chromedriver_ports.setter - def chromedriver_ports(self, value: list[int]) -> None: + def chromedriver_ports(self, value: List[int]) -> None: """ Array of possible port numbers to assign for Chromedriver communication. If none of the port in this array is free then a server error is thrown. diff --git a/appium/options/android/common/context/chromedriver_use_system_executable_option.py b/appium/options/android/common/context/chromedriver_use_system_executable_option.py index 673c43fc8..b164ed13f 100644 --- a/appium/options/android/common/context/chromedriver_use_system_executable_option.py +++ b/appium/options/android/common/context/chromedriver_use_system_executable_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ChromedriverUseSystemExecutableOption(SupportsCapabilities): @property - def chromedriver_use_system_executable(self) -> bool | None: + def chromedriver_use_system_executable(self) -> Optional[bool]: """ Whether to use the system chromedriver. """ diff --git a/appium/options/android/common/context/ensure_webviews_have_pages_option.py b/appium/options/android/common/context/ensure_webviews_have_pages_option.py index da374fa67..d1f549a9f 100644 --- a/appium/options/android/common/context/ensure_webviews_have_pages_option.py +++ b/appium/options/android/common/context/ensure_webviews_have_pages_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class EnsureWebviewsHavePagesOption(SupportsCapabilities): @property - def ensure_webviews_have_pages(self) -> bool | None: + def ensure_webviews_have_pages(self) -> Optional[bool]: """ Whether to ensure if web views have pages. """ diff --git a/appium/options/android/common/context/extract_chrome_android_package_from_context_name_option.py b/appium/options/android/common/context/extract_chrome_android_package_from_context_name_option.py index 79bc4a6c3..95559072f 100644 --- a/appium/options/android/common/context/extract_chrome_android_package_from_context_name_option.py +++ b/appium/options/android/common/context/extract_chrome_android_package_from_context_name_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ExtractChromeAndroidPackageFromContextNameOption(SupportsCapabilities): @property - def extract_chrome_android_package_from_context_name(self) -> bool | None: + def extract_chrome_android_package_from_context_name(self) -> Optional[bool]: """ Whether to use the android package identifier associated with the context name. """ diff --git a/appium/options/android/common/context/native_web_screenshot_option.py b/appium/options/android/common/context/native_web_screenshot_option.py index 06a1db0d1..1c59a60b5 100644 --- a/appium/options/android/common/context/native_web_screenshot_option.py +++ b/appium/options/android/common/context/native_web_screenshot_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class NativeWebScreenshotOption(SupportsCapabilities): @property - def native_web_screenshot(self) -> bool | None: + def native_web_screenshot(self) -> Optional[bool]: """ Whether to use native screenshots in web view context. """ diff --git a/appium/options/android/common/context/recreate_chrome_driver_sessions_option.py b/appium/options/android/common/context/recreate_chrome_driver_sessions_option.py index 4ec8880ff..4ef8d1096 100644 --- a/appium/options/android/common/context/recreate_chrome_driver_sessions_option.py +++ b/appium/options/android/common/context/recreate_chrome_driver_sessions_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class RecreateChromeDriverSessionsOption(SupportsCapabilities): @property - def recreate_chrome_driver_sessions(self) -> bool | None: + def recreate_chrome_driver_sessions(self) -> Optional[bool]: """ Whether chromedriver sessions should be killed and then recreated instead of just suspending it on context switch. diff --git a/appium/options/android/common/context/show_chromedriver_log_option.py b/appium/options/android/common/context/show_chromedriver_log_option.py index eff41c558..da4f283a4 100644 --- a/appium/options/android/common/context/show_chromedriver_log_option.py +++ b/appium/options/android/common/context/show_chromedriver_log_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ShowChromedriverLogOption(SupportsCapabilities): @property - def show_chromedriver_log(self) -> bool | None: + def show_chromedriver_log(self) -> Optional[bool]: """ Whether to forward chromedriver output to the Appium server log. """ diff --git a/appium/options/android/common/context/webview_devtools_port_option.py b/appium/options/android/common/context/webview_devtools_port_option.py index a0c298579..2f0e6d7a7 100644 --- a/appium/options/android/common/context/webview_devtools_port_option.py +++ b/appium/options/android/common/context/webview_devtools_port_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class WebviewDevtoolsPortOption(SupportsCapabilities): @property - def webview_devtools_port(self) -> int | None: + def webview_devtools_port(self) -> Optional[int]: """ Local port number to use for devtools communication. """ diff --git a/appium/options/android/common/localization/locale_script_option.py b/appium/options/android/common/localization/locale_script_option.py index d740aecd7..abcd08529 100644 --- a/appium/options/android/common/localization/locale_script_option.py +++ b/appium/options/android/common/localization/locale_script_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class LocaleScriptOption(SupportsCapabilities): @property - def locale_script(self) -> str | None: + def locale_script(self) -> Optional[str]: """ Canonical name of the locale to be set for the app under test. """ diff --git a/appium/options/android/common/locking/skip_unlock_option.py b/appium/options/android/common/locking/skip_unlock_option.py index 6f4dc5492..c80a6efa7 100644 --- a/appium/options/android/common/locking/skip_unlock_option.py +++ b/appium/options/android/common/locking/skip_unlock_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SkipUnlockOption(SupportsCapabilities): @property - def skip_unlock(self) -> bool | None: + def skip_unlock(self) -> Optional[bool]: """ Whether to skip the check for lock screen presence. """ diff --git a/appium/options/android/common/locking/unlock_key_option.py b/appium/options/android/common/locking/unlock_key_option.py index 297f27e08..ffee5a1a5 100644 --- a/appium/options/android/common/locking/unlock_key_option.py +++ b/appium/options/android/common/locking/unlock_key_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UnlockKeyOption(SupportsCapabilities): @property - def unlock_key(self) -> str | None: + def unlock_key(self) -> Optional[str]: """ Unlock key. """ diff --git a/appium/options/android/common/locking/unlock_strategy_option.py b/appium/options/android/common/locking/unlock_strategy_option.py index fc36cd38c..fb884518d 100644 --- a/appium/options/android/common/locking/unlock_strategy_option.py +++ b/appium/options/android/common/locking/unlock_strategy_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UnlockStrategyOption(SupportsCapabilities): @property - def unlock_strategy(self) -> str | None: + def unlock_strategy(self) -> Optional[str]: """ Unlock strategy name. """ diff --git a/appium/options/android/common/locking/unlock_success_timeout_option.py b/appium/options/android/common/locking/unlock_success_timeout_option.py index 268656c0e..488ae5f9e 100644 --- a/appium/options/android/common/locking/unlock_success_timeout_option.py +++ b/appium/options/android/common/locking/unlock_success_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class UnlockSuccessTimeoutOption(SupportsCapabilities): @property - def unlock_success_timeout(self) -> timedelta | None: + def unlock_success_timeout(self) -> Optional[timedelta]: """ Timeout to wait until the device is unlocked. """ @@ -32,7 +33,7 @@ def unlock_success_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @unlock_success_timeout.setter - def unlock_success_timeout(self, value: timedelta | int) -> None: + def unlock_success_timeout(self, value: Union[timedelta, int]) -> None: """ Maximum timeout to wait until the device is unlocked. 2000 ms by default. diff --git a/appium/options/android/common/locking/unlock_type_option.py b/appium/options/android/common/locking/unlock_type_option.py index a1677b400..7026cf343 100644 --- a/appium/options/android/common/locking/unlock_type_option.py +++ b/appium/options/android/common/locking/unlock_type_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UnlockTypeOption(SupportsCapabilities): @property - def unlock_type(self) -> str | None: + def unlock_type(self) -> Optional[str]: """ Unlock type. """ diff --git a/appium/options/android/common/mjpeg/mjpeg_screenshot_url_option.py b/appium/options/android/common/mjpeg/mjpeg_screenshot_url_option.py index c0ddcf6da..70bad0177 100644 --- a/appium/options/android/common/mjpeg/mjpeg_screenshot_url_option.py +++ b/appium/options/android/common/mjpeg/mjpeg_screenshot_url_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class MjpegScreenshotUrlOption(SupportsCapabilities): @property - def mjpeg_screenshot_url(self) -> str | None: + def mjpeg_screenshot_url(self) -> Optional[str]: """ URL of a service that provides realtime device screenshots in MJPEG format. """ diff --git a/appium/options/android/common/other/disable_suppress_accessibility_service_option.py b/appium/options/android/common/other/disable_suppress_accessibility_service_option.py index e0a5b0169..980da567c 100644 --- a/appium/options/android/common/other/disable_suppress_accessibility_service_option.py +++ b/appium/options/android/common/other/disable_suppress_accessibility_service_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class DisableSuppressAccessibilityServiceOption(SupportsCapabilities): @property - def disable_suppress_accessibility_service(self) -> bool | None: + def disable_suppress_accessibility_service(self) -> Optional[bool]: """ Whether to suppress accessibility services. """ diff --git a/appium/options/android/common/other/user_profile_option.py b/appium/options/android/common/other/user_profile_option.py index a886acef9..76871396a 100644 --- a/appium/options/android/common/other/user_profile_option.py +++ b/appium/options/android/common/other/user_profile_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UserProfileOption(SupportsCapabilities): @property - def user_profile(self) -> int | None: + def user_profile(self) -> Optional[int]: """ Integer identifier of a user profile. """ diff --git a/appium/options/android/common/signing/key_alias_option.py b/appium/options/android/common/signing/key_alias_option.py index ea948faa7..471b28ee8 100644 --- a/appium/options/android/common/signing/key_alias_option.py +++ b/appium/options/android/common/signing/key_alias_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class KeyAliasOption(SupportsCapabilities): @property - def key_alias(self) -> str | None: + def key_alias(self) -> Optional[str]: """ Keystore key alias. """ diff --git a/appium/options/android/common/signing/key_password_option.py b/appium/options/android/common/signing/key_password_option.py index d8879c6cb..5ed91d1c1 100644 --- a/appium/options/android/common/signing/key_password_option.py +++ b/appium/options/android/common/signing/key_password_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class KeyPasswordOption(SupportsCapabilities): @property - def key_password(self) -> str | None: + def key_password(self) -> Optional[str]: """ Keystore key password. """ diff --git a/appium/options/android/common/signing/keystore_password_option.py b/appium/options/android/common/signing/keystore_password_option.py index c4a9cc3ec..8b7667117 100644 --- a/appium/options/android/common/signing/keystore_password_option.py +++ b/appium/options/android/common/signing/keystore_password_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class KeystorePasswordOption(SupportsCapabilities): @property - def keystore_password(self) -> str | None: + def keystore_password(self) -> Optional[str]: """ Keystore password. """ diff --git a/appium/options/android/common/signing/keystore_path_option.py b/appium/options/android/common/signing/keystore_path_option.py index 6085ce50e..3489efe5c 100644 --- a/appium/options/android/common/signing/keystore_path_option.py +++ b/appium/options/android/common/signing/keystore_path_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class KeystorePathOption(SupportsCapabilities): @property - def keystore_path(self) -> str | None: + def keystore_path(self) -> Optional[str]: """ The path to keystore. """ diff --git a/appium/options/android/common/signing/no_sign_option.py b/appium/options/android/common/signing/no_sign_option.py index 8e0d59a8c..a5a52841b 100644 --- a/appium/options/android/common/signing/no_sign_option.py +++ b/appium/options/android/common/signing/no_sign_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class NoSignOption(SupportsCapabilities): @property - def no_sign(self) -> bool | None: + def no_sign(self) -> Optional[bool]: """ Whether to skip application signing. """ diff --git a/appium/options/android/common/signing/use_keystore_option.py b/appium/options/android/common/signing/use_keystore_option.py index 737d65658..f8a947a5c 100644 --- a/appium/options/android/common/signing/use_keystore_option.py +++ b/appium/options/android/common/signing/use_keystore_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UseKeystoreOption(SupportsCapabilities): @property - def use_keystore(self) -> bool | None: + def use_keystore(self) -> Optional[bool]: """ Whether to use custom keystore. """ diff --git a/appium/options/android/espresso/activity_options_option.py b/appium/options/android/espresso/activity_options_option.py index 45d632dbc..a15935feb 100644 --- a/appium/options/android/espresso/activity_options_option.py +++ b/appium/options/android/espresso/activity_options_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,14 +24,14 @@ class ActivityOptionsOption(SupportsCapabilities): @property - def activity_options(self) -> dict | None: + def activity_options(self) -> Optional[Dict]: """ Activity options. """ return self.get_capability(ACTIVITY_OPTIONS) @activity_options.setter - def activity_options(self, value: dict) -> None: + def activity_options(self, value: Dict) -> None: """ The mapping of custom options for the main app activity that is going to be started. Check diff --git a/appium/options/android/espresso/app_locale_option.py b/appium/options/android/espresso/app_locale_option.py index 478678067..01ae27545 100644 --- a/appium/options/android/espresso/app_locale_option.py +++ b/appium/options/android/espresso/app_locale_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,14 +24,14 @@ class AppLocaleOption(SupportsCapabilities): @property - def app_locale(self) -> dict[str, str] | None: + def app_locale(self) -> Optional[Dict[str, str]]: """ Locale for the app under test. """ return self.get_capability(APP_LOCALE) @app_locale.setter - def app_locale(self, value: dict[str, str]) -> None: + def app_locale(self, value: Dict[str, str]) -> None: """ Sets the locale for the app under test. The main difference between this option and the above ones is that this option only changes the locale for the application diff --git a/appium/options/android/espresso/base.py b/appium/options/android/espresso/base.py index 4ceec1b38..c725b63a7 100644 --- a/appium/options/android/espresso/base.py +++ b/appium/options/android/espresso/base.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict from appium.options.android.common.adb.adb_exec_timeout_option import AdbExecTimeoutOption from appium.options.android.common.adb.adb_port_option import AdbPortOption @@ -213,7 +214,7 @@ class EspressoOptions( AppLocaleOption, ): @property - def default_capabilities(self) -> dict: + def default_capabilities(self) -> Dict: return { AUTOMATION_NAME: 'Espresso', PLATFORM_NAME: 'Android', diff --git a/appium/options/android/espresso/espresso_build_config_option.py b/appium/options/android/espresso/espresso_build_config_option.py index 7f46e21ce..7a8694f31 100644 --- a/appium/options/android/espresso/espresso_build_config_option.py +++ b/appium/options/android/espresso/espresso_build_config_option.py @@ -16,7 +16,7 @@ # under the License. import json -from typing import Any +from typing import Any, Dict, Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -25,7 +25,7 @@ class EspressoBuildConfigOption(SupportsCapabilities): @property - def espresso_build_config(self) -> dict[str, Any] | str | None: + def espresso_build_config(self) -> Optional[Union[Dict[str, Any], str]]: """ Espresso build config. """ @@ -36,7 +36,7 @@ def espresso_build_config(self) -> dict[str, Any] | str | None: return value @espresso_build_config.setter - def espresso_build_config(self, value: dict[str, Any] | str) -> None: + def espresso_build_config(self, value: Union[Dict[str, Any], str]) -> None: """ This config allows to customize several important properties of Espresso server. Refer to diff --git a/appium/options/android/espresso/espresso_server_launch_timeout_option.py b/appium/options/android/espresso/espresso_server_launch_timeout_option.py index 91d3cf19c..9335358de 100644 --- a/appium/options/android/espresso/espresso_server_launch_timeout_option.py +++ b/appium/options/android/espresso/espresso_server_launch_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class EspressoServerLaunchTimeoutOption(SupportsCapabilities): @property - def espresso_server_launch_timeout(self) -> timedelta | None: + def espresso_server_launch_timeout(self) -> Optional[timedelta]: """ Maximum timeout to wait until Espresso server is listening on the device. """ @@ -32,7 +33,7 @@ def espresso_server_launch_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @espresso_server_launch_timeout.setter - def espresso_server_launch_timeout(self, value: timedelta | int) -> None: + def espresso_server_launch_timeout(self, value: Union[timedelta, int]) -> None: """ Set the maximum timeout to wait util Espresso is listening on the device. 45000 ms by default diff --git a/appium/options/android/espresso/force_espresso_rebuild_option.py b/appium/options/android/espresso/force_espresso_rebuild_option.py index cbdf67b06..8971db795 100644 --- a/appium/options/android/espresso/force_espresso_rebuild_option.py +++ b/appium/options/android/espresso/force_espresso_rebuild_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ForceEspressoRebuildOption(SupportsCapabilities): @property - def force_espresso_rebuild(self) -> bool | None: + def force_espresso_rebuild(self) -> Optional[bool]: """ Whether to force Espresso server rebuild on a new session startup. """ diff --git a/appium/options/android/espresso/intent_options_option.py b/appium/options/android/espresso/intent_options_option.py index 7618cb690..59625bd91 100644 --- a/appium/options/android/espresso/intent_options_option.py +++ b/appium/options/android/espresso/intent_options_option.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -from typing import Any +from typing import Any, Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,14 +24,14 @@ class IntentOptionsOption(SupportsCapabilities): @property - def intent_options(self) -> dict[str, Any] | None: + def intent_options(self) -> Optional[Dict[str, Any]]: """ Intent options. """ return self.get_capability(INTENT_OPTIONS) @intent_options.setter - def intent_options(self, value: dict[str, Any]) -> None: + def intent_options(self, value: Dict[str, Any]) -> None: """ The mapping of custom options for the intent that is going to be passed to the main app activity. Check diff --git a/appium/options/android/espresso/show_gradle_log_option.py b/appium/options/android/espresso/show_gradle_log_option.py index 9fdfff486..48d233dbf 100644 --- a/appium/options/android/espresso/show_gradle_log_option.py +++ b/appium/options/android/espresso/show_gradle_log_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ShowGradleLogOption(SupportsCapabilities): @property - def show_gradle_log(self) -> bool | None: + def show_gradle_log(self) -> Optional[bool]: """ Whether to include Gradle log to the regular server log. """ diff --git a/appium/options/android/uiautomator2/base.py b/appium/options/android/uiautomator2/base.py index 4b976ea97..7efcb9036 100644 --- a/appium/options/android/uiautomator2/base.py +++ b/appium/options/android/uiautomator2/base.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict from appium.options.android.common.adb.adb_exec_timeout_option import AdbExecTimeoutOption from appium.options.android.common.adb.adb_port_option import AdbPortOption @@ -213,7 +214,7 @@ class UiAutomator2Options( UserProfileOption, ): @property - def default_capabilities(self) -> dict: + def default_capabilities(self) -> Dict: return { AUTOMATION_NAME: 'UIAutomator2', PLATFORM_NAME: 'Android', diff --git a/appium/options/android/uiautomator2/disable_window_animation_option.py b/appium/options/android/uiautomator2/disable_window_animation_option.py index 65274a058..5ff614b49 100644 --- a/appium/options/android/uiautomator2/disable_window_animation_option.py +++ b/appium/options/android/uiautomator2/disable_window_animation_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class DisableWindowAnimationOption(SupportsCapabilities): @property - def disable_window_animation(self) -> bool | None: + def disable_window_animation(self) -> Optional[bool]: """ Whether window animations when starting the instrumentation process are disabled. diff --git a/appium/options/android/uiautomator2/mjpeg_server_port_option.py b/appium/options/android/uiautomator2/mjpeg_server_port_option.py index aebde7ce5..6c702646f 100644 --- a/appium/options/android/uiautomator2/mjpeg_server_port_option.py +++ b/appium/options/android/uiautomator2/mjpeg_server_port_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class MjpegServerPortOption(SupportsCapabilities): @property - def mjpeg_server_port(self) -> int | None: + def mjpeg_server_port(self) -> Optional[int]: """ Number of the port UiAutomator2 server starts the MJPEG server on. """ diff --git a/appium/options/android/uiautomator2/skip_device_initialization_option.py b/appium/options/android/uiautomator2/skip_device_initialization_option.py index 6b4e8d1fb..eda37be30 100644 --- a/appium/options/android/uiautomator2/skip_device_initialization_option.py +++ b/appium/options/android/uiautomator2/skip_device_initialization_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SkipDeviceInitializationOption(SupportsCapabilities): @property - def skip_device_initialization(self) -> bool | None: + def skip_device_initialization(self) -> Optional[bool]: """ Whether initial device startup checks by the server are disabled. """ diff --git a/appium/options/android/uiautomator2/skip_server_installation_option.py b/appium/options/android/uiautomator2/skip_server_installation_option.py index 6ff5eba42..c343f78f1 100644 --- a/appium/options/android/uiautomator2/skip_server_installation_option.py +++ b/appium/options/android/uiautomator2/skip_server_installation_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SkipServerInstallationOption(SupportsCapabilities): @property - def skip_server_installation(self) -> bool | None: + def skip_server_installation(self) -> Optional[bool]: """ Whether to skip the server components installation on the device under test and all the related checks. diff --git a/appium/options/android/uiautomator2/uiautomator2_server_install_timeout_option.py b/appium/options/android/uiautomator2/uiautomator2_server_install_timeout_option.py index 2c48608e6..4b25638f8 100644 --- a/appium/options/android/uiautomator2/uiautomator2_server_install_timeout_option.py +++ b/appium/options/android/uiautomator2/uiautomator2_server_install_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class Uiautomator2ServerInstallTimeoutOption(SupportsCapabilities): @property - def uiautomator2_server_install_timeout(self) -> timedelta | None: + def uiautomator2_server_install_timeout(self) -> Optional[timedelta]: """ Maximum timeout to wait until UiAutomator2 server is installed on the device. """ @@ -32,7 +33,7 @@ def uiautomator2_server_install_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @uiautomator2_server_install_timeout.setter - def uiautomator2_server_install_timeout(self, value: timedelta | int) -> None: + def uiautomator2_server_install_timeout(self, value: Union[timedelta, int]) -> None: """ Set the maximum timeout to wait util UiAutomator2 server is installed on the device. 20000 ms by default diff --git a/appium/options/android/uiautomator2/uiautomator2_server_launch_timeout_option.py b/appium/options/android/uiautomator2/uiautomator2_server_launch_timeout_option.py index 993087772..ce74c4473 100644 --- a/appium/options/android/uiautomator2/uiautomator2_server_launch_timeout_option.py +++ b/appium/options/android/uiautomator2/uiautomator2_server_launch_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class Uiautomator2ServerLaunchTimeoutOption(SupportsCapabilities): @property - def uiautomator2_server_launch_timeout(self) -> timedelta | None: + def uiautomator2_server_launch_timeout(self) -> Optional[timedelta]: """ Maximum timeout to wait until UiAutomator2 server is listening on the device. """ @@ -32,7 +33,7 @@ def uiautomator2_server_launch_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @uiautomator2_server_launch_timeout.setter - def uiautomator2_server_launch_timeout(self, value: timedelta | int) -> None: + def uiautomator2_server_launch_timeout(self, value: Union[timedelta, int]) -> None: """ Set the maximum timeout to wait util UiAutomator2Server is listening on the device. 30000 ms by default diff --git a/appium/options/android/uiautomator2/uiautomator2_server_read_timeout_option.py b/appium/options/android/uiautomator2/uiautomator2_server_read_timeout_option.py index bd962fe0c..cb8a39c25 100644 --- a/appium/options/android/uiautomator2/uiautomator2_server_read_timeout_option.py +++ b/appium/options/android/uiautomator2/uiautomator2_server_read_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class Uiautomator2ServerReadTimeoutOption(SupportsCapabilities): @property - def uiautomator2_server_read_timeout(self) -> timedelta | None: + def uiautomator2_server_read_timeout(self) -> Optional[timedelta]: """ Maximum timeout to wait for an HTTP response from UiAutomator2Server. """ @@ -32,7 +33,7 @@ def uiautomator2_server_read_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @uiautomator2_server_read_timeout.setter - def uiautomator2_server_read_timeout(self, value: timedelta | int) -> None: + def uiautomator2_server_read_timeout(self, value: Union[timedelta, int]) -> None: """ Set the maximum timeout to wait for a HTTP response from UiAutomator2Server. Only values greater than zero are accepted. If the given value is too low diff --git a/appium/options/common/app_option.py b/appium/options/common/app_option.py index 0ad52788a..4a8fff1b1 100644 --- a/appium/options/common/app_option.py +++ b/appium/options/common/app_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppOption(SupportsCapabilities): @property - def app(self) -> str | None: + def app(self) -> Optional[str]: """ String representing app location. """ diff --git a/appium/options/common/auto_web_view_option.py b/appium/options/common/auto_web_view_option.py index ced463773..6983aef47 100644 --- a/appium/options/common/auto_web_view_option.py +++ b/appium/options/common/auto_web_view_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AutoWebViewOption(SupportsCapabilities): @property - def auto_web_view(self) -> bool | None: + def auto_web_view(self) -> Optional[bool]: """ Whether the driver should try to automatically switch to a web view context after the session is started. diff --git a/appium/options/common/automation_name_option.py b/appium/options/common/automation_name_option.py index 49fe5efb0..c18e0a15b 100644 --- a/appium/options/common/automation_name_option.py +++ b/appium/options/common/automation_name_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AutomationNameOption(SupportsCapabilities): @property - def automation_name(self) -> str | None: + def automation_name(self) -> Optional[str]: """ String representing the name of the automation engine name. """ diff --git a/appium/options/common/base.py b/appium/options/common/base.py index 5bd4f1a94..5ac7b2e36 100644 --- a/appium/options/common/base.py +++ b/appium/options/common/base.py @@ -16,7 +16,7 @@ # under the License. import copy -from typing import Any, TypeVar +from typing import Any, Dict, TypeVar from selenium.webdriver.common.options import BaseOptions @@ -43,7 +43,7 @@ class AppiumOptions( FullResetOption, NewCommandTimeoutOption, ): - _caps: dict + _caps: Dict W3C_CAPABILITY_NAMES = frozenset( [ 'acceptInsecureCerts', @@ -85,14 +85,14 @@ def get_capability(self, name: str) -> Any: """Fetches capability value or None if the capability is not set""" return self._caps[name] if name in self._caps else self._caps.get(f'{APPIUM_PREFIX}{name}') - def load_capabilities(self: T, caps: dict[str, Any]) -> T: + def load_capabilities(self: T, caps: Dict[str, Any]) -> T: """Sets multiple capabilities""" for name, value in caps.items(): self.set_capability(name, value) return self @staticmethod - def as_w3c(capabilities: dict) -> dict: + def as_w3c(capabilities: Dict) -> Dict: """ Formats given capabilities to a valid W3C session request object @@ -112,7 +112,7 @@ def process_key(k: str) -> str: processed_caps = {process_key(k): v for k, v in copy.deepcopy(capabilities).items()} return {'capabilities': {'firstMatch': [{}], 'alwaysMatch': processed_caps}} - def to_w3c(self) -> dict: + def to_w3c(self) -> Dict: """ Formats the instance to a valid W3C session request object @@ -120,9 +120,9 @@ def to_w3c(self) -> dict: """ return self.as_w3c(self.to_capabilities()) - def to_capabilities(self) -> dict: + def to_capabilities(self) -> Dict: return copy.copy(self._caps) @property - def default_capabilities(self) -> dict: + def default_capabilities(self) -> Dict: return {} diff --git a/appium/options/common/browser_name_option.py b/appium/options/common/browser_name_option.py index fc5fec4ea..2b107972a 100644 --- a/appium/options/common/browser_name_option.py +++ b/appium/options/common/browser_name_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class BrowserNameOption(SupportsCapabilities): @property - def browser_name(self) -> str | None: + def browser_name(self) -> Optional[str]: """ The name of the browser to run the test on. """ diff --git a/appium/options/common/bundle_id_option.py b/appium/options/common/bundle_id_option.py index 1a09ed939..0546571a5 100644 --- a/appium/options/common/bundle_id_option.py +++ b/appium/options/common/bundle_id_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class BundleIdOption(SupportsCapabilities): @property - def bundle_id(self) -> str | None: + def bundle_id(self) -> Optional[str]: """ The bundle identifier of the application to automate. """ diff --git a/appium/options/common/clear_system_files_option.py b/appium/options/common/clear_system_files_option.py index 2614fb36f..208c4bb96 100644 --- a/appium/options/common/clear_system_files_option.py +++ b/appium/options/common/clear_system_files_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ClearSystemFilesOption(SupportsCapabilities): @property - def clear_system_files(self) -> bool | None: + def clear_system_files(self) -> Optional[bool]: """ Whether the driver should delete generated files at the end of a session. """ diff --git a/appium/options/common/device_name_option.py b/appium/options/common/device_name_option.py index 72b952957..8e33cce39 100644 --- a/appium/options/common/device_name_option.py +++ b/appium/options/common/device_name_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class DeviceNameOption(SupportsCapabilities): @property - def device_name(self) -> str | None: + def device_name(self) -> Optional[str]: """ The name of the device. """ diff --git a/appium/options/common/enable_performance_logging_option.py b/appium/options/common/enable_performance_logging_option.py index f95b249ec..22a4bdb30 100644 --- a/appium/options/common/enable_performance_logging_option.py +++ b/appium/options/common/enable_performance_logging_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class EnablePerformanceLoggingOption(SupportsCapabilities): @property - def enable_performance_logging(self) -> bool | None: + def enable_performance_logging(self) -> Optional[bool]: """ Whether to enable additional performance logging. """ diff --git a/appium/options/common/event_timings_option.py b/appium/options/common/event_timings_option.py index f2e09b48e..b9c0a1245 100644 --- a/appium/options/common/event_timings_option.py +++ b/appium/options/common/event_timings_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class EventTimingsOption(SupportsCapabilities): @property - def event_timings(self) -> bool | None: + def event_timings(self) -> Optional[bool]: """ Whether the driver should to report the timings for various Appium-internal events. diff --git a/appium/options/common/full_reset_option.py b/appium/options/common/full_reset_option.py index 2e8b73be6..aee86d7db 100644 --- a/appium/options/common/full_reset_option.py +++ b/appium/options/common/full_reset_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class FullResetOption(SupportsCapabilities): @property - def full_reset(self) -> bool | None: + def full_reset(self) -> Optional[bool]: """ Whether the driver should perform a full reset. """ diff --git a/appium/options/common/is_headless_option.py b/appium/options/common/is_headless_option.py index 19830ceb3..bc0c2b110 100644 --- a/appium/options/common/is_headless_option.py +++ b/appium/options/common/is_headless_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class IsHeadlessOption(SupportsCapabilities): @property - def is_headless(self) -> bool | None: + def is_headless(self) -> Optional[bool]: """ Whether the driver should start emulator/simulator in headless mode. """ diff --git a/appium/options/common/language_option.py b/appium/options/common/language_option.py index b176218f6..f82de63d0 100644 --- a/appium/options/common/language_option.py +++ b/appium/options/common/language_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class LanguageOption(SupportsCapabilities): @property - def language(self) -> str | None: + def language(self) -> Optional[str]: """ Language abbreviation to use in a test session. """ diff --git a/appium/options/common/locale_option.py b/appium/options/common/locale_option.py index 571dad571..58503a890 100644 --- a/appium/options/common/locale_option.py +++ b/appium/options/common/locale_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class LocaleOption(SupportsCapabilities): @property - def locale(self) -> str | None: + def locale(self) -> Optional[str]: """ Locale abbreviation to use in a test session. """ diff --git a/appium/options/common/new_command_timeout_option.py b/appium/options/common/new_command_timeout_option.py index 79c9d4b34..377ef7193 100644 --- a/appium/options/common/new_command_timeout_option.py +++ b/appium/options/common/new_command_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from .supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class NewCommandTimeoutOption(SupportsCapabilities): @property - def new_command_timeout(self) -> timedelta | None: + def new_command_timeout(self) -> Optional[timedelta]: """ The allowed time before seeing a new server command. """ @@ -32,7 +33,7 @@ def new_command_timeout(self) -> timedelta | None: return None if value is None else timedelta(seconds=value) @new_command_timeout.setter - def new_command_timeout(self, value: timedelta | int) -> None: + def new_command_timeout(self, value: Union[timedelta, int]) -> None: """ Set the allowed time before seeing a new server command. The value could either be provided as timedelta instance or an integer number of seconds. diff --git a/appium/options/common/no_reset_option.py b/appium/options/common/no_reset_option.py index 9b6f3e926..ee2dd9988 100644 --- a/appium/options/common/no_reset_option.py +++ b/appium/options/common/no_reset_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class NoResetOption(SupportsCapabilities): @property - def no_reset(self) -> bool | None: + def no_reset(self) -> Optional[bool]: """ Whether the driver should not perform a reset. """ diff --git a/appium/options/common/orientation_option.py b/appium/options/common/orientation_option.py index 54c7e031d..4141a4d90 100644 --- a/appium/options/common/orientation_option.py +++ b/appium/options/common/orientation_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class OrientationOption(SupportsCapabilities): @property - def orientation(self) -> str | None: + def orientation(self) -> Optional[str]: """ The orientation of the device's screen. Usually this is either 'PORTRAIT' or 'LANDSCAPE'. diff --git a/appium/options/common/other_apps_option.py b/appium/options/common/other_apps_option.py index b1569f193..6efbbbc4e 100644 --- a/appium/options/common/other_apps_option.py +++ b/appium/options/common/other_apps_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class OtherAppsOption(SupportsCapabilities): @property - def other_apps(self) -> str | None: + def other_apps(self) -> Optional[str]: """ Locations of apps to install before running a test. """ diff --git a/appium/options/common/platform_version_option.py b/appium/options/common/platform_version_option.py index 4baf6b66c..d237cc280 100644 --- a/appium/options/common/platform_version_option.py +++ b/appium/options/common/platform_version_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class PlatformVersionOption(SupportsCapabilities): @property - def platform_version(self) -> str | None: + def platform_version(self) -> Optional[str]: """ The platform version of an emulator or a real device. This capability is used for device autodetection if udid is not provided. diff --git a/appium/options/common/postrun_option.py b/appium/options/common/postrun_option.py index 5a965d8f8..f940cd6b6 100644 --- a/appium/options/common/postrun_option.py +++ b/appium/options/common/postrun_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict, Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class PostrunOption(SupportsCapabilities): @property - def postrun(self) -> dict[str, str] | None: + def postrun(self) -> Optional[Dict[str, str]]: """ System script which is supposed to be executed upon driver session quit. @@ -31,7 +32,7 @@ def postrun(self) -> dict[str, str] | None: return self.get_capability(POSTRUN) @postrun.setter - def postrun(self, value: dict[str, str]) -> None: + def postrun(self, value: Dict[str, str]) -> None: """ Set a system script to execute upon driver session quit. """ diff --git a/appium/options/common/prerun_option.py b/appium/options/common/prerun_option.py index a718d6740..139874c76 100644 --- a/appium/options/common/prerun_option.py +++ b/appium/options/common/prerun_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict, Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class PrerunOption(SupportsCapabilities): @property - def prerun(self) -> dict[str, str] | None: + def prerun(self) -> Optional[Dict[str, str]]: """ System script which is supposed to be executed before a driver session is initialised. @@ -31,7 +32,7 @@ def prerun(self) -> dict[str, str] | None: return self.get_capability(PRERUN) @prerun.setter - def prerun(self, value: dict[str, str]) -> None: + def prerun(self, value: Dict[str, str]) -> None: """ Set a system script which is supposed to be executed before a driver session is initialised. diff --git a/appium/options/common/print_page_source_on_find_failure_option.py b/appium/options/common/print_page_source_on_find_failure_option.py index b97fbc8a1..b93f07cf5 100644 --- a/appium/options/common/print_page_source_on_find_failure_option.py +++ b/appium/options/common/print_page_source_on_find_failure_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class PrintPageSourceOnFindFailureOption(SupportsCapabilities): @property - def print_page_source_on_find_failure(self) -> bool | None: + def print_page_source_on_find_failure(self) -> Optional[bool]: """ Whether the driver should print the page source to the log if a find failure occurs. diff --git a/appium/options/common/skip_log_capture_option.py b/appium/options/common/skip_log_capture_option.py index 522bc1079..668704e13 100644 --- a/appium/options/common/skip_log_capture_option.py +++ b/appium/options/common/skip_log_capture_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SkipLogCaptureOption(SupportsCapabilities): @property - def skip_log_capture(self) -> bool | None: + def skip_log_capture(self) -> Optional[bool]: """ Whether the driver should not record device logs. """ diff --git a/appium/options/common/system_host_option.py b/appium/options/common/system_host_option.py index 57d502b75..0544de5ca 100644 --- a/appium/options/common/system_host_option.py +++ b/appium/options/common/system_host_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SystemHostOption(SupportsCapabilities): @property - def system_host(self) -> str | None: + def system_host(self) -> Optional[str]: """ The name of the host for the internal server to listen on. """ diff --git a/appium/options/common/system_port_option.py b/appium/options/common/system_port_option.py index fef871e38..e11f3b59d 100644 --- a/appium/options/common/system_port_option.py +++ b/appium/options/common/system_port_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SystemPortOption(SupportsCapabilities): @property - def system_port(self) -> int | None: + def system_port(self) -> Optional[int]: """ The number of the port for the internal server to listen on. """ diff --git a/appium/options/common/udid_option.py b/appium/options/common/udid_option.py index 97f703c96..f760a1d21 100644 --- a/appium/options/common/udid_option.py +++ b/appium/options/common/udid_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from .supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UdidOption(SupportsCapabilities): @property - def udid(self) -> str | None: + def udid(self) -> Optional[str]: """ The unique identifier of the device under test. """ diff --git a/appium/options/flutter_integration/base.py b/appium/options/flutter_integration/base.py index 6854197af..f67b9a93c 100644 --- a/appium/options/flutter_integration/base.py +++ b/appium/options/flutter_integration/base.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict from appium.options.common.automation_name_option import AUTOMATION_NAME from appium.options.common.base import AppiumOptions @@ -32,7 +33,7 @@ class FlutterOptions( FlutterSystemPortOption, ): @property - def default_capabilities(self) -> dict: + def default_capabilities(self) -> Dict: return { AUTOMATION_NAME: 'FlutterIntegration', } diff --git a/appium/options/flutter_integration/flutter_element_wait_timeout_option.py b/appium/options/flutter_integration/flutter_element_wait_timeout_option.py index d367be1cd..bd4279774 100644 --- a/appium/options/flutter_integration/flutter_element_wait_timeout_option.py +++ b/appium/options/flutter_integration/flutter_element_wait_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class FlutterElementWaitTimeOutOption(SupportsCapabilities): @property - def flutter_element_wait_timeout(self) -> timedelta | None: + def flutter_element_wait_timeout(self) -> Optional[timedelta]: """ Maximum timeout to wait for element for Flutter integration test @@ -34,7 +35,7 @@ def flutter_element_wait_timeout(self) -> timedelta | None: return self.get_capability(FLUTTER_ELEMENT_WAIT_TIMEOUT) @flutter_element_wait_timeout.setter - def flutter_element_wait_timeout(self, value: timedelta | int) -> None: + def flutter_element_wait_timeout(self, value: Union[timedelta, int]) -> None: """ Sets the maximum timeout to wait for a Flutter element in an integration test. Default timeout is 5000ms diff --git a/appium/options/flutter_integration/flutter_server_launch_timeout_option.py b/appium/options/flutter_integration/flutter_server_launch_timeout_option.py index d387c3c14..32cea2f94 100644 --- a/appium/options/flutter_integration/flutter_server_launch_timeout_option.py +++ b/appium/options/flutter_integration/flutter_server_launch_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class FlutterServerLaunchTimeOutOption(SupportsCapabilities): @property - def flutter_server_launch_timeout(self) -> timedelta | None: + def flutter_server_launch_timeout(self) -> Optional[timedelta]: """ Gets the current timeout for launching the Flutter server in a Flutter application. @@ -35,7 +36,7 @@ def flutter_server_launch_timeout(self) -> timedelta | None: return self.get_capability(FLUTTER_SERVER_LAUNCH_TIMEOUT) @flutter_server_launch_timeout.setter - def flutter_server_launch_timeout(self, value: timedelta | int) -> None: + def flutter_server_launch_timeout(self, value: Union[timedelta, int]) -> None: """ Sets the timeout for launching the Flutter server in Flutter application. Default timeout is 5000ms diff --git a/appium/options/flutter_integration/flutter_system_port_option.py b/appium/options/flutter_integration/flutter_system_port_option.py index b71a28a7c..db7ab7f54 100644 --- a/appium/options/flutter_integration/flutter_system_port_option.py +++ b/appium/options/flutter_integration/flutter_system_port_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class FlutterSystemPortOption(SupportsCapabilities): @property - def flutter_system_port(self) -> int | None: + def flutter_system_port(self) -> Optional[int]: """ Get flutter system port for Flutter integration tests. diff --git a/appium/options/gecko/android_storage_option.py b/appium/options/gecko/android_storage_option.py index 9827ce25a..1209ac501 100644 --- a/appium/options/gecko/android_storage_option.py +++ b/appium/options/gecko/android_storage_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AndroidStorageOption(SupportsCapabilities): @property - def android_storage(self) -> str | None: + def android_storage(self) -> Optional[str]: """ The currently set storage type. """ diff --git a/appium/options/gecko/base.py b/appium/options/gecko/base.py index dc4723e2e..873e5646a 100644 --- a/appium/options/gecko/base.py +++ b/appium/options/gecko/base.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict from appium.options.common.automation_name_option import AUTOMATION_NAME from appium.options.common.base import AppiumOptions @@ -44,7 +45,7 @@ def system_port(self, value: int) -> None: SystemPortOption.system_port.fset(self, value) # type: ignore @property - def default_capabilities(self) -> dict: + def default_capabilities(self) -> Dict: return { AUTOMATION_NAME: 'Gecko', } diff --git a/appium/options/gecko/firefox_options_option.py b/appium/options/gecko/firefox_options_option.py index da3206cff..87906b4bb 100644 --- a/appium/options/gecko/firefox_options_option.py +++ b/appium/options/gecko/firefox_options_option.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -from typing import Any +from typing import Any, Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,14 +24,14 @@ class FirefoxOptionsOption(SupportsCapabilities): @property - def firefox_options(self) -> dict[str, Any] | None: + def firefox_options(self) -> Optional[Dict[str, Any]]: """ Firefox options mapping. """ return self.get_capability(FIREFOX_OPTIONS) @firefox_options.setter - def firefox_options(self, value: dict[str, Any]) -> None: + def firefox_options(self, value: Dict[str, Any]) -> None: """ See https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions """ diff --git a/appium/options/gecko/marionette_port_option.py b/appium/options/gecko/marionette_port_option.py index ac9e4071a..24846fd76 100644 --- a/appium/options/gecko/marionette_port_option.py +++ b/appium/options/gecko/marionette_port_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class MarionettePortOption(SupportsCapabilities): @property - def marionette_port(self) -> int | None: + def marionette_port(self) -> Optional[int]: """ The number of the port for the Marionette server to listen on. """ diff --git a/appium/options/gecko/verbosity_option.py b/appium/options/gecko/verbosity_option.py index 4b6b6548f..9888ddf2d 100644 --- a/appium/options/gecko/verbosity_option.py +++ b/appium/options/gecko/verbosity_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class VerbosityOption(SupportsCapabilities): @property - def verbosity(self) -> str | None: + def verbosity(self) -> Optional[str]: """ The verbosity level of driver logging. """ diff --git a/appium/options/ios/safari/automatic_inspection_option.py b/appium/options/ios/safari/automatic_inspection_option.py index a92039fa0..3ab38e82d 100644 --- a/appium/options/ios/safari/automatic_inspection_option.py +++ b/appium/options/ios/safari/automatic_inspection_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AutomaticInspectionOption(SupportsCapabilities): @property - def automatic_inspection(self) -> bool | None: + def automatic_inspection(self) -> Optional[bool]: """ Whether to use automatic inspection. """ diff --git a/appium/options/ios/safari/automatic_profiling_option.py b/appium/options/ios/safari/automatic_profiling_option.py index 67a81a9b1..76eae6206 100644 --- a/appium/options/ios/safari/automatic_profiling_option.py +++ b/appium/options/ios/safari/automatic_profiling_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AutomaticProfilingOption(SupportsCapabilities): @property - def automatic_profiling(self) -> bool | None: + def automatic_profiling(self) -> Optional[bool]: """ Whether to use automatic profiling. """ diff --git a/appium/options/ios/safari/base.py b/appium/options/ios/safari/base.py index f46bc9f59..8c6cfa685 100644 --- a/appium/options/ios/safari/base.py +++ b/appium/options/ios/safari/base.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict from appium.options.common.automation_name_option import AUTOMATION_NAME from appium.options.common.base import PLATFORM_NAME, AppiumOptions @@ -43,7 +44,7 @@ class SafariOptions( WebkitWebrtcOption, ): @property - def default_capabilities(self) -> dict: + def default_capabilities(self) -> Dict: return { PLATFORM_NAME: 'iOS', AUTOMATION_NAME: 'Safari', diff --git a/appium/options/ios/safari/device_name_option.py b/appium/options/ios/safari/device_name_option.py index 28deafef3..ebc949ad8 100644 --- a/appium/options/ios/safari/device_name_option.py +++ b/appium/options/ios/safari/device_name_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class DeviceNameOption(SupportsCapabilities): @property - def device_name(self) -> str | None: + def device_name(self) -> Optional[str]: """ String representing the name of the device. """ diff --git a/appium/options/ios/safari/device_type_option.py b/appium/options/ios/safari/device_type_option.py index 9071c6af4..329974e34 100644 --- a/appium/options/ios/safari/device_type_option.py +++ b/appium/options/ios/safari/device_type_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class DeviceTypeOption(SupportsCapabilities): @property - def device_type(self) -> str | None: + def device_type(self) -> Optional[str]: """ String representing the type of the device. """ diff --git a/appium/options/ios/safari/device_udid_option.py b/appium/options/ios/safari/device_udid_option.py index aec15d20a..12850acfc 100644 --- a/appium/options/ios/safari/device_udid_option.py +++ b/appium/options/ios/safari/device_udid_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class DeviceUdidOption(SupportsCapabilities): @property - def device_udid(self) -> str | None: + def device_udid(self) -> Optional[str]: """ String representing the UDID of the device. """ diff --git a/appium/options/ios/safari/platform_build_version_option.py b/appium/options/ios/safari/platform_build_version_option.py index 7454664a6..f411ec22e 100644 --- a/appium/options/ios/safari/platform_build_version_option.py +++ b/appium/options/ios/safari/platform_build_version_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class PlatformBuildVersionOption(SupportsCapabilities): @property - def platform_build_version(self) -> str | None: + def platform_build_version(self) -> Optional[str]: """ String representing the platform build version. """ diff --git a/appium/options/ios/safari/platform_version_option.py b/appium/options/ios/safari/platform_version_option.py index e53ef140f..fd3d4b60b 100644 --- a/appium/options/ios/safari/platform_version_option.py +++ b/appium/options/ios/safari/platform_version_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class PlatformVersionOption(SupportsCapabilities): @property - def platform_version(self) -> str | None: + def platform_version(self) -> Optional[str]: """ String representing the platform version. """ diff --git a/appium/options/ios/safari/use_simulator_option.py b/appium/options/ios/safari/use_simulator_option.py index 91b9eb391..25a09a6b0 100644 --- a/appium/options/ios/safari/use_simulator_option.py +++ b/appium/options/ios/safari/use_simulator_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UseSimulatorOption(SupportsCapabilities): @property - def use_simulator(self) -> bool | None: + def use_simulator(self) -> Optional[bool]: """ Whether to use iOS Simulator. """ diff --git a/appium/options/ios/safari/webkit_webrtc_option.py b/appium/options/ios/safari/webkit_webrtc_option.py index ac0d4c976..7980df7ab 100644 --- a/appium/options/ios/safari/webkit_webrtc_option.py +++ b/appium/options/ios/safari/webkit_webrtc_option.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -from typing import Any +from typing import Any, Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,14 +24,14 @@ class WebkitWebrtcOption(SupportsCapabilities): @property - def webkit_webrtc(self) -> dict[str, Any] | None: + def webkit_webrtc(self) -> Optional[Dict[str, Any]]: """ WebRTC policies. """ return self.get_capability(WEBKIT_WEBRTC) @webkit_webrtc.setter - def webkit_webrtc(self, value: dict[str, Any]) -> None: + def webkit_webrtc(self, value: Dict[str, Any]) -> None: """ This option allows a test to temporarily change Safari's policies for WebRTC and Media Capture. diff --git a/appium/options/ios/xcuitest/app/app_install_strategy_option.py b/appium/options/ios/xcuitest/app/app_install_strategy_option.py index 3ce9f8554..ff812fe86 100644 --- a/appium/options/ios/xcuitest/app/app_install_strategy_option.py +++ b/appium/options/ios/xcuitest/app/app_install_strategy_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppInstallStrategyOption(SupportsCapabilities): @property - def app_install_strategy(self) -> str | None: + def app_install_strategy(self) -> Optional[str]: """ App install strategy. """ diff --git a/appium/options/ios/xcuitest/app/app_push_timeout_option.py b/appium/options/ios/xcuitest/app/app_push_timeout_option.py index a135b853b..3f49bde96 100644 --- a/appium/options/ios/xcuitest/app/app_push_timeout_option.py +++ b/appium/options/ios/xcuitest/app/app_push_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class AppPushTimeoutOption(SupportsCapabilities): @property - def app_push_timeout(self) -> timedelta | None: + def app_push_timeout(self) -> Optional[timedelta]: """ Maximum timeout for application upload. """ @@ -32,7 +33,7 @@ def app_push_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @app_push_timeout.setter - def app_push_timeout(self, value: timedelta | int) -> None: + def app_push_timeout(self, value: Union[timedelta, int]) -> None: """ The timeout for application upload. Works for real devices only. diff --git a/appium/options/ios/xcuitest/app/localizable_strings_dir_option.py b/appium/options/ios/xcuitest/app/localizable_strings_dir_option.py index 21c985e49..e78155a6c 100644 --- a/appium/options/ios/xcuitest/app/localizable_strings_dir_option.py +++ b/appium/options/ios/xcuitest/app/localizable_strings_dir_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class LocalizableStringsDirOption(SupportsCapabilities): @property - def localizable_strings_dir(self) -> str | None: + def localizable_strings_dir(self) -> Optional[str]: """ Resource folder name where the main locale strings are stored. """ diff --git a/appium/options/ios/xcuitest/base.py b/appium/options/ios/xcuitest/base.py index 75ad7aab4..0d349934c 100644 --- a/appium/options/ios/xcuitest/base.py +++ b/appium/options/ios/xcuitest/base.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict from appium.options.common.app_option import AppOption from appium.options.common.auto_web_view_option import AutoWebViewOption @@ -219,7 +220,7 @@ class XCUITestOptions( WebviewConnectTimeoutOption, ): @property - def default_capabilities(self) -> dict: + def default_capabilities(self) -> Dict: return { AUTOMATION_NAME: 'XCUITest', PLATFORM_NAME: 'iOS', diff --git a/appium/options/ios/xcuitest/general/include_device_caps_to_session_info_option.py b/appium/options/ios/xcuitest/general/include_device_caps_to_session_info_option.py index 388f90ccd..b5df6d286 100644 --- a/appium/options/ios/xcuitest/general/include_device_caps_to_session_info_option.py +++ b/appium/options/ios/xcuitest/general/include_device_caps_to_session_info_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class IncludeDeviceCapsToSessionInfoOption(SupportsCapabilities): @property - def include_device_caps_to_session_info(self) -> bool | None: + def include_device_caps_to_session_info(self) -> Optional[bool]: """ Whether to include screen information as the result of Get Session Capabilities. """ diff --git a/appium/options/ios/xcuitest/general/reset_location_service_option.py b/appium/options/ios/xcuitest/general/reset_location_service_option.py index 0c70dee46..c9e242cc5 100644 --- a/appium/options/ios/xcuitest/general/reset_location_service_option.py +++ b/appium/options/ios/xcuitest/general/reset_location_service_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ResetLocationServiceOption(SupportsCapabilities): @property - def reset_location_service(self) -> bool | None: + def reset_location_service(self) -> Optional[bool]: """ Whether to reset the location service in the session deletion on real devices. """ diff --git a/appium/options/ios/xcuitest/other/command_timeouts_option.py b/appium/options/ios/xcuitest/other/command_timeouts_option.py index f48c52de2..420b35039 100644 --- a/appium/options/ios/xcuitest/other/command_timeouts_option.py +++ b/appium/options/ios/xcuitest/other/command_timeouts_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Dict, Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class CommandTimeoutsOption(SupportsCapabilities): @property - def command_timeouts(self) -> dict[str, timedelta] | timedelta | None: + def command_timeouts(self) -> Optional[Union[Dict[str, timedelta], timedelta]]: """ Custom timeout(s) for WDA backend commands execution. """ @@ -36,7 +37,7 @@ def command_timeouts(self) -> dict[str, timedelta] | timedelta | None: return timedelta(milliseconds=int(value)) @command_timeouts.setter - def command_timeouts(self, value: dict[str, timedelta] | timedelta | int) -> None: + def command_timeouts(self, value: Union[Dict[str, timedelta], timedelta, int]) -> None: """ Custom timeout for all WDA backend commands execution. This might be useful if WDA backend freezes unexpectedly or requires too diff --git a/appium/options/ios/xcuitest/other/launch_with_idb_option.py b/appium/options/ios/xcuitest/other/launch_with_idb_option.py index 9a2a99d8b..c8701c620 100644 --- a/appium/options/ios/xcuitest/other/launch_with_idb_option.py +++ b/appium/options/ios/xcuitest/other/launch_with_idb_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class LaunchWithIdbOption(SupportsCapabilities): @property - def launch_with_idb(self) -> bool | None: + def launch_with_idb(self) -> Optional[bool]: """ Whether to launch WebDriverAgentRunner with idb instead of xcodebuild. """ diff --git a/appium/options/ios/xcuitest/other/show_ios_log_option.py b/appium/options/ios/xcuitest/other/show_ios_log_option.py index fba71a83a..b4bf75d3a 100644 --- a/appium/options/ios/xcuitest/other/show_ios_log_option.py +++ b/appium/options/ios/xcuitest/other/show_ios_log_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ShowIosLogOption(SupportsCapabilities): @property - def show_ios_log(self) -> bool | None: + def show_ios_log(self) -> Optional[bool]: """ Whether to show any logs captured from a device in the appium logs. """ diff --git a/appium/options/ios/xcuitest/other/use_json_source_option.py b/appium/options/ios/xcuitest/other/use_json_source_option.py index 48b8d20a5..1eac50bb8 100644 --- a/appium/options/ios/xcuitest/other/use_json_source_option.py +++ b/appium/options/ios/xcuitest/other/use_json_source_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UseJsonSourceOption(SupportsCapabilities): @property - def use_json_source(self) -> bool | None: + def use_json_source(self) -> Optional[bool]: """ Whether to get JSON source from WDA and transform it to XML on the driver side. """ diff --git a/appium/options/ios/xcuitest/simulator/calendar_access_authorized_option.py b/appium/options/ios/xcuitest/simulator/calendar_access_authorized_option.py index a9a4e08a1..a0e1afe9b 100644 --- a/appium/options/ios/xcuitest/simulator/calendar_access_authorized_option.py +++ b/appium/options/ios/xcuitest/simulator/calendar_access_authorized_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class CalendarAccessAuthorizedOption(SupportsCapabilities): @property - def calendar_access_authorized(self) -> bool | None: + def calendar_access_authorized(self) -> Optional[bool]: """ Whether to enable calendar access on IOS Simulator. """ diff --git a/appium/options/ios/xcuitest/simulator/calendar_format_option.py b/appium/options/ios/xcuitest/simulator/calendar_format_option.py index 2d5c96519..4e7565987 100644 --- a/appium/options/ios/xcuitest/simulator/calendar_format_option.py +++ b/appium/options/ios/xcuitest/simulator/calendar_format_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class CalendarFormatOption(SupportsCapabilities): @property - def calendar_format(self) -> str | None: + def calendar_format(self) -> Optional[str]: """ Calendar format for the iOS Simulator. """ diff --git a/appium/options/ios/xcuitest/simulator/connect_hardware_keyboard_option.py b/appium/options/ios/xcuitest/simulator/connect_hardware_keyboard_option.py index 944a22627..f7118d6de 100644 --- a/appium/options/ios/xcuitest/simulator/connect_hardware_keyboard_option.py +++ b/appium/options/ios/xcuitest/simulator/connect_hardware_keyboard_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ConnectHardwareKeyboardOption(SupportsCapabilities): @property - def connect_hardware_keyboard(self) -> bool | None: + def connect_hardware_keyboard(self) -> Optional[bool]: """ Whether to connect hardware keyboard to Simulator. """ diff --git a/appium/options/ios/xcuitest/simulator/custom_ssl_cert_option.py b/appium/options/ios/xcuitest/simulator/custom_ssl_cert_option.py index 131e04283..a3914b939 100644 --- a/appium/options/ios/xcuitest/simulator/custom_ssl_cert_option.py +++ b/appium/options/ios/xcuitest/simulator/custom_ssl_cert_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class CustomSslCertOption(SupportsCapabilities): @property - def custom_ssl_cert(self) -> str | None: + def custom_ssl_cert(self) -> Optional[str]: """ SSL certificate content. """ diff --git a/appium/options/ios/xcuitest/simulator/enforce_fresh_simulator_creation_option.py b/appium/options/ios/xcuitest/simulator/enforce_fresh_simulator_creation_option.py index ceb44d376..cea87eb05 100644 --- a/appium/options/ios/xcuitest/simulator/enforce_fresh_simulator_creation_option.py +++ b/appium/options/ios/xcuitest/simulator/enforce_fresh_simulator_creation_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class EnforceFreshSimulatorCreationOption(SupportsCapabilities): @property - def enforce_fresh_simulator_creation(self) -> bool | None: + def enforce_fresh_simulator_creation(self) -> Optional[bool]: """ Whether to create a new simulator for each new test session. """ diff --git a/appium/options/ios/xcuitest/simulator/force_simulator_software_keyboard_presence_option.py b/appium/options/ios/xcuitest/simulator/force_simulator_software_keyboard_presence_option.py index 62915e91b..1e1a697aa 100644 --- a/appium/options/ios/xcuitest/simulator/force_simulator_software_keyboard_presence_option.py +++ b/appium/options/ios/xcuitest/simulator/force_simulator_software_keyboard_presence_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ForceSimulatorSoftwareKeyboardPresenceOption(SupportsCapabilities): @property - def force_simulator_software_keyboard_presence(self) -> bool | None: + def force_simulator_software_keyboard_presence(self) -> Optional[bool]: """ Whether to enforce software keyboard presence. """ diff --git a/appium/options/ios/xcuitest/simulator/ios_simulator_logs_predicate_option.py b/appium/options/ios/xcuitest/simulator/ios_simulator_logs_predicate_option.py index 6e67354aa..c7d486477 100644 --- a/appium/options/ios/xcuitest/simulator/ios_simulator_logs_predicate_option.py +++ b/appium/options/ios/xcuitest/simulator/ios_simulator_logs_predicate_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class IosSimulatorLogsPredicateOption(SupportsCapabilities): @property - def ios_simulator_logs_predicate(self) -> bool | None: + def ios_simulator_logs_predicate(self) -> Optional[bool]: """ Get Simulator log filtering predicate. """ diff --git a/appium/options/ios/xcuitest/simulator/keep_key_chains_option.py b/appium/options/ios/xcuitest/simulator/keep_key_chains_option.py index 9395b30b1..a304c935e 100644 --- a/appium/options/ios/xcuitest/simulator/keep_key_chains_option.py +++ b/appium/options/ios/xcuitest/simulator/keep_key_chains_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class KeepKeyChainsOption(SupportsCapabilities): @property - def keep_key_chains(self) -> bool | None: + def keep_key_chains(self) -> Optional[bool]: """ Whether to preserve Simulator keychains after full reset. """ diff --git a/appium/options/ios/xcuitest/simulator/keychains_exclude_patterns_option.py b/appium/options/ios/xcuitest/simulator/keychains_exclude_patterns_option.py index ddfba4223..d971f5c5c 100644 --- a/appium/options/ios/xcuitest/simulator/keychains_exclude_patterns_option.py +++ b/appium/options/ios/xcuitest/simulator/keychains_exclude_patterns_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class KeychainsExcludePatternsOption(SupportsCapabilities): @property - def keychains_exclude_patterns(self) -> str | None: + def keychains_exclude_patterns(self) -> Optional[str]: """ Keychains exclude patterns. """ diff --git a/appium/options/ios/xcuitest/simulator/permissions_option.py b/appium/options/ios/xcuitest/simulator/permissions_option.py index 1957273d6..6146e382b 100644 --- a/appium/options/ios/xcuitest/simulator/permissions_option.py +++ b/appium/options/ios/xcuitest/simulator/permissions_option.py @@ -16,6 +16,7 @@ # under the License. import json +from typing import Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class PermissionsOption(SupportsCapabilities): @property - def permissions(self) -> dict[str, dict[str, str]] | None: + def permissions(self) -> Optional[Dict[str, Dict[str, str]]]: """ Get Simulator permissions. """ @@ -32,7 +33,7 @@ def permissions(self) -> dict[str, dict[str, str]] | None: return None if value is None else json.loads(value) @permissions.setter - def permissions(self, value: dict[str, dict[str, str]]) -> None: + def permissions(self, value: Dict[str, Dict[str, str]]) -> None: """ Allows setting of permissions for the specified application bundle on Simulator only. diff --git a/appium/options/ios/xcuitest/simulator/reduce_motion_option.py b/appium/options/ios/xcuitest/simulator/reduce_motion_option.py index 29d0defb0..89dbe8779 100644 --- a/appium/options/ios/xcuitest/simulator/reduce_motion_option.py +++ b/appium/options/ios/xcuitest/simulator/reduce_motion_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ReduceMotionOption(SupportsCapabilities): @property - def reduce_motion(self) -> bool | None: + def reduce_motion(self) -> Optional[bool]: """ Whether to reduce motion accessibility preference. """ diff --git a/appium/options/ios/xcuitest/simulator/reset_on_session_start_only_option.py b/appium/options/ios/xcuitest/simulator/reset_on_session_start_only_option.py index ce6b4d82f..294a72117 100644 --- a/appium/options/ios/xcuitest/simulator/reset_on_session_start_only_option.py +++ b/appium/options/ios/xcuitest/simulator/reset_on_session_start_only_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ResetOnSessionStartOnlyOption(SupportsCapabilities): @property - def reset_on_session_start_only(self) -> bool | None: + def reset_on_session_start_only(self) -> Optional[bool]: """ Whether to perform Simulator reset on test session finish (false) or not (true). """ diff --git a/appium/options/ios/xcuitest/simulator/scale_factor_option.py b/appium/options/ios/xcuitest/simulator/scale_factor_option.py index 2d6b528b7..8b7e6e037 100644 --- a/appium/options/ios/xcuitest/simulator/scale_factor_option.py +++ b/appium/options/ios/xcuitest/simulator/scale_factor_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ScaleFactorOption(SupportsCapabilities): @property - def scale_factor(self) -> str | None: + def scale_factor(self) -> Optional[str]: """ Simulator scale factor. """ diff --git a/appium/options/ios/xcuitest/simulator/shutdown_other_simulators_option.py b/appium/options/ios/xcuitest/simulator/shutdown_other_simulators_option.py index 60497919d..a7c5edc51 100644 --- a/appium/options/ios/xcuitest/simulator/shutdown_other_simulators_option.py +++ b/appium/options/ios/xcuitest/simulator/shutdown_other_simulators_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ShutdownOtherSimulatorsOption(SupportsCapabilities): @property - def shutdown_other_simulators(self) -> bool | None: + def shutdown_other_simulators(self) -> Optional[bool]: """ Whether to shut down of other booted simulators except of the current one. """ diff --git a/appium/options/ios/xcuitest/simulator/simulator_devices_set_path_option.py b/appium/options/ios/xcuitest/simulator/simulator_devices_set_path_option.py index 2ac9253e7..845575355 100644 --- a/appium/options/ios/xcuitest/simulator/simulator_devices_set_path_option.py +++ b/appium/options/ios/xcuitest/simulator/simulator_devices_set_path_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SimulatorDevicesSetPathOption(SupportsCapabilities): @property - def simulator_devices_set_path(self) -> str | None: + def simulator_devices_set_path(self) -> Optional[str]: """ Alternative path to the simulator devices set. """ diff --git a/appium/options/ios/xcuitest/simulator/simulator_pasteboard_automatic_sync_option.py b/appium/options/ios/xcuitest/simulator/simulator_pasteboard_automatic_sync_option.py index 435ea5aa8..a1258df4e 100644 --- a/appium/options/ios/xcuitest/simulator/simulator_pasteboard_automatic_sync_option.py +++ b/appium/options/ios/xcuitest/simulator/simulator_pasteboard_automatic_sync_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SimulatorPasteboardAutomaticSyncOption(SupportsCapabilities): @property - def simulator_pasteboard_automatic_sync(self) -> bool | None: + def simulator_pasteboard_automatic_sync(self) -> Optional[bool]: """ Pasteboard automation sync state. """ diff --git a/appium/options/ios/xcuitest/simulator/simulator_startup_timeout_option.py b/appium/options/ios/xcuitest/simulator/simulator_startup_timeout_option.py index e282e229f..29353498d 100644 --- a/appium/options/ios/xcuitest/simulator/simulator_startup_timeout_option.py +++ b/appium/options/ios/xcuitest/simulator/simulator_startup_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class SimulatorStartupTimeoutOption(SupportsCapabilities): @property - def simulator_startup_timeout(self) -> timedelta | None: + def simulator_startup_timeout(self) -> Optional[timedelta]: """ Simulator startup timeout. """ @@ -32,7 +33,7 @@ def simulator_startup_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @simulator_startup_timeout.setter - def simulator_startup_timeout(self, value: timedelta | int) -> None: + def simulator_startup_timeout(self, value: Union[timedelta, int]) -> None: """ Allows to change the default timeout for Simulator startup. By default, this value is set to 120000ms (2 minutes), diff --git a/appium/options/ios/xcuitest/simulator/simulator_trace_pointer_option.py b/appium/options/ios/xcuitest/simulator/simulator_trace_pointer_option.py index 508cad64f..d5f5d1fd8 100644 --- a/appium/options/ios/xcuitest/simulator/simulator_trace_pointer_option.py +++ b/appium/options/ios/xcuitest/simulator/simulator_trace_pointer_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SimulatorTracePointerOption(SupportsCapabilities): @property - def simulator_trace_pointer(self) -> bool | None: + def simulator_trace_pointer(self) -> Optional[bool]: """ Whether to highlight pointer moves in the Simulator window. """ diff --git a/appium/options/ios/xcuitest/simulator/simulator_window_center_option.py b/appium/options/ios/xcuitest/simulator/simulator_window_center_option.py index 85c06c9a1..1f8b2d8b8 100644 --- a/appium/options/ios/xcuitest/simulator/simulator_window_center_option.py +++ b/appium/options/ios/xcuitest/simulator/simulator_window_center_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SimulatorWindowCenterOption(SupportsCapabilities): @property - def simulator_window_center(self) -> str | None: + def simulator_window_center(self) -> Optional[str]: """ Simulator window center coordinates. """ diff --git a/appium/options/ios/xcuitest/wda/allow_provisioning_device_regitration_option.py b/appium/options/ios/xcuitest/wda/allow_provisioning_device_regitration_option.py index 504cebf30..c88124b4c 100644 --- a/appium/options/ios/xcuitest/wda/allow_provisioning_device_regitration_option.py +++ b/appium/options/ios/xcuitest/wda/allow_provisioning_device_regitration_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AllowProvisioningDeviceRegistrationOption(SupportsCapabilities): @property - def allow_provisioning_device_registration(self) -> bool | None: + def allow_provisioning_device_registration(self) -> Optional[bool]: """ Whether to allow xcodebuild to register your destination device on the developer portal. """ diff --git a/appium/options/ios/xcuitest/wda/auto_accept_alerts_option.py b/appium/options/ios/xcuitest/wda/auto_accept_alerts_option.py index 59378a857..05c55943e 100644 --- a/appium/options/ios/xcuitest/wda/auto_accept_alerts_option.py +++ b/appium/options/ios/xcuitest/wda/auto_accept_alerts_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AutoAcceptAlertsOption(SupportsCapabilities): @property - def auto_accept_alerts(self) -> bool | None: + def auto_accept_alerts(self) -> Optional[bool]: """ Whether to accept all alerts automatically. """ diff --git a/appium/options/ios/xcuitest/wda/auto_disimiss_alerts_option.py b/appium/options/ios/xcuitest/wda/auto_disimiss_alerts_option.py index 1900274c7..61a28414c 100644 --- a/appium/options/ios/xcuitest/wda/auto_disimiss_alerts_option.py +++ b/appium/options/ios/xcuitest/wda/auto_disimiss_alerts_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AutoDismissAlertsOption(SupportsCapabilities): @property - def auto_dismiss_alerts(self) -> bool | None: + def auto_dismiss_alerts(self) -> Optional[bool]: """ Whether to dismiss all alerts automatically. """ diff --git a/appium/options/ios/xcuitest/wda/derived_data_path_option.py b/appium/options/ios/xcuitest/wda/derived_data_path_option.py index 063df5489..ffa40a682 100644 --- a/appium/options/ios/xcuitest/wda/derived_data_path_option.py +++ b/appium/options/ios/xcuitest/wda/derived_data_path_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class DerivedDataPathOption(SupportsCapabilities): @property - def derived_data_path(self) -> str | None: + def derived_data_path(self) -> Optional[str]: """ Path to the derived data WDA folder. """ diff --git a/appium/options/ios/xcuitest/wda/disable_automatic_screenshots_option.py b/appium/options/ios/xcuitest/wda/disable_automatic_screenshots_option.py index ef17b5d63..ac76af330 100644 --- a/appium/options/ios/xcuitest/wda/disable_automatic_screenshots_option.py +++ b/appium/options/ios/xcuitest/wda/disable_automatic_screenshots_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class DisableAutomaticScreenshotsOption(SupportsCapabilities): @property - def disable_automatic_screenshots(self) -> bool | None: + def disable_automatic_screenshots(self) -> Optional[bool]: """ Whether to disable automatic XCTest screenshots. """ diff --git a/appium/options/ios/xcuitest/wda/force_app_launch_option.py b/appium/options/ios/xcuitest/wda/force_app_launch_option.py index ba2f5d04c..364133d03 100644 --- a/appium/options/ios/xcuitest/wda/force_app_launch_option.py +++ b/appium/options/ios/xcuitest/wda/force_app_launch_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ForceAppLaunchOption(SupportsCapabilities): @property - def force_app_launch(self) -> bool | None: + def force_app_launch(self) -> Optional[bool]: """ Whether to enforce app restart on session startup. """ diff --git a/appium/options/ios/xcuitest/wda/keychain_password_option.py b/appium/options/ios/xcuitest/wda/keychain_password_option.py index e0a2a0914..5514519f0 100644 --- a/appium/options/ios/xcuitest/wda/keychain_password_option.py +++ b/appium/options/ios/xcuitest/wda/keychain_password_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class KeychainPasswordOption(SupportsCapabilities): @property - def keychain_password(self) -> str | None: + def keychain_password(self) -> Optional[str]: """ Custom keychain password. """ diff --git a/appium/options/ios/xcuitest/wda/keychain_path_option.py b/appium/options/ios/xcuitest/wda/keychain_path_option.py index a3b111d8d..5cf397426 100644 --- a/appium/options/ios/xcuitest/wda/keychain_path_option.py +++ b/appium/options/ios/xcuitest/wda/keychain_path_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class KeychainPathOption(SupportsCapabilities): @property - def keychain_path(self) -> str | None: + def keychain_path(self) -> Optional[str]: """ Path to a custom keychain. """ diff --git a/appium/options/ios/xcuitest/wda/max_typing_frequency_option.py b/appium/options/ios/xcuitest/wda/max_typing_frequency_option.py index 0a778c603..d5ea8dea1 100644 --- a/appium/options/ios/xcuitest/wda/max_typing_frequency_option.py +++ b/appium/options/ios/xcuitest/wda/max_typing_frequency_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class MaxTypingFrequencyOption(SupportsCapabilities): @property - def max_typing_frequency(self) -> int | None: + def max_typing_frequency(self) -> Optional[int]: """ The number of keystrokes per minute. """ diff --git a/appium/options/ios/xcuitest/wda/mjpeg_server_port_option.py b/appium/options/ios/xcuitest/wda/mjpeg_server_port_option.py index dedd43ee0..679c1af4a 100644 --- a/appium/options/ios/xcuitest/wda/mjpeg_server_port_option.py +++ b/appium/options/ios/xcuitest/wda/mjpeg_server_port_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class MjpegServerPortOption(SupportsCapabilities): @property - def mjpeg_server_port(self) -> int | None: + def mjpeg_server_port(self) -> Optional[int]: """ Port number on which WDA broadcasts screenshots stream encoded into MJPEG format from the device under test. diff --git a/appium/options/ios/xcuitest/wda/prebuilt_wda_path_option.py b/appium/options/ios/xcuitest/wda/prebuilt_wda_path_option.py index 649b5cf69..f26d12f0a 100644 --- a/appium/options/ios/xcuitest/wda/prebuilt_wda_path_option.py +++ b/appium/options/ios/xcuitest/wda/prebuilt_wda_path_option.py @@ -15,6 +15,7 @@ # language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class PrebuiltWdaPathOption(SupportsCapabilities): @property - def prebuilt_wda_path(self) -> str | None: + def prebuilt_wda_path(self) -> Optional[str]: """ The path to the prebuilt WebDriverAgent. """ diff --git a/appium/options/ios/xcuitest/wda/process_arguments_option.py b/appium/options/ios/xcuitest/wda/process_arguments_option.py index 07f7ef126..5b169a945 100644 --- a/appium/options/ios/xcuitest/wda/process_arguments_option.py +++ b/appium/options/ios/xcuitest/wda/process_arguments_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict, List, Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,14 +24,14 @@ class ProcessArgumentsOption(SupportsCapabilities): @property - def process_arguments(self) -> dict[str, list[str] | dict[str, str]] | None: + def process_arguments(self) -> Optional[Dict[str, Union[List[str], Dict[str, str]]]]: """ Command line arguments and/or environment variables of the application under test. """ return self.get_capability(PROCESS_ARGUMENTS) @process_arguments.setter - def process_arguments(self, value: dict[str, list[str] | dict[str, str]]) -> None: + def process_arguments(self, value: Dict[str, Union[List[str], Dict[str, str]]]) -> None: """ Provides process arguments and environment which will be sent to the WebDriverAgent server. Acceptable dictionary keys are 'env' diff --git a/appium/options/ios/xcuitest/wda/result_bundle_path_option.py b/appium/options/ios/xcuitest/wda/result_bundle_path_option.py index f3e470acc..5cf7ec098 100644 --- a/appium/options/ios/xcuitest/wda/result_bundle_path_option.py +++ b/appium/options/ios/xcuitest/wda/result_bundle_path_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ResultBundlePathOption(SupportsCapabilities): @property - def result_bundle_path(self) -> str | None: + def result_bundle_path(self) -> Optional[str]: """ Path where the resulting XCTest bundle should be stored. """ diff --git a/appium/options/ios/xcuitest/wda/screenshot_quality_option.py b/appium/options/ios/xcuitest/wda/screenshot_quality_option.py index bbe052fa2..d808db9d6 100644 --- a/appium/options/ios/xcuitest/wda/screenshot_quality_option.py +++ b/appium/options/ios/xcuitest/wda/screenshot_quality_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ScreenshotQualityOption(SupportsCapabilities): @property - def screenshot_quality(self) -> int | None: + def screenshot_quality(self) -> Optional[int]: """ Screenshot quality value. """ diff --git a/appium/options/ios/xcuitest/wda/should_terminate_app_option.py b/appium/options/ios/xcuitest/wda/should_terminate_app_option.py index 3d9fc07ad..8550627cf 100644 --- a/appium/options/ios/xcuitest/wda/should_terminate_app_option.py +++ b/appium/options/ios/xcuitest/wda/should_terminate_app_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ShouldTerminateAppOption(SupportsCapabilities): @property - def should_terminate_app(self) -> bool | None: + def should_terminate_app(self) -> Optional[bool]: """ Whether to enforce app termination on session quit. """ diff --git a/appium/options/ios/xcuitest/wda/should_use_singleton_test_manager_option.py b/appium/options/ios/xcuitest/wda/should_use_singleton_test_manager_option.py index 6fbf4e893..569236810 100644 --- a/appium/options/ios/xcuitest/wda/should_use_singleton_test_manager_option.py +++ b/appium/options/ios/xcuitest/wda/should_use_singleton_test_manager_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ShouldUseSingletonTestManagerOption(SupportsCapabilities): @property - def should_use_singleton_test_manager(self) -> bool | None: + def should_use_singleton_test_manager(self) -> Optional[bool]: """ Whether to use the default proxy for test management within WebDriverAgent. """ diff --git a/appium/options/ios/xcuitest/wda/show_xcode_log_option.py b/appium/options/ios/xcuitest/wda/show_xcode_log_option.py index 26cd52f9e..0e435b71d 100644 --- a/appium/options/ios/xcuitest/wda/show_xcode_log_option.py +++ b/appium/options/ios/xcuitest/wda/show_xcode_log_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ShowXcodeLogOption(SupportsCapabilities): @property - def show_xcode_log(self) -> bool | None: + def show_xcode_log(self) -> Optional[bool]: """ Whether to display the output of the Xcode command used to run the tests. """ diff --git a/appium/options/ios/xcuitest/wda/simple_is_visible_check_option.py b/appium/options/ios/xcuitest/wda/simple_is_visible_check_option.py index 1f596b0af..694ed5743 100644 --- a/appium/options/ios/xcuitest/wda/simple_is_visible_check_option.py +++ b/appium/options/ios/xcuitest/wda/simple_is_visible_check_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SimpleIsVisibleCheckOption(SupportsCapabilities): @property - def simple_is_visible_check(self) -> bool | None: + def simple_is_visible_check(self) -> Optional[bool]: """ Whether to use native methods for determining visibility of elements. """ diff --git a/appium/options/ios/xcuitest/wda/updated_wda_bundle_id_option.py b/appium/options/ios/xcuitest/wda/updated_wda_bundle_id_option.py index a6f2a8eef..b799c949b 100644 --- a/appium/options/ios/xcuitest/wda/updated_wda_bundle_id_option.py +++ b/appium/options/ios/xcuitest/wda/updated_wda_bundle_id_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UpdatedWdaBundleIdOption(SupportsCapabilities): @property - def updated_wda_bundle_id(self) -> str | None: + def updated_wda_bundle_id(self) -> Optional[str]: """ WDA bundle identifier. """ diff --git a/appium/options/ios/xcuitest/wda/use_native_caching_strategy_option.py b/appium/options/ios/xcuitest/wda/use_native_caching_strategy_option.py index f59bf6067..a3e30de17 100644 --- a/appium/options/ios/xcuitest/wda/use_native_caching_strategy_option.py +++ b/appium/options/ios/xcuitest/wda/use_native_caching_strategy_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UseNativeCachingStrategyOption(SupportsCapabilities): @property - def use_native_caching_strategy(self) -> bool | None: + def use_native_caching_strategy(self) -> Optional[bool]: """ Whether to use the native caching strategy. """ diff --git a/appium/options/ios/xcuitest/wda/use_new_wda_option.py b/appium/options/ios/xcuitest/wda/use_new_wda_option.py index fddfec6b1..f52684a65 100644 --- a/appium/options/ios/xcuitest/wda/use_new_wda_option.py +++ b/appium/options/ios/xcuitest/wda/use_new_wda_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UseNewWdaOption(SupportsCapabilities): @property - def use_new_wda(self) -> bool | None: + def use_new_wda(self) -> Optional[bool]: """ Whether whether to uninstall of any existing WebDriverAgent app on the device under test. diff --git a/appium/options/ios/xcuitest/wda/use_prebuilt_wda_option.py b/appium/options/ios/xcuitest/wda/use_prebuilt_wda_option.py index 4d1c8d1bf..76fd5163e 100644 --- a/appium/options/ios/xcuitest/wda/use_prebuilt_wda_option.py +++ b/appium/options/ios/xcuitest/wda/use_prebuilt_wda_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UsePrebuiltWdaOption(SupportsCapabilities): @property - def use_prebuilt_wda(self) -> bool | None: + def use_prebuilt_wda(self) -> Optional[bool]: """ Whether to skip the build phase of running the WDA app. """ diff --git a/appium/options/ios/xcuitest/wda/use_preinstalled_wda_option.py b/appium/options/ios/xcuitest/wda/use_preinstalled_wda_option.py index 0ea313a91..547d87f90 100644 --- a/appium/options/ios/xcuitest/wda/use_preinstalled_wda_option.py +++ b/appium/options/ios/xcuitest/wda/use_preinstalled_wda_option.py @@ -15,6 +15,7 @@ # language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UsePreinstalledWdaOption(SupportsCapabilities): @property - def use_preinstalled_wda(self) -> bool | None: + def use_preinstalled_wda(self) -> Optional[bool]: """ Whether to use a preinstalled WebDriverAgent. """ diff --git a/appium/options/ios/xcuitest/wda/use_simple_build_test_option.py b/appium/options/ios/xcuitest/wda/use_simple_build_test_option.py index 86cba52a5..516e08cbe 100644 --- a/appium/options/ios/xcuitest/wda/use_simple_build_test_option.py +++ b/appium/options/ios/xcuitest/wda/use_simple_build_test_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UseSimpleBuildTestOption(SupportsCapabilities): @property - def use_simple_build_test(self) -> bool | None: + def use_simple_build_test(self) -> Optional[bool]: """ Whether to enforce app termination on session quit. """ diff --git a/appium/options/ios/xcuitest/wda/use_xctestrun_file_option.py b/appium/options/ios/xcuitest/wda/use_xctestrun_file_option.py index cb74886fc..0d1a3c64b 100644 --- a/appium/options/ios/xcuitest/wda/use_xctestrun_file_option.py +++ b/appium/options/ios/xcuitest/wda/use_xctestrun_file_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class UseXctestrunFileOption(SupportsCapabilities): @property - def use_xctestrun_file(self) -> bool | None: + def use_xctestrun_file(self) -> Optional[bool]: """ Whether to use of .xctestrun file to launch WDA. """ diff --git a/appium/options/ios/xcuitest/wda/wait_for_idle_timeout_option.py b/appium/options/ios/xcuitest/wda/wait_for_idle_timeout_option.py index 06a0420a4..ff994d43e 100644 --- a/appium/options/ios/xcuitest/wda/wait_for_idle_timeout_option.py +++ b/appium/options/ios/xcuitest/wda/wait_for_idle_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class WaitForIdleTimeoutOption(SupportsCapabilities): @property - def wait_for_idle_timeout(self) -> timedelta | None: + def wait_for_idle_timeout(self) -> Optional[timedelta]: """ Maximum timeout to wait until WDA responds to HTTP requests. """ @@ -32,7 +33,7 @@ def wait_for_idle_timeout(self) -> timedelta | None: return None if value is None else timedelta(seconds=value) @wait_for_idle_timeout.setter - def wait_for_idle_timeout(self, value: timedelta | float) -> None: + def wait_for_idle_timeout(self, value: Union[timedelta, float]) -> None: """ The time to wait until the application under test is idling. XCTest requires the app's main thread to be idling in order to execute any action on it, diff --git a/appium/options/ios/xcuitest/wda/wait_for_quiescence_option.py b/appium/options/ios/xcuitest/wda/wait_for_quiescence_option.py index d4e108940..b8be7f322 100644 --- a/appium/options/ios/xcuitest/wda/wait_for_quiescence_option.py +++ b/appium/options/ios/xcuitest/wda/wait_for_quiescence_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,14 +25,14 @@ class WaitForQuiescenceOption(SupportsCapabilities): @property - def wait_for_quiescence(self) -> bool | None: + def wait_for_quiescence(self) -> Optional[bool]: """ Whether to wait for application quiescence. """ return self.get_capability(WAIT_FOR_QUIESCENCE) @wait_for_quiescence.setter - def wait_for_quiescence(self, value: timedelta | float) -> None: + def wait_for_quiescence(self, value: Union[timedelta, float]) -> None: """ It allows to turn on/off waiting for application quiescence in WebDriverAgent, while performing queries. The default value is true. You can avoid this kind diff --git a/appium/options/ios/xcuitest/wda/wda_base_url_option.py b/appium/options/ios/xcuitest/wda/wda_base_url_option.py index f777aa510..c2ca5cd81 100644 --- a/appium/options/ios/xcuitest/wda/wda_base_url_option.py +++ b/appium/options/ios/xcuitest/wda/wda_base_url_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class WdaBaseUrlOption(SupportsCapabilities): @property - def wda_base_url(self) -> str | None: + def wda_base_url(self) -> Optional[str]: """ Prefix to build a custom WebDriverAgent URL. """ diff --git a/appium/options/ios/xcuitest/wda/wda_connection_timeout_option.py b/appium/options/ios/xcuitest/wda/wda_connection_timeout_option.py index 10711810f..e21bb1834 100644 --- a/appium/options/ios/xcuitest/wda/wda_connection_timeout_option.py +++ b/appium/options/ios/xcuitest/wda/wda_connection_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class WdaConnectionTimeoutOption(SupportsCapabilities): @property - def wda_connection_timeout(self) -> timedelta | None: + def wda_connection_timeout(self) -> Optional[timedelta]: """ Maximum timeout to wait until WDA responds to HTTP requests. """ @@ -32,7 +33,7 @@ def wda_connection_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @wda_connection_timeout.setter - def wda_connection_timeout(self, value: timedelta | int) -> None: + def wda_connection_timeout(self, value: Union[timedelta, int]) -> None: """ Connection timeout to wait for a response from WebDriverAgent. Defaults to 240000ms. diff --git a/appium/options/ios/xcuitest/wda/wda_eventloop_idle_delay_option.py b/appium/options/ios/xcuitest/wda/wda_eventloop_idle_delay_option.py index f6661da88..e957f2690 100644 --- a/appium/options/ios/xcuitest/wda/wda_eventloop_idle_delay_option.py +++ b/appium/options/ios/xcuitest/wda/wda_eventloop_idle_delay_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class WdaEventloopIdleDelayOption(SupportsCapabilities): @property - def wda_eventloop_idle_delay(self) -> timedelta | None: + def wda_eventloop_idle_delay(self) -> Optional[timedelta]: """ Event loop idle delay. """ @@ -32,7 +33,7 @@ def wda_eventloop_idle_delay(self) -> timedelta | None: return None if value is None else timedelta(seconds=value) @wda_eventloop_idle_delay.setter - def wda_eventloop_idle_delay(self, value: timedelta | float) -> None: + def wda_eventloop_idle_delay(self, value: Union[timedelta, float]) -> None: """ Delays the invocation of -[XCUIApplicationProcess setEventLoopHasIdled:] by the duration specified with this capability. This can help quiescence apps diff --git a/appium/options/ios/xcuitest/wda/wda_launch_timeout_option.py b/appium/options/ios/xcuitest/wda/wda_launch_timeout_option.py index cbc911c4b..325b7e40e 100644 --- a/appium/options/ios/xcuitest/wda/wda_launch_timeout_option.py +++ b/appium/options/ios/xcuitest/wda/wda_launch_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class WdaLaunchTimeoutOption(SupportsCapabilities): @property - def wda_launch_timeout(self) -> timedelta | None: + def wda_launch_timeout(self) -> Optional[timedelta]: """ Maximum timeout to wait until WDA is listening. """ @@ -32,7 +33,7 @@ def wda_launch_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @wda_launch_timeout.setter - def wda_launch_timeout(self, value: timedelta | int) -> None: + def wda_launch_timeout(self, value: Union[timedelta, int]) -> None: """ Timeout to wait for WebDriverAgent to be pingable, after its building is finished. Defaults to 60000ms. diff --git a/appium/options/ios/xcuitest/wda/wda_local_port_option.py b/appium/options/ios/xcuitest/wda/wda_local_port_option.py index b0ca7031e..1af241a35 100644 --- a/appium/options/ios/xcuitest/wda/wda_local_port_option.py +++ b/appium/options/ios/xcuitest/wda/wda_local_port_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class WdaLocalPortOption(SupportsCapabilities): @property - def wda_local_port(self) -> int | None: + def wda_local_port(self) -> Optional[int]: """ Local port number where the WDA traffic is being forwarded. """ diff --git a/appium/options/ios/xcuitest/wda/wda_startup_retries_option.py b/appium/options/ios/xcuitest/wda/wda_startup_retries_option.py index 00de25d4c..a70999621 100644 --- a/appium/options/ios/xcuitest/wda/wda_startup_retries_option.py +++ b/appium/options/ios/xcuitest/wda/wda_startup_retries_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class WdaStartupRetriesOption(SupportsCapabilities): @property - def wda_startup_retries(self) -> int | None: + def wda_startup_retries(self) -> Optional[int]: """ Number of retries before to fail WDA deployment. """ diff --git a/appium/options/ios/xcuitest/wda/wda_startup_retry_interval_option.py b/appium/options/ios/xcuitest/wda/wda_startup_retry_interval_option.py index 53313e895..99d89daa6 100644 --- a/appium/options/ios/xcuitest/wda/wda_startup_retry_interval_option.py +++ b/appium/options/ios/xcuitest/wda/wda_startup_retry_interval_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class WdaStartupRetryIntervalOption(SupportsCapabilities): @property - def wda_startup_retry_interval(self) -> timedelta | None: + def wda_startup_retry_interval(self) -> Optional[timedelta]: """ Interval to wait between tries to build and launch WebDriverAgent. """ @@ -32,7 +33,7 @@ def wda_startup_retry_interval(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @wda_startup_retry_interval.setter - def wda_startup_retry_interval(self, value: timedelta | int) -> None: + def wda_startup_retry_interval(self, value: Union[timedelta, int]) -> None: """ Time interval to wait between tries to build and launch WebDriverAgent. Defaults to 10000ms. diff --git a/appium/options/ios/xcuitest/wda/web_driver_agent_url_option.py b/appium/options/ios/xcuitest/wda/web_driver_agent_url_option.py index b453bee38..5dd89f2c6 100644 --- a/appium/options/ios/xcuitest/wda/web_driver_agent_url_option.py +++ b/appium/options/ios/xcuitest/wda/web_driver_agent_url_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class WebDriverAgentUrlOption(SupportsCapabilities): @property - def web_driver_agent_url(self) -> str | None: + def web_driver_agent_url(self) -> Optional[str]: """ WedDriverAgent URL. """ diff --git a/appium/options/ios/xcuitest/wda/xcode_org_id_option.py b/appium/options/ios/xcuitest/wda/xcode_org_id_option.py index 1ddb3f1c7..d5c517935 100644 --- a/appium/options/ios/xcuitest/wda/xcode_org_id_option.py +++ b/appium/options/ios/xcuitest/wda/xcode_org_id_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class XcodeOrgIdOption(SupportsCapabilities): @property - def xcode_org_id(self) -> str | None: + def xcode_org_id(self) -> Optional[str]: """ Signing certificate organization id for WebDriverAgent compilation. """ diff --git a/appium/options/ios/xcuitest/wda/xcode_signing_id_option.py b/appium/options/ios/xcuitest/wda/xcode_signing_id_option.py index ce1877cb6..08ba1855e 100644 --- a/appium/options/ios/xcuitest/wda/xcode_signing_id_option.py +++ b/appium/options/ios/xcuitest/wda/xcode_signing_id_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class XcodeSigningIdOption(SupportsCapabilities): @property - def xcode_signing_id(self) -> str | None: + def xcode_signing_id(self) -> Optional[str]: """ Signing certificate for WebDriverAgent compilation. """ diff --git a/appium/options/ios/xcuitest/webview/absolute_web_locations_option.py b/appium/options/ios/xcuitest/webview/absolute_web_locations_option.py index 50ffdc67d..114c737dc 100644 --- a/appium/options/ios/xcuitest/webview/absolute_web_locations_option.py +++ b/appium/options/ios/xcuitest/webview/absolute_web_locations_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AbsoluteWebLocationsOption(SupportsCapabilities): @property - def absolute_web_locations(self) -> bool | None: + def absolute_web_locations(self) -> Optional[bool]: """ Whether Get Element Location returns coordinates relative to the page origin for web view elements. diff --git a/appium/options/ios/xcuitest/webview/additional_webview_bundle_ids_option.py b/appium/options/ios/xcuitest/webview/additional_webview_bundle_ids_option.py index 1e75499da..6546c548f 100644 --- a/appium/options/ios/xcuitest/webview/additional_webview_bundle_ids_option.py +++ b/appium/options/ios/xcuitest/webview/additional_webview_bundle_ids_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import List, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,14 +24,14 @@ class AdditionalWebviewBundleIdsOption(SupportsCapabilities): @property - def additional_webview_bundle_ids(self) -> list[str] | None: + def additional_webview_bundle_ids(self) -> Optional[List[str]]: """ Array of possible bundle identifiers for webviews. """ return self.get_capability(ADDITIONAL_WEBVIEW_BUNDLE_IDS) @additional_webview_bundle_ids.setter - def additional_webview_bundle_ids(self, value: list[str]) -> None: + def additional_webview_bundle_ids(self, value: List[str]) -> None: """ Array of possible bundle identifiers for webviews. This is sometimes necessary if the Web Inspector is found to be returning a modified diff --git a/appium/options/ios/xcuitest/webview/enable_async_execute_from_https_option.py b/appium/options/ios/xcuitest/webview/enable_async_execute_from_https_option.py index d76d24544..ae2a3dcc1 100644 --- a/appium/options/ios/xcuitest/webview/enable_async_execute_from_https_option.py +++ b/appium/options/ios/xcuitest/webview/enable_async_execute_from_https_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class EnableAsyncExecuteFromHttpsOption(SupportsCapabilities): @property - def enable_async_execute_from_https(self) -> bool | None: + def enable_async_execute_from_https(self) -> Optional[bool]: """ Whether to allow simulators to execute async JavaScript on pages using HTTPS. """ diff --git a/appium/options/ios/xcuitest/webview/full_context_list_option.py b/appium/options/ios/xcuitest/webview/full_context_list_option.py index c58e73e1f..7bdd3102d 100644 --- a/appium/options/ios/xcuitest/webview/full_context_list_option.py +++ b/appium/options/ios/xcuitest/webview/full_context_list_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class FullContextListOption(SupportsCapabilities): @property - def full_context_list(self) -> bool | None: + def full_context_list(self) -> Optional[bool]: """ Whether to return the detailed information on contexts for the get available context command. diff --git a/appium/options/ios/xcuitest/webview/include_safari_in_webviews_option.py b/appium/options/ios/xcuitest/webview/include_safari_in_webviews_option.py index ff13eb423..c21cb141c 100644 --- a/appium/options/ios/xcuitest/webview/include_safari_in_webviews_option.py +++ b/appium/options/ios/xcuitest/webview/include_safari_in_webviews_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class IncludeSafariInWebviewsOption(SupportsCapabilities): @property - def include_safari_in_webviews(self) -> bool | None: + def include_safari_in_webviews(self) -> Optional[bool]: """ Whether to add Safari web views to the list of contexts available during a native/webview app test. diff --git a/appium/options/ios/xcuitest/webview/native_web_tap_option.py b/appium/options/ios/xcuitest/webview/native_web_tap_option.py index 3ca218eb9..02a40b98e 100644 --- a/appium/options/ios/xcuitest/webview/native_web_tap_option.py +++ b/appium/options/ios/xcuitest/webview/native_web_tap_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class NativeWebTapOption(SupportsCapabilities): @property - def native_web_tap(self) -> bool | None: + def native_web_tap(self) -> Optional[bool]: """ Whether to enable native taps in web view mode. """ diff --git a/appium/options/ios/xcuitest/webview/safari_garbage_collect_option.py b/appium/options/ios/xcuitest/webview/safari_garbage_collect_option.py index 696a19137..63aefc4c4 100644 --- a/appium/options/ios/xcuitest/webview/safari_garbage_collect_option.py +++ b/appium/options/ios/xcuitest/webview/safari_garbage_collect_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SafariGarbageCollectOption(SupportsCapabilities): @property - def safari_garbage_collect(self) -> bool | None: + def safari_garbage_collect(self) -> Optional[bool]: """ Whether to turn on garbage collection when executing scripts on Safari. """ diff --git a/appium/options/ios/xcuitest/webview/safari_ignore_fraud_warning_option.py b/appium/options/ios/xcuitest/webview/safari_ignore_fraud_warning_option.py index 38f6cc2bb..c600285d0 100644 --- a/appium/options/ios/xcuitest/webview/safari_ignore_fraud_warning_option.py +++ b/appium/options/ios/xcuitest/webview/safari_ignore_fraud_warning_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SafariIgnoreFraudWarningOption(SupportsCapabilities): @property - def safari_ignore_fraud_warning(self) -> bool | None: + def safari_ignore_fraud_warning(self) -> Optional[bool]: """ Whether to prevent Safari from showing a fraudulent website warning. """ diff --git a/appium/options/ios/xcuitest/webview/safari_ignore_web_hostnames_option.py b/appium/options/ios/xcuitest/webview/safari_ignore_web_hostnames_option.py index e8f9052f3..9f48f8cf3 100644 --- a/appium/options/ios/xcuitest/webview/safari_ignore_web_hostnames_option.py +++ b/appium/options/ios/xcuitest/webview/safari_ignore_web_hostnames_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SafariIgnoreWebHostnamesOption(SupportsCapabilities): @property - def safari_ignore_web_hostnames(self) -> str | None: + def safari_ignore_web_hostnames(self) -> Optional[str]: """ Comma-separated list of host names to be ignored. """ diff --git a/appium/options/ios/xcuitest/webview/safari_initial_url_option.py b/appium/options/ios/xcuitest/webview/safari_initial_url_option.py index 5787cd2da..79dc14fe3 100644 --- a/appium/options/ios/xcuitest/webview/safari_initial_url_option.py +++ b/appium/options/ios/xcuitest/webview/safari_initial_url_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SafariInitialUrlOption(SupportsCapabilities): @property - def safari_initial_url(self) -> str | None: + def safari_initial_url(self) -> Optional[str]: """ The initial safari url. """ diff --git a/appium/options/ios/xcuitest/webview/safari_log_all_communication_hex_dump_option.py b/appium/options/ios/xcuitest/webview/safari_log_all_communication_hex_dump_option.py index ef7d15af7..03b2e28cc 100644 --- a/appium/options/ios/xcuitest/webview/safari_log_all_communication_hex_dump_option.py +++ b/appium/options/ios/xcuitest/webview/safari_log_all_communication_hex_dump_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SafariLogAllCommunicationHexDumpOption(SupportsCapabilities): @property - def safari_log_all_communication_hex_dump(self) -> bool | None: + def safari_log_all_communication_hex_dump(self) -> Optional[bool]: """ Whether to log of plists sent to and received from the Web Inspector in hex dump format. diff --git a/appium/options/ios/xcuitest/webview/safari_log_all_communication_option.py b/appium/options/ios/xcuitest/webview/safari_log_all_communication_option.py index 36ff3f1f7..b022dba3d 100644 --- a/appium/options/ios/xcuitest/webview/safari_log_all_communication_option.py +++ b/appium/options/ios/xcuitest/webview/safari_log_all_communication_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SafariLogAllCommunicationOption(SupportsCapabilities): @property - def safari_log_all_communication(self) -> bool | None: + def safari_log_all_communication(self) -> Optional[bool]: """ Whether to log of plists sent to and received from the Web Inspector. """ diff --git a/appium/options/ios/xcuitest/webview/safari_open_links_in_background_option.py b/appium/options/ios/xcuitest/webview/safari_open_links_in_background_option.py index 4fe804d77..2ebb48011 100644 --- a/appium/options/ios/xcuitest/webview/safari_open_links_in_background_option.py +++ b/appium/options/ios/xcuitest/webview/safari_open_links_in_background_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SafariOpenLinksInBackgroundOption(SupportsCapabilities): @property - def safari_open_links_in_background(self) -> bool | None: + def safari_open_links_in_background(self) -> Optional[bool]: """ Whether Safari should allow links to open in new windows. """ diff --git a/appium/options/ios/xcuitest/webview/safari_socket_chunk_size_option.py b/appium/options/ios/xcuitest/webview/safari_socket_chunk_size_option.py index c5fb4bff6..c33ea61d2 100644 --- a/appium/options/ios/xcuitest/webview/safari_socket_chunk_size_option.py +++ b/appium/options/ios/xcuitest/webview/safari_socket_chunk_size_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SafariSocketChunkSizeOption(SupportsCapabilities): @property - def safari_socket_chunk_size(self) -> int | None: + def safari_socket_chunk_size(self) -> Optional[int]: """ Get the size of a single remote debugger socket chunk. """ diff --git a/appium/options/ios/xcuitest/webview/safari_web_inspector_max_frame_length_option.py b/appium/options/ios/xcuitest/webview/safari_web_inspector_max_frame_length_option.py index 6732dfef2..d92fb2d4a 100644 --- a/appium/options/ios/xcuitest/webview/safari_web_inspector_max_frame_length_option.py +++ b/appium/options/ios/xcuitest/webview/safari_web_inspector_max_frame_length_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SafariWebInspectorMaxFrameLengthOption(SupportsCapabilities): @property - def safari_web_inspector_max_frame_length(self) -> int | None: + def safari_web_inspector_max_frame_length(self) -> Optional[int]: """ Maximum size in bytes of a single data frame for the Web Inspector. """ diff --git a/appium/options/ios/xcuitest/webview/webkit_response_timeout_option.py b/appium/options/ios/xcuitest/webview/webkit_response_timeout_option.py index 25d0f8d76..1e274bf01 100644 --- a/appium/options/ios/xcuitest/webview/webkit_response_timeout_option.py +++ b/appium/options/ios/xcuitest/webview/webkit_response_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class WebkitResponseTimeoutOption(SupportsCapabilities): @property - def webkit_response_timeout(self) -> timedelta | None: + def webkit_response_timeout(self) -> Optional[timedelta]: """ Time to wait for a response from WebKit in a Safari session. """ @@ -32,7 +33,7 @@ def webkit_response_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @webkit_response_timeout.setter - def webkit_response_timeout(self, value: timedelta | int) -> None: + def webkit_response_timeout(self, value: Union[timedelta, int]) -> None: """ (Real device only) Set the time to wait for a response from WebKit in a Safari session. Defaults to 5000ms. diff --git a/appium/options/ios/xcuitest/webview/webview_connect_retries_option.py b/appium/options/ios/xcuitest/webview/webview_connect_retries_option.py index e8dd86b36..7792f15b0 100644 --- a/appium/options/ios/xcuitest/webview/webview_connect_retries_option.py +++ b/appium/options/ios/xcuitest/webview/webview_connect_retries_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class WebviewConnectRetriesOption(SupportsCapabilities): @property - def webview_connect_retries(self) -> int | None: + def webview_connect_retries(self) -> Optional[int]: """ Number of retries to send connection message to remote debugger, to get a webview. diff --git a/appium/options/ios/xcuitest/webview/webview_connect_timeout_option.py b/appium/options/ios/xcuitest/webview/webview_connect_timeout_option.py index b011e6a47..9fa742020 100644 --- a/appium/options/ios/xcuitest/webview/webview_connect_timeout_option.py +++ b/appium/options/ios/xcuitest/webview/webview_connect_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class WebviewConnectTimeoutOption(SupportsCapabilities): @property - def webview_connect_timeout(self) -> timedelta | None: + def webview_connect_timeout(self) -> Optional[timedelta]: """ Timeout to wait for the initial presence of webviews. """ @@ -32,7 +33,7 @@ def webview_connect_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @webview_connect_timeout.setter - def webview_connect_timeout(self, value: timedelta | int) -> None: + def webview_connect_timeout(self, value: Union[timedelta, int]) -> None: """ The time to wait for the initial presence of webviews in MobileSafari or hybrid apps. Defaults to 0ms. diff --git a/appium/options/mac/mac2/app_path_option.py b/appium/options/mac/mac2/app_path_option.py index bf12ea633..71506fd45 100644 --- a/appium/options/mac/mac2/app_path_option.py +++ b/appium/options/mac/mac2/app_path_option.py @@ -16,6 +16,7 @@ # under the License. from os import PathLike, fspath +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,14 +25,14 @@ class AppPathOption(SupportsCapabilities): @property - def app_path(self) -> str | None: + def app_path(self) -> Optional[str]: """ The path of the application to automate. """ return self.get_capability(APP_PATH) @app_path.setter - def app_path(self, value: str | PathLike) -> None: + def app_path(self, value: Union[str, PathLike]) -> None: """ Set the path of the application to automate. """ diff --git a/appium/options/mac/mac2/arguments_option.py b/appium/options/mac/mac2/arguments_option.py index 063ebe94f..3c9a5a70e 100644 --- a/appium/options/mac/mac2/arguments_option.py +++ b/appium/options/mac/mac2/arguments_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import List, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,14 +24,14 @@ class ArgumentsOption(SupportsCapabilities): @property - def arguments(self) -> list[str] | None: + def arguments(self) -> Optional[List[str]]: """ Array of application command line arguments. """ return self.get_capability(ARGUMENTS) @arguments.setter - def arguments(self, value: list[str]) -> None: + def arguments(self, value: List[str]) -> None: """ Set the array of application command line arguments. This capability is only going to be applied if the application is not running on session startup. diff --git a/appium/options/mac/mac2/base.py b/appium/options/mac/mac2/base.py index 511a096f4..d635f2b38 100644 --- a/appium/options/mac/mac2/base.py +++ b/appium/options/mac/mac2/base.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict from appium.options.common.automation_name_option import AUTOMATION_NAME from appium.options.common.base import PLATFORM_NAME, AppiumOptions @@ -51,7 +52,7 @@ class Mac2Options( WebDriverAgentMacUrlOption, ): @PrerunOption.prerun.setter # type: ignore - def prerun(self, value: dict[str, str]) -> None: + def prerun(self, value: Dict[str, str]) -> None: """ A mapping containing either 'script' or 'command' key. The value of each key must be a valid AppleScript script or command to be @@ -62,7 +63,7 @@ def prerun(self, value: dict[str, str]) -> None: PrerunOption.prerun.fset(self, value) # type: ignore @PostrunOption.postrun.setter # type: ignore - def postrun(self, value: dict[str, str]) -> None: + def postrun(self, value: Dict[str, str]) -> None: """ A mapping containing either 'script' or 'command' key. The value of each key must be a valid AppleScript script or command to be @@ -105,7 +106,7 @@ def bundle_id(self, value: str) -> None: BundleIdOption.bundle_id.fset(self, value) # type: ignore @property - def default_capabilities(self) -> dict: + def default_capabilities(self) -> Dict: return { AUTOMATION_NAME: 'Mac2', PLATFORM_NAME: 'Mac', diff --git a/appium/options/mac/mac2/bootstrap_root_option.py b/appium/options/mac/mac2/bootstrap_root_option.py index dc68a6bbe..25c53d1ba 100644 --- a/appium/options/mac/mac2/bootstrap_root_option.py +++ b/appium/options/mac/mac2/bootstrap_root_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class BootstrapRootOption(SupportsCapabilities): @property - def bootstrap_root(self) -> str | None: + def bootstrap_root(self) -> Optional[str]: """ The full path to WebDriverAgentMac root folder where Xcode project of the server sources lives. diff --git a/appium/options/mac/mac2/environment_option.py b/appium/options/mac/mac2/environment_option.py index 40818ebea..b5d72a9fc 100644 --- a/appium/options/mac/mac2/environment_option.py +++ b/appium/options/mac/mac2/environment_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict, Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,14 +24,14 @@ class EnvironmentOption(SupportsCapabilities): @property - def environment(self) -> dict[str, str] | None: + def environment(self) -> Optional[Dict[str, str]]: """ Application environment variables mapping. """ return self.get_capability(ENVIRONMENT) @environment.setter - def environment(self, value: dict[str, str]) -> None: + def environment(self, value: Dict[str, str]) -> None: """ Set the dictionary of environment variables (name->value) that are going to be passed to the application under test on top of environment variables inherited from diff --git a/appium/options/mac/mac2/server_startup_timeout_option.py b/appium/options/mac/mac2/server_startup_timeout_option.py index 7bfb4af28..32e13f79a 100644 --- a/appium/options/mac/mac2/server_startup_timeout_option.py +++ b/appium/options/mac/mac2/server_startup_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class ServerStartupTimeoutOption(SupportsCapabilities): @property - def server_startup_timeout(self) -> timedelta | None: + def server_startup_timeout(self) -> Optional[timedelta]: """ Get the timeout to wait util the WebDriverAgentMac project is built and started. @@ -33,7 +34,7 @@ def server_startup_timeout(self) -> timedelta | None: return None if value_ms is None else timedelta(milliseconds=value_ms) @server_startup_timeout.setter - def server_startup_timeout(self, value: int | timedelta) -> None: + def server_startup_timeout(self, value: Union[int, timedelta]) -> None: """ Set the timeout to wait util the WebDriverAgentMac project is built and started. diff --git a/appium/options/mac/mac2/show_server_logs_option.py b/appium/options/mac/mac2/show_server_logs_option.py index ad380f50b..635304925 100644 --- a/appium/options/mac/mac2/show_server_logs_option.py +++ b/appium/options/mac/mac2/show_server_logs_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ShowServerLogsOption(SupportsCapabilities): @property - def show_server_logs(self) -> bool | None: + def show_server_logs(self) -> Optional[bool]: """ Whether to show WDA server logs in the Appium log. """ diff --git a/appium/options/mac/mac2/skip_app_kill_option.py b/appium/options/mac/mac2/skip_app_kill_option.py index 41fdc3942..adfcd19a3 100644 --- a/appium/options/mac/mac2/skip_app_kill_option.py +++ b/appium/options/mac/mac2/skip_app_kill_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class SkipAppKillOption(SupportsCapabilities): @property - def skip_app_kill(self) -> bool | None: + def skip_app_kill(self) -> Optional[bool]: """ Whether to skip the termination of the application under test. """ diff --git a/appium/options/mac/mac2/web_driver_agent_mac_url_option.py b/appium/options/mac/mac2/web_driver_agent_mac_url_option.py index b8bb4d90e..8ffab4391 100644 --- a/appium/options/mac/mac2/web_driver_agent_mac_url_option.py +++ b/appium/options/mac/mac2/web_driver_agent_mac_url_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class WebDriverAgentMacUrlOption(SupportsCapabilities): @property - def web_driver_agent_mac_url(self) -> str | None: + def web_driver_agent_mac_url(self) -> Optional[str]: """ The URL Appium will connect to an existing WebDriverAgentMac instance. """ diff --git a/appium/options/windows/windows/app_arguments_option.py b/appium/options/windows/windows/app_arguments_option.py index 9ea8bd47c..665977d37 100644 --- a/appium/options/windows/windows/app_arguments_option.py +++ b/appium/options/windows/windows/app_arguments_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppArgumentsOption(SupportsCapabilities): @property - def app_arguments(self) -> str | None: + def app_arguments(self) -> Optional[str]: """ Application arguments string, for example `/?`. """ diff --git a/appium/options/windows/windows/app_top_level_window_option.py b/appium/options/windows/windows/app_top_level_window_option.py index d568816c2..f573fb48e 100644 --- a/appium/options/windows/windows/app_top_level_window_option.py +++ b/appium/options/windows/windows/app_top_level_window_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppTopLevelWindowOption(SupportsCapabilities): @property - def app_top_level_window(self) -> str | None: + def app_top_level_window(self) -> Optional[str]: """ Hexadecimal handle of an existing application top level window to attach to. """ diff --git a/appium/options/windows/windows/app_working_dir_option.py b/appium/options/windows/windows/app_working_dir_option.py index 8539542ec..0160f89aa 100644 --- a/appium/options/windows/windows/app_working_dir_option.py +++ b/appium/options/windows/windows/app_working_dir_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class AppWorkingDirOption(SupportsCapabilities): @property - def app_working_dir(self) -> str | None: + def app_working_dir(self) -> Optional[str]: """ Full path to the folder, which is going to be set as the working dir for the application under test. diff --git a/appium/options/windows/windows/base.py b/appium/options/windows/windows/base.py index 070e8f353..52de731e6 100644 --- a/appium/options/windows/windows/base.py +++ b/appium/options/windows/windows/base.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Dict from appium.options.common.app_option import AppOption from appium.options.common.automation_name_option import AUTOMATION_NAME @@ -57,7 +58,7 @@ def app(self, value: str) -> None: AppOption.app.fset(self, value) # type: ignore @PrerunOption.prerun.setter # type: ignore - def prerun(self, value: dict[str, str]) -> None: + def prerun(self, value: Dict[str, str]) -> None: """ A mapping containing either 'script' or 'command' key. The value of each key must be a valid PowerShell script or command to be @@ -68,7 +69,7 @@ def prerun(self, value: dict[str, str]) -> None: PrerunOption.prerun.fset(self, value) # type: ignore @PostrunOption.postrun.setter # type: ignore - def postrun(self, value: dict[str, str]) -> None: + def postrun(self, value: Dict[str, str]) -> None: """ A mapping containing either 'script' or 'command' key. The value of each key must be a valid PowerShell script or command to be @@ -89,7 +90,7 @@ def system_port(self, value: int) -> None: SystemPortOption.system_port.fset(self, value) # type: ignore @property - def default_capabilities(self) -> dict: + def default_capabilities(self) -> Dict: return { AUTOMATION_NAME: 'Windows', PLATFORM_NAME: 'Windows', diff --git a/appium/options/windows/windows/create_session_timeout_option.py b/appium/options/windows/windows/create_session_timeout_option.py index 17654f2bb..acf1289a8 100644 --- a/appium/options/windows/windows/create_session_timeout_option.py +++ b/appium/options/windows/windows/create_session_timeout_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class CreateSessionTimeoutOption(SupportsCapabilities): @property - def create_session_timeout(self) -> timedelta | None: + def create_session_timeout(self) -> Optional[timedelta]: """ Timeout used to retry Appium Windows Driver session startup. """ @@ -32,7 +33,7 @@ def create_session_timeout(self) -> timedelta | None: return None if value is None else timedelta(milliseconds=value) @create_session_timeout.setter - def create_session_timeout(self, value: timedelta | int) -> None: + def create_session_timeout(self, value: Union[timedelta, int]) -> None: """ Set the timeout used to retry Appium Windows Driver session startup. This capability could be used as a workaround for the long startup times diff --git a/appium/options/windows/windows/expreimental_web_driver_option.py b/appium/options/windows/windows/expreimental_web_driver_option.py index af45aa4c2..ae065d717 100644 --- a/appium/options/windows/windows/expreimental_web_driver_option.py +++ b/appium/options/windows/windows/expreimental_web_driver_option.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from typing import Optional from appium.options.common.supports_capabilities import SupportsCapabilities @@ -23,7 +24,7 @@ class ExperimentalWebDriverOption(SupportsCapabilities): @property - def experimental_webdriver(self) -> bool | None: + def experimental_webdriver(self) -> Optional[bool]: """ Whether to enable experimental features and optimizations. """ diff --git a/appium/options/windows/windows/wait_for_app_launch_option.py b/appium/options/windows/windows/wait_for_app_launch_option.py index 68c0f1f2d..9631b9231 100644 --- a/appium/options/windows/windows/wait_for_app_launch_option.py +++ b/appium/options/windows/windows/wait_for_app_launch_option.py @@ -16,6 +16,7 @@ # under the License. from datetime import timedelta +from typing import Optional, Union from appium.options.common.supports_capabilities import SupportsCapabilities @@ -24,7 +25,7 @@ class WaitForAppLaunchOption(SupportsCapabilities): @property - def wait_for_app_launch(self) -> timedelta | None: + def wait_for_app_launch(self) -> Optional[timedelta]: """ Timeout used to retry Appium Windows Driver session startup. """ @@ -32,7 +33,7 @@ def wait_for_app_launch(self) -> timedelta | None: return None if value is None else timedelta(seconds=value) @wait_for_app_launch.setter - def wait_for_app_launch(self, value: timedelta | int) -> None: + def wait_for_app_launch(self, value: Union[timedelta, int]) -> None: """ Similar to createSessionTimeout, but is applied on the server side. Enables Appium Windows Driver to wait for diff --git a/appium/protocols/webdriver/can_execute_commands.py b/appium/protocols/webdriver/can_execute_commands.py index 0ec39cf78..de4f1b4ad 100644 --- a/appium/protocols/webdriver/can_execute_commands.py +++ b/appium/protocols/webdriver/can_execute_commands.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Protocol +from typing import Dict, Protocol, Union from selenium.webdriver.remote.remote_connection import RemoteConnection @@ -20,4 +20,4 @@ class CanExecuteCommands(Protocol): command_executor: RemoteConnection - def execute(self, driver_command: str, params: dict | None = None) -> dict: ... + def execute(self, driver_command: str, params: Union[Dict, None] = None) -> Dict: ... diff --git a/appium/protocols/webdriver/can_execute_scripts.py b/appium/protocols/webdriver/can_execute_scripts.py index 3b6e9facf..1d04f6cae 100644 --- a/appium/protocols/webdriver/can_execute_scripts.py +++ b/appium/protocols/webdriver/can_execute_scripts.py @@ -12,15 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Protocol +from typing import Any, List, Optional, Protocol class CanExecuteScripts(Protocol): - def pin_script(self, script: str, script_key: Any | None = None) -> Any: ... + def pin_script(self, script: str, script_key: Optional[Any] = None) -> Any: ... def unpin(self, script_key: Any) -> None: ... - def get_pinned_scripts(self) -> list[str]: ... + def get_pinned_scripts(self) -> List[str]: ... def execute_script(self, script: str, *args: Any) -> Any: ... diff --git a/appium/protocols/webdriver/can_find_elements.py b/appium/protocols/webdriver/can_find_elements.py index 01cbe5b4d..07b0b8279 100644 --- a/appium/protocols/webdriver/can_find_elements.py +++ b/appium/protocols/webdriver/can_find_elements.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Protocol, runtime_checkable +from typing import TYPE_CHECKING, Dict, List, Protocol, Union, runtime_checkable if TYPE_CHECKING: from appium.webdriver.webelement import WebElement @@ -27,6 +27,6 @@ class CanFindElements(Protocol): - find_elements(by, value): Find multiple elements """ - def find_element(self, by: str, value: str | dict | None = None) -> 'WebElement': ... + def find_element(self, by: str, value: Union[str, Dict, None] = None) -> 'WebElement': ... - def find_elements(self, by: str, value: str | dict | None = None) -> list['WebElement']: ... + def find_elements(self, by: str, value: Union[str, Dict, None] = None) -> List['WebElement']: ... diff --git a/appium/protocols/webdriver/can_remember_extension_presence.py b/appium/protocols/webdriver/can_remember_extension_presence.py new file mode 100644 index 000000000..1684788b0 --- /dev/null +++ b/appium/protocols/webdriver/can_remember_extension_presence.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Protocol, TypeVar + +T = TypeVar('T') + + +class CanRememberExtensionPresence(Protocol): + def assert_extension_exists(self: T, ext_name: str) -> T: ... + + def mark_extension_absence(self: T, ext_name: str) -> T: ... diff --git a/appium/webdriver/appium_connection.py b/appium/webdriver/appium_connection.py index 559a695c0..9ca311824 100644 --- a/appium/webdriver/appium_connection.py +++ b/appium/webdriver/appium_connection.py @@ -13,7 +13,7 @@ # limitations under the License. import uuid -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Dict from selenium.webdriver.remote.remote_connection import RemoteConnection @@ -28,7 +28,7 @@ HEADER_IDEMOTENCY_KEY = 'X-Idempotency-Key' -def _get_new_headers(key: str, headers: dict[str, str]) -> dict[str, str]: +def _get_new_headers(key: str, headers: Dict[str, str]) -> Dict[str, str]: """Return a new dictionary of heafers without the given key. The key match is case-insensitive.""" lower_key = key.lower() @@ -51,7 +51,7 @@ class AppiumConnection(RemoteConnection): extra_headers = {} @classmethod - def get_remote_connection_headers(cls, parsed_url: 'ParseResult', keep_alive: bool = True) -> dict[str, Any]: + def get_remote_connection_headers(cls, parsed_url: 'ParseResult', keep_alive: bool = True) -> Dict[str, Any]: """Override get_remote_connection_headers in RemoteConnection to control the extra headers. This method will be used in sending a request method in this class. """ diff --git a/appium/webdriver/appium_service.py b/appium/webdriver/appium_service.py index fe34d8691..93c926c65 100644 --- a/appium/webdriver/appium_service.py +++ b/appium/webdriver/appium_service.py @@ -17,9 +17,7 @@ import subprocess as sp import sys import time -from collections.abc import Callable -from ipaddress import AddressValueError, IPv6Address -from typing import Any +from typing import Any, Callable, List, Optional, Set from selenium.webdriver.remote.remote_connection import urllib3 @@ -43,8 +41,8 @@ class AppiumStartupError(RuntimeError): class AppiumService: def __init__(self) -> None: - self._process: sp.Popen | None = None - self._cmd: list[str] | None = None + self._process: Optional[sp.Popen] = None + self._cmd: Optional[List[str]] = None def start(self, **kwargs: Any) -> sp.Popen: """Starts Appium service with given arguments. @@ -84,21 +82,21 @@ def start(self, **kwargs: Any) -> sp.Popen: """ self.stop() - env = kwargs.get('env') + env = kwargs['env'] if 'env' in kwargs else None node: str = kwargs.get('node') or get_node() npm: str = kwargs.get('npm') or get_npm() main_script: str = kwargs.get('main_script') or get_main_script(node, npm) # A workaround for https://github.com/appium/python-client/issues/534 default_std = sp.DEVNULL if sys.platform == 'win32' else sp.PIPE - stdout = kwargs.get('stdout', default_std) - stderr = kwargs.get('stderr', default_std) + stdout = kwargs['stdout'] if 'stdout' in kwargs else default_std + stderr = kwargs['stderr'] if 'stderr' in kwargs else default_std timeout_ms = int(kwargs['timeout_ms']) if 'timeout_ms' in kwargs else STARTUP_TIMEOUT_MS - args: list[str] = [node, main_script] + args: List[str] = [node, main_script] if 'args' in kwargs: args.extend(kwargs['args']) self._cmd = args self._process = sp.Popen(args=args, stdout=stdout, stderr=stderr, env=env) - error_msg: str | None = None + error_msg: Optional[str] = None startup_failure_msg = ( 'Appium server process is unable to start. Make sure proper values have been ' f"provided to 'node' ({node}), 'npm' ({npm}) and 'main_script' ({main_script}) " @@ -185,7 +183,7 @@ def is_listening(self) -> bool: try: return is_service_listening( _make_server_url(self._cmd), - timeout=STATE_CHECK_INTERVAL_MS / 100.0, # 5 seconds + timeout=STATE_CHECK_INTERVAL_MS, custom_validator=self._assert_is_running, ) except AppiumStartupError: @@ -196,7 +194,7 @@ def _assert_is_running(self) -> None: raise AppiumStartupError() -def is_service_listening(url: str, timeout: float = 5, custom_validator: Callable[[], None] | None = None) -> bool: +def is_service_listening(url: str, timeout: float = 5, custom_validator: Optional[Callable[[], None]] = None) -> bool: """ Check if the service is running @@ -224,7 +222,7 @@ def is_service_listening(url: str, timeout: float = 5, custom_validator: Callabl return False -def find_executable(executable: str) -> str | None: +def find_executable(executable: str) -> Optional[str]: path = os.environ['PATH'] paths = path.split(os.pathsep) _, ext = os.path.splitext(executable) @@ -258,8 +256,8 @@ def get_npm() -> str: return result -def get_main_script(node: str | None, npm: str | None) -> str: - result: str | None = None +def get_main_script(node: Optional[str], npm: Optional[str]) -> str: + result: Optional[str] = None npm_path = npm or get_npm() for args in [['root', '-g'], ['root']]: try: @@ -283,29 +281,26 @@ def get_main_script(node: str | None, npm: str | None) -> str: return result -def _parse_arg_value(args: list[str], arg_names: set[str], default: str) -> str: +def _parse_arg_value(args: List[str], arg_names: Set[str], default: str) -> str: for idx, arg in enumerate(args): if arg in arg_names and idx < len(args) - 1: return args[idx + 1] - name, separator, value = arg.partition('=') - if separator and name in arg_names: - return value return default -def _parse_port(args: list[str]) -> int: +def _parse_port(args: List[str]) -> int: return int(_parse_arg_value(args, {'--port', '-p'}, str(DEFAULT_PORT))) -def _parse_base_path(args: list[str]) -> str: +def _parse_base_path(args: List[str]) -> str: return _parse_arg_value(args, {'--base-path', '-pa'}, DEFAULT_BASE_PATH) -def _parse_host(args: list[str]) -> str: +def _parse_host(args: List[str]) -> str: return _parse_arg_value(args, {'--address', '-a'}, DEFAULT_HOST) -def _parse_protocol(args: list[str]) -> str: +def _parse_protocol(args: List[str]) -> str: return ( 'https' if _parse_arg_value(args, {'--ssl-cert-path'}, '') and _parse_arg_value(args, {'--ssl-key-path'}, '') @@ -313,20 +308,13 @@ def _parse_protocol(args: list[str]) -> str: ) -def _make_status_path(args: list[str]) -> str: +def _make_status_path(args: List[str]) -> str: base_path = _parse_base_path(args) return STATUS_URL if base_path == DEFAULT_BASE_PATH else f'{re.sub(r"/+$", "", base_path)}{STATUS_URL}' -def _make_server_url(args: list[str]) -> str: - host = _parse_host(args) - try: - IPv6Address(host) - except AddressValueError: - pass - else: - host = f'[{host}]' - return f'{_parse_protocol(args)}://{host}:{_parse_port(args)}{_make_status_path(args)}' +def _make_server_url(args: List[str]) -> str: + return f'{_parse_protocol(args)}://{_parse_host(args)}:{_parse_port(args)}{_make_status_path(args)}' if __name__ == '__main__': diff --git a/appium/webdriver/errorhandler.py b/appium/webdriver/errorhandler.py index d074b01a8..b2a5cab91 100644 --- a/appium/webdriver/errorhandler.py +++ b/appium/webdriver/errorhandler.py @@ -13,15 +13,14 @@ # limitations under the License. import json -from collections.abc import Sequence -from typing import Any +from typing import Any, Dict, List, Sequence, Type, Union import selenium.common.exceptions as sel_exceptions from selenium.webdriver.remote import errorhandler import appium.common.exceptions as appium_exceptions -ERROR_TO_EXC_MAPPING: dict[str, type[sel_exceptions.WebDriverException]] = { +ERROR_TO_EXC_MAPPING: Dict[str, Type[sel_exceptions.WebDriverException]] = { 'element click intercepted': sel_exceptions.ElementClickInterceptedException, 'element not interactable': sel_exceptions.ElementNotInteractableException, 'insecure certificate': sel_exceptions.InsecureCertificateException, @@ -56,13 +55,13 @@ } -def format_stacktrace(original: None | str | Sequence) -> list[str]: +def format_stacktrace(original: Union[None, str, Sequence]) -> List[str]: if not original: return [] if isinstance(original, str): return original.split('\n') - result: list[str] = [] + result: List[str] = [] try: for frame in original: if not isinstance(frame, dict): @@ -82,7 +81,7 @@ def format_stacktrace(original: None | str | Sequence) -> list[str]: class MobileErrorHandler(errorhandler.ErrorHandler): - def check_response(self, response: dict[str, Any]) -> None: + def check_response(self, response: Dict[str, Any]) -> None: """ https://www.w3.org/TR/webdriver/#errors """ @@ -108,7 +107,7 @@ def check_response(self, response: dict[str, Any]) -> None: # In theory, we should also be checking HTTP status codes. # Java client, for example, prints a warning if the actual `error` # value does not match to the response's HTTP status code. - exception_class: type[sel_exceptions.WebDriverException] = ERROR_TO_EXC_MAPPING.get( + exception_class: Type[sel_exceptions.WebDriverException] = ERROR_TO_EXC_MAPPING.get( error, sel_exceptions.WebDriverException ) if exception_class is sel_exceptions.WebDriverException and message: diff --git a/appium/webdriver/extensions/action_helpers.py b/appium/webdriver/extensions/action_helpers.py index be32ca9f1..9c3709223 100644 --- a/appium/webdriver/extensions/action_helpers.py +++ b/appium/webdriver/extensions/action_helpers.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, List, Optional, Tuple, cast from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.actions import interaction @@ -29,7 +29,7 @@ class ActionHelpers: - def scroll(self, origin_el: WebElement, destination_el: WebElement, duration: int | None = None) -> Self: + def scroll(self, origin_el: WebElement, destination_el: WebElement, duration: Optional[int] = None) -> Self: """Scrolls from one element to another Args: @@ -63,7 +63,7 @@ def scroll(self, origin_el: WebElement, destination_el: WebElement, duration: in actions.perform() return self - def drag_and_drop(self, origin_el: WebElement, destination_el: WebElement, pause: float | None = None) -> Self: + def drag_and_drop(self, origin_el: WebElement, destination_el: WebElement, pause: Optional[float] = None) -> Self: """Drag the origin element to the destination element Args: @@ -84,7 +84,7 @@ def drag_and_drop(self, origin_el: WebElement, destination_el: WebElement, pause actions.perform() return self - def tap(self, positions: list[tuple[int, int]], duration: int | None = None) -> Self: + def tap(self, positions: List[Tuple[int, int]], duration: Optional[int] = None) -> Self: """Taps on an particular place with up to five fingers, holding for a certain time @@ -113,10 +113,12 @@ def tap(self, positions: list[tuple[int, int]], duration: int | None = None) -> actions.w3c_actions.pointer_action.release() actions.perform() else: + finger = 0 actions = ActionChains(cast('WebDriver', self)) actions.w3c_actions.devices = [] - for finger, position in enumerate(positions, start=1): + for position in positions: + finger += 1 x = position[0] y = position[1] diff --git a/appium/webdriver/extensions/android/activities.py b/appium/webdriver/extensions/android/activities.py index cdcfdc25c..76da521b0 100644 --- a/appium/webdriver/extensions/android/activities.py +++ b/appium/webdriver/extensions/android/activities.py @@ -12,33 +12,35 @@ # See the License for the specific language governing permissions and # limitations under the License. -from selenium.common.exceptions import TimeoutException +from selenium.common.exceptions import TimeoutException, UnknownMethodException from selenium.webdriver.support.ui import WebDriverWait from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from appium.webdriver.mobilecommand import MobileCommand as Command -class Activities(CanExecuteCommands, CanExecuteScripts): +class Activities(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): @property def current_activity(self) -> str: """Retrieves the current activity running on the device. - Requires the Appium driver to support the `mobile: getCurrentActivity` execute method. - Returns: str: The current activity name running on the device """ ext_name = 'mobile: getCurrentActivity' - return self.execute_script(ext_name) + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name) + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute(Command.GET_CURRENT_ACTIVITY)['value'] def wait_activity(self, activity: str, timeout: int, interval: int = 1) -> bool: """Wait for an activity: block until target activity presents or time out. This is an Android-only method. - Requires the Appium driver to support the `mobile: getCurrentActivity` execute method. - Args: activity: target activity timeout: max wait time, in seconds @@ -54,3 +56,10 @@ def wait_activity(self, activity: str, timeout: int, interval: int = 1) -> bool: return True except TimeoutException: return False + + def _add_commands(self) -> None: + self.command_executor.add_command( + Command.GET_CURRENT_ACTIVITY, + 'GET', + '/session/$sessionId/appium/device/current_activity', + ) diff --git a/appium/webdriver/extensions/android/common.py b/appium/webdriver/extensions/android/common.py index 79572107f..f8ad2c720 100644 --- a/appium/webdriver/extensions/android/common.py +++ b/appium/webdriver/extensions/android/common.py @@ -16,9 +16,10 @@ from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence -class Common(CanExecuteCommands, CanExecuteScripts): +class Common(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): def open_notifications(self) -> Self: """Open notification shade in Android (API Level 18 and above) @@ -32,3 +33,6 @@ def open_notifications(self) -> Self: def current_package(self) -> str: """Retrieves the current package running on the device.""" return self.execute_script('mobile: getCurrentPackage') + + def _add_commands(self) -> None: + pass diff --git a/appium/webdriver/extensions/android/display.py b/appium/webdriver/extensions/android/display.py index 4ee29ba79..28abdcbbf 100644 --- a/appium/webdriver/extensions/android/display.py +++ b/appium/webdriver/extensions/android/display.py @@ -12,16 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. +from selenium.common.exceptions import UnknownMethodException + from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from appium.webdriver.mobilecommand import MobileCommand as Command -class Display(CanExecuteCommands, CanExecuteScripts): +class Display(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): def get_display_density(self) -> int: """Get the display density, Android only - Requires the Appium driver to support the `mobile: getDisplayDensity` execute method. - Returns: The display density of the Android device(dpi) @@ -32,4 +34,15 @@ def get_display_density(self) -> int: int: The display density """ ext_name = 'mobile: getDisplayDensity' - return self.execute_script(ext_name) + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name) + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute(Command.GET_DISPLAY_DENSITY)['value'] + + def _add_commands(self) -> None: + self.command_executor.add_command( + Command.GET_DISPLAY_DENSITY, + 'GET', + '/session/$sessionId/appium/device/display_density', + ) diff --git a/appium/webdriver/extensions/android/gsm.py b/appium/webdriver/extensions/android/gsm.py index 56aedbd53..ce093c4a9 100644 --- a/appium/webdriver/extensions/android/gsm.py +++ b/appium/webdriver/extensions/android/gsm.py @@ -123,3 +123,6 @@ def set_gsm_voice(self, state: str) -> Self: args = {'state': state} self.execute_script(ext_name, args) return self + + def _add_commands(self) -> None: + pass diff --git a/appium/webdriver/extensions/android/network.py b/appium/webdriver/extensions/android/network.py index ad039104c..6054e29d9 100644 --- a/appium/webdriver/extensions/android/network.py +++ b/appium/webdriver/extensions/android/network.py @@ -12,12 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +from selenium.common.exceptions import UnknownMethodException from typing_extensions import Self from appium.common.helper import extract_const_attributes from appium.common.logger import logger from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from appium.webdriver.mobilecommand import MobileCommand as Command class NetSpeed: @@ -38,7 +41,7 @@ class NetworkMask: AIRPLANE_MODE = 0b001 -class Network(CanExecuteCommands, CanExecuteScripts): +class Network(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): @property def network_connection(self) -> int: """Returns an integer bitmask specifying the network connection type. @@ -48,19 +51,18 @@ def network_connection(self) -> int: This API only works reliably on emulators (any version) and real devices since API level 31. - - Requires the Appium driver to support the `mobile: getConnectivity` execute method. - - Returns: - The current network connection bitmask """ ext_name = 'mobile: getConnectivity' - result_map = self.execute_script(ext_name) - return ( - (NetworkMask.WIFI if result_map['wifi'] else 0) - | (NetworkMask.DATA if result_map['data'] else 0) - | (NetworkMask.AIRPLANE_MODE if result_map['airplaneMode'] else 0) - ) + try: + result_map = self.assert_extension_exists(ext_name).execute_script(ext_name) + return ( + (NetworkMask.WIFI if result_map['wifi'] else 0) + | (NetworkMask.DATA if result_map['data'] else 0) + | (NetworkMask.AIRPLANE_MODE if result_map['airplaneMode'] else 0) + ) + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute(Command.GET_NETWORK_CONNECTION, {})['value'] def set_network_connection(self, connection_type: int) -> int: """Sets the network connection type. Android only. @@ -86,39 +88,43 @@ def set_network_connection(self, connection_type: int) -> int: This API only works reliably on emulators (any version) and real devices since API level 31. - Requires the Appium driver to support the `mobile: setConnectivity` and - `mobile: getConnectivity` execute methods. - Args: connection_type: a member of the enum `appium.webdriver.ConnectionType` - Returns: - The current network connection bitmask after applying the change + Return: + int: Set network connection type """ ext_name = 'mobile: setConnectivity' - self.execute_script( - ext_name, - { - 'wifi': bool(connection_type & NetworkMask.WIFI), - 'data': bool(connection_type & NetworkMask.DATA), - 'airplaneMode': bool(connection_type & NetworkMask.AIRPLANE_MODE), - }, - ) - return self.network_connection + try: + return self.assert_extension_exists(ext_name).execute_script( + ext_name, + { + 'wifi': bool(connection_type & NetworkMask.WIFI), + 'data': bool(connection_type & NetworkMask.DATA), + 'airplaneMode': bool(connection_type & NetworkMask.AIRPLANE_MODE), + }, + ) + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute( + Command.SET_NETWORK_CONNECTION, {'parameters': {'type': connection_type}} + )['value'] def toggle_wifi(self) -> Self: """Toggle the wifi on the device, Android only. This API only works reliably on emulators (any version) and real devices since API level 31. - Requires the Appium driver to support the `mobile: getConnectivity` and - `mobile: setConnectivity` execute methods. - Returns: Union['WebDriver', 'Network']: Self instance """ ext_name = 'mobile: setConnectivity' - self.execute_script(ext_name, {'wifi': not (self.network_connection & NetworkMask.WIFI)}) + try: + self.assert_extension_exists(ext_name).execute_script( + ext_name, {'wifi': not (self.network_connection & NetworkMask.WIFI)} + ) + except UnknownMethodException: + self.mark_extension_absence(ext_name).execute(Command.TOGGLE_WIFI, {}) return self def set_network_speed(self, speed_type: str) -> Self: @@ -126,8 +132,6 @@ def set_network_speed(self, speed_type: str) -> Self: Android Emulator only. - Requires the Appium driver to support the `mobile: networkSpeed` execute method. - Args: speed_type: The network speed type. A member of the const appium.webdriver.extensions.android.network.NetSpeed. @@ -145,5 +149,27 @@ def set_network_speed(self, speed_type: str) -> Self: f'(e.g. {NetSpeed.__name__}.LTE)' ) ext_name = 'mobile: networkSpeed' - self.execute_script(ext_name, {'speed': speed_type}) + try: + self.assert_extension_exists(ext_name).execute_script(ext_name, {'speed': speed_type}) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.SET_NETWORK_SPEED, {'netspeed': speed_type}) return self + + def _add_commands(self) -> None: + self.command_executor.add_command(Command.TOGGLE_WIFI, 'POST', '/session/$sessionId/appium/device/toggle_wifi') + self.command_executor.add_command( + Command.GET_NETWORK_CONNECTION, + 'GET', + '/session/$sessionId/network_connection', + ) + self.command_executor.add_command( + Command.SET_NETWORK_CONNECTION, + 'POST', + '/session/$sessionId/network_connection', + ) + self.command_executor.add_command( + Command.SET_NETWORK_SPEED, + 'POST', + '/session/$sessionId/appium/device/network_speed', + ) diff --git a/appium/webdriver/extensions/android/performance.py b/appium/webdriver/extensions/android/performance.py index 4994eca38..f5781cf3f 100644 --- a/appium/webdriver/extensions/android/performance.py +++ b/appium/webdriver/extensions/android/performance.py @@ -12,27 +12,31 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Dict, List, Union + +from selenium.common.exceptions import UnknownMethodException from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from appium.webdriver.mobilecommand import MobileCommand as Command -class Performance(CanExecuteCommands, CanExecuteScripts): - def get_performance_data(self, package_name: str, data_type: str, data_read_timeout: int | None = None) -> list[list[str]]: +class Performance(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): + def get_performance_data( + self, package_name: str, data_type: str, data_read_timeout: Union[int, None] = None + ) -> List[List[str]]: """Returns the information of the system state which is supported to read as like cpu, memory, network traffic, and battery. Android only. - Requires the Appium driver to support the `mobile: getPerformanceData` execute method. - Args: package_name: The package name of the application data_type: The type of system state which wants to read. It should be one of the supported performance data types. Check :func:`.get_performance_data_types` for supported types - data_read_timeout: Legacy parameter retained for compatibility; ignored by - `mobile: getPerformanceData` + data_read_timeout: The number of attempts to read Usage: self.driver.get_performance_data('my.app.package', 'cpuinfo', 5) @@ -41,16 +45,20 @@ def get_performance_data(self, package_name: str, data_type: str, data_read_time The data along to `data_type` """ ext_name = 'mobile: getPerformanceData' - args: dict[str, str | int] = {'packageName': package_name, 'dataType': data_type} - return self.execute_script(ext_name, args) + args: Dict[str, Union[str, int]] = {'packageName': package_name, 'dataType': data_type} + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name, args) + except UnknownMethodException: + # TODO: Remove the fallback + if data_read_timeout is not None: + args['dataReadTimeout'] = data_read_timeout + return self.mark_extension_absence(ext_name).execute(Command.GET_PERFORMANCE_DATA, args)['value'] - def get_performance_data_types(self) -> list[str]: + def get_performance_data_types(self) -> List[str]: """Returns the information types of the system state which is supported to read as like cpu, memory, network traffic, and battery. Android only. - Requires the Appium driver to support the `mobile: getPerformanceDataTypes` execute method. - Usage: self.driver.get_performance_data_types() @@ -58,4 +66,20 @@ def get_performance_data_types(self) -> list[str]: Available data types """ ext_name = 'mobile: getPerformanceDataTypes' - return self.execute_script(ext_name) + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name) + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute(Command.GET_PERFORMANCE_DATA_TYPES)['value'] + + def _add_commands(self) -> None: + self.command_executor.add_command( + Command.GET_PERFORMANCE_DATA, + 'POST', + '/session/$sessionId/appium/getPerformanceData', + ) + self.command_executor.add_command( + Command.GET_PERFORMANCE_DATA_TYPES, + 'POST', + '/session/$sessionId/appium/performanceData/types', + ) diff --git a/appium/webdriver/extensions/android/power.py b/appium/webdriver/extensions/android/power.py index 7b8b574ec..537ab6803 100644 --- a/appium/webdriver/extensions/android/power.py +++ b/appium/webdriver/extensions/android/power.py @@ -12,13 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +from selenium.common.exceptions import UnknownMethodException from typing_extensions import Self from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from appium.webdriver.mobilecommand import MobileCommand as Command -class Power(CanExecuteCommands, CanExecuteScripts): +class Power(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): AC_OFF, AC_ON = 'off', 'on' def set_power_capacity(self, percent: int) -> Self: @@ -26,8 +29,6 @@ def set_power_capacity(self, percent: int) -> Self: Android only. - Requires the Appium driver to support the `mobile: powerCapacity` execute method. - Args: percent: The power capacity to be set. Can be set from 0 to 100 @@ -39,7 +40,11 @@ def set_power_capacity(self, percent: int) -> Self: """ ext_name = 'mobile: powerCapacity' args = {'percent': percent} - self.execute_script(ext_name, args) + try: + self.assert_extension_exists(ext_name).execute_script(ext_name, args) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.SET_POWER_CAPACITY, args) return self def set_power_ac(self, ac_state: str) -> Self: @@ -47,8 +52,6 @@ def set_power_ac(self, ac_state: str) -> Self: Android only. - Requires the Appium driver to support the `mobile: powerAC` execute method. - Args: ac_state: The power ac state to be set. Use `Power.AC_OFF`, `Power.AC_ON` @@ -61,5 +64,17 @@ def set_power_ac(self, ac_state: str) -> Self: """ ext_name = 'mobile: powerAC' args = {'state': ac_state} - self.execute_script(ext_name, args) + try: + self.assert_extension_exists(ext_name).execute_script(ext_name, args) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.SET_POWER_AC, args) return self + + def _add_commands(self) -> None: + self.command_executor.add_command( + Command.SET_POWER_CAPACITY, + 'POST', + '/session/$sessionId/appium/device/power_capacity', + ) + self.command_executor.add_command(Command.SET_POWER_AC, 'POST', '/session/$sessionId/appium/device/power_ac') diff --git a/appium/webdriver/extensions/android/sms.py b/appium/webdriver/extensions/android/sms.py index c4ada1f50..f5769c561 100644 --- a/appium/webdriver/extensions/android/sms.py +++ b/appium/webdriver/extensions/android/sms.py @@ -12,20 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. +from selenium.common.exceptions import UnknownMethodException from typing_extensions import Self from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from appium.webdriver.mobilecommand import MobileCommand as Command -class Sms(CanExecuteCommands, CanExecuteScripts): +class Sms(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): def send_sms(self, phone_number: str, message: str) -> Self: """Emulate send SMS event on the connected emulator. Android only. - Requires the Appium driver to support the `mobile: sendSms` execute method. - Args: phone_number: The phone number of message sender message: The message to send @@ -38,5 +39,12 @@ def send_sms(self, phone_number: str, message: str) -> Self: """ ext_name = 'mobile: sendSms' args = {'phoneNumber': phone_number, 'message': message} - self.execute_script(ext_name, args) + try: + self.assert_extension_exists(ext_name).execute_script(ext_name, args) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.SEND_SMS, args) return self + + def _add_commands(self) -> None: + self.command_executor.add_command(Command.SEND_SMS, 'POST', '/session/$sessionId/appium/device/send_sms') diff --git a/appium/webdriver/extensions/android/system_bars.py b/appium/webdriver/extensions/android/system_bars.py index 80ea273e2..a02c21f8f 100644 --- a/appium/webdriver/extensions/android/system_bars.py +++ b/appium/webdriver/extensions/android/system_bars.py @@ -12,19 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Dict, Union + +from selenium.common.exceptions import UnknownMethodException from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from appium.webdriver.mobilecommand import MobileCommand as Command -class SystemBars(CanExecuteCommands, CanExecuteScripts): - def get_system_bars(self) -> dict[str, dict[str, int | bool]]: +class SystemBars(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): + def get_system_bars(self) -> Dict[str, Dict[str, Union[int, bool]]]: """Retrieve visibility and bounds information of the status and navigation bars. Android only. - Requires the Appium driver to support the `mobile: getSystemBars` execute method. - Returns: A dictionary whose keys are - statusBar @@ -41,4 +44,15 @@ def get_system_bars(self) -> dict[str, dict[str, int | bool]]: - height """ ext_name = 'mobile: getSystemBars' - return self.execute_script(ext_name) + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name) + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute(Command.GET_SYSTEM_BARS)['value'] + + def _add_commands(self) -> None: + self.command_executor.add_command( + Command.GET_SYSTEM_BARS, + 'GET', + '/session/$sessionId/appium/device/system_bars', + ) diff --git a/appium/webdriver/extensions/applications.py b/appium/webdriver/extensions/applications.py index 583e8ba5e..a6782bfa9 100644 --- a/appium/webdriver/extensions/applications.py +++ b/appium/webdriver/extensions/applications.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any +from typing import Any, Dict, Union from typing_extensions import Self @@ -172,7 +172,7 @@ class for more details. }, ) - def app_strings(self, language: str | None = None, string_file: str | None = None) -> dict[str, str]: + def app_strings(self, language: Union[str, None] = None, string_file: Union[str, None] = None) -> Dict[str, str]: """Returns the application strings from the device for the specified language. @@ -190,3 +190,6 @@ def app_strings(self, language: str | None = None, string_file: str | None = Non if string_file is not None: data['stringFile'] = string_file return self.execute_script(ext_name, data) + + def _add_commands(self) -> None: + pass diff --git a/appium/webdriver/extensions/clipboard.py b/appium/webdriver/extensions/clipboard.py index 9afe0923c..f5354f2e7 100644 --- a/appium/webdriver/extensions/clipboard.py +++ b/appium/webdriver/extensions/clipboard.py @@ -13,22 +13,25 @@ # limitations under the License. import base64 +from typing import Optional +from selenium.common.exceptions import UnknownMethodException from typing_extensions import Self from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence from appium.webdriver.clipboard_content_type import ClipboardContentType +from ..mobilecommand import MobileCommand as Command -class Clipboard(CanExecuteCommands, CanExecuteScripts): + +class Clipboard(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): def set_clipboard( - self, content: bytes, content_type: str = ClipboardContentType.PLAINTEXT, label: str | None = None + self, content: bytes, content_type: str = ClipboardContentType.PLAINTEXT, label: Optional[str] = None ) -> Self: """Set the content of the system clipboard - Requires the Appium driver to support the `mobile: setClipboard` execute method. - Args: content: The content to be set as bytearray string content_type: One of ClipboardContentType items. Only ClipboardContentType.PLAINTEXT @@ -45,14 +48,16 @@ def set_clipboard( } if label: options['label'] = label - self.execute_script(ext_name, options) + try: + self.assert_extension_exists(ext_name).execute_script(ext_name, options) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.SET_CLIPBOARD, options) return self - def set_clipboard_text(self, text: str, label: str | None = None) -> Self: + def set_clipboard_text(self, text: str, label: Optional[str] = None) -> Self: """Copies the given text to the system clipboard - Requires the Appium driver to support the `mobile: setClipboard` execute method. - Args: text: The text to be set label:label argument, which only works for Android @@ -65,8 +70,6 @@ def set_clipboard_text(self, text: str, label: str | None = None) -> Self: def get_clipboard(self, content_type: str = ClipboardContentType.PLAINTEXT) -> bytes: """Receives the content of the system clipboard - Requires the Appium driver to support the `mobile: getClipboard` execute method. - Args: content_type: One of ClipboardContentType items. Only ClipboardContentType.PLAINTEXT is supported on Android @@ -76,15 +79,29 @@ def get_clipboard(self, content_type: str = ClipboardContentType.PLAINTEXT) -> b """ ext_name = 'mobile: getClipboard' options = {'contentType': content_type} - base64_str = self.execute_script(ext_name, options) + try: + base64_str = self.assert_extension_exists(ext_name).execute_script(ext_name, options) + except UnknownMethodException: + # TODO: Remove the fallback + base64_str = self.mark_extension_absence(ext_name).execute(Command.GET_CLIPBOARD, options)['value'] return base64.b64decode(base64_str) def get_clipboard_text(self) -> str: """Receives the text of the system clipboard - Requires the Appium driver to support the `mobile: getClipboard` execute method. - Returns: The actual clipboard text or an empty string if the clipboard is empty """ return self.get_clipboard(ClipboardContentType.PLAINTEXT).decode('UTF-8') + + def _add_commands(self) -> None: + self.command_executor.add_command( + Command.SET_CLIPBOARD, + 'POST', + '/session/$sessionId/appium/device/set_clipboard', + ) + self.command_executor.add_command( + Command.GET_CLIPBOARD, + 'POST', + '/session/$sessionId/appium/device/get_clipboard', + ) diff --git a/appium/webdriver/extensions/context.py b/appium/webdriver/extensions/context.py index fff3ecc30..628432e11 100644 --- a/appium/webdriver/extensions/context.py +++ b/appium/webdriver/extensions/context.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import List from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands @@ -20,7 +21,7 @@ class Context(CanExecuteCommands): @property - def contexts(self) -> list[str]: + def contexts(self) -> List[str]: """Returns the contexts within the current session. Usage: diff --git a/appium/webdriver/extensions/device_time.py b/appium/webdriver/extensions/device_time.py index 3eef3dda9..22ac3c25a 100644 --- a/appium/webdriver/extensions/device_time.py +++ b/appium/webdriver/extensions/device_time.py @@ -12,29 +12,35 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Optional + +from selenium.common.exceptions import UnknownMethodException from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence + +from ..mobilecommand import MobileCommand as Command -class DeviceTime(CanExecuteCommands, CanExecuteScripts): +class DeviceTime(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): @property def device_time(self) -> str: """Returns the date and time from the device. - Requires the Appium driver to support the `mobile: getDeviceTime` execute method. - Return: str: The date and time """ ext_name = 'mobile: getDeviceTime' - return self.execute_script(ext_name) + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name) + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute(Command.GET_DEVICE_TIME_GET, {})['value'] - def get_device_time(self, format: str | None = None) -> str: + def get_device_time(self, format: Optional[str] = None) -> str: """Returns the date and time from the device. - Requires the Appium driver to support the `mobile: getDeviceTime` execute method. - Args: format: The set of format specifiers. Read https://momentjs.com/docs/ to get the full list of supported datetime format specifiers. @@ -51,4 +57,19 @@ def get_device_time(self, format: str | None = None) -> str: ext_name = 'mobile: getDeviceTime' if format is None: return self.device_time - return self.execute_script(ext_name, {'format': format}) + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name, {'format': format}) + except UnknownMethodException: + return self.mark_extension_absence(ext_name).execute(Command.GET_DEVICE_TIME_POST, {'format': format})['value'] + + def _add_commands(self) -> None: + self.command_executor.add_command( + Command.GET_DEVICE_TIME_GET, + 'GET', + '/session/$sessionId/appium/device/system_time', + ) + self.command_executor.add_command( + Command.GET_DEVICE_TIME_POST, + 'POST', + '/session/$sessionId/appium/device/system_time', + ) diff --git a/appium/webdriver/extensions/execute_driver.py b/appium/webdriver/extensions/execute_driver.py index aa479ed8a..698c0218b 100644 --- a/appium/webdriver/extensions/execute_driver.py +++ b/appium/webdriver/extensions/execute_driver.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any +from typing import Any, Dict, Optional, Union from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands @@ -21,7 +21,7 @@ class ExecuteDriver(CanExecuteCommands): # TODO Inner class case - def execute_driver(self, script: str, script_type: str = 'webdriverio', timeout_ms: int | None = None) -> Any: + def execute_driver(self, script: str, script_type: str = 'webdriverio', timeout_ms: Optional[int] = None) -> Any: """Run a set of script against the current session, allowing execution of many commands in one Appium request. Please read http://appium.io/docs/en/commands/session/execute-driver for more details about the acceptable scripts and the output format. @@ -45,11 +45,11 @@ def execute_driver(self, script: str, script_type: str = 'webdriverio', timeout_ """ class Result: - def __init__(self, res: dict): + def __init__(self, res: Dict): self.result = res['result'] self.logs = res['logs'] - option: dict[str, str | int] = {'script': script, 'type': script_type} + option: Dict[str, Union[str, int]] = {'script': script, 'type': script_type} if timeout_ms is not None: option['timeout'] = timeout_ms diff --git a/appium/webdriver/extensions/execute_mobile_command.py b/appium/webdriver/extensions/execute_mobile_command.py index 2cc8c172a..8d78d418b 100644 --- a/appium/webdriver/extensions/execute_mobile_command.py +++ b/appium/webdriver/extensions/execute_mobile_command.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any +from typing import Any, Dict from typing_extensions import Self @@ -39,7 +39,7 @@ def press_button(self, button_name: str) -> Self: return self @property - def battery_info(self) -> dict[str, Any]: + def battery_info(self) -> Dict[str, Any]: """Retrieves battery information for the device under test. Returns: diff --git a/appium/webdriver/extensions/flutter_integration/flutter_commands.py b/appium/webdriver/extensions/flutter_integration/flutter_commands.py index 365ee29f4..5e469cae1 100644 --- a/appium/webdriver/extensions/flutter_integration/flutter_commands.py +++ b/appium/webdriver/extensions/flutter_integration/flutter_commands.py @@ -13,7 +13,7 @@ # limitations under the License. import os -from typing import Any, Union +from typing import Any, Dict, List, Optional, Tuple, Union from appium.common.helper import encode_file_to_base64 from appium.webdriver.extensions.flutter_integration.flutter_finder import FlutterFinder @@ -30,8 +30,8 @@ def __init__(self, driver: WebDriver) -> None: def wait_for_visible( self, - locator: WebElement | FlutterFinder, - timeout: float | None = None, + locator: Union[WebElement, FlutterFinder], + timeout: Optional[float] = None, ) -> None: """ Waits for a element to become visible. @@ -43,7 +43,7 @@ def wait_for_visible( Returns: None """ - opts: dict[str, Any] = self.__get_locator_options(locator) + opts: Dict[str, Any] = self.__get_locator_options(locator) if timeout is not None: opts['timeout'] = timeout @@ -51,8 +51,8 @@ def wait_for_visible( def wait_for_invisible( self, - locator: WebElement | FlutterFinder, - timeout: float | None = None, + locator: Union[WebElement, FlutterFinder], + timeout: Optional[float] = None, ) -> None: """ Waits for a element to become invisible. @@ -64,7 +64,7 @@ def wait_for_invisible( Returns: None: """ - opts: dict[str, Any] = self.__get_locator_options(locator) + opts: Dict[str, Any] = self.__get_locator_options(locator) if timeout is not None: opts['timeout'] = timeout @@ -72,7 +72,7 @@ def wait_for_invisible( # flutter action commands - def perform_double_click(self, element: WebElement, offset: tuple[int, int] | None = None) -> None: + def perform_double_click(self, element: WebElement, offset: Optional[Tuple[int, int]] = None) -> None: """ Performs a double-click on the given element, with an optional offset. @@ -83,12 +83,12 @@ def perform_double_click(self, element: WebElement, offset: tuple[int, int] | No Returns: None: """ - opts: dict[str, WebElement | dict[str, int]] = {'origin': element} + opts: Dict[str, Union[WebElement, Dict[str, int]]] = {'origin': element} if offset is not None: opts['offset'] = {'x': offset[0], 'y': offset[1]} self.execute_flutter_command('doubleClick', opts) - def perform_long_press(self, element: WebElement, offset: tuple[int, int] | None = None) -> None: + def perform_long_press(self, element: WebElement, offset: Optional[Tuple[int, int]] = None) -> None: """ Performs a long press on the given element, with an optional offset. @@ -99,7 +99,7 @@ def perform_long_press(self, element: WebElement, offset: tuple[int, int] | None Returns: None: """ - opts: dict[str, WebElement | dict[str, int]] = {'origin': element} + opts: Dict[str, Union[WebElement, Dict[str, int]]] = {'origin': element} if offset is not None: opts['offset'] = {'x': offset[0], 'y': offset[1]} self.execute_flutter_command('longPress', opts) @@ -154,7 +154,10 @@ def inject_mock_image(self, value: str) -> str: Returns: str: Image ID of the injected image. """ - base64_encoded_image = encode_file_to_base64(value) if os.path.isfile(value) else value + if os.path.isfile(value): + base64_encoded_image = encode_file_to_base64(value) + else: + base64_encoded_image = value return self.execute_flutter_command('injectImage', {'base64Image': base64_encoded_image}) def activate_injected_image(self, image_id: str) -> None: @@ -171,10 +174,10 @@ def activate_injected_image(self, image_id: str) -> None: def get_render_tree( self, - widget_type: str | None = None, - key: str | None = None, - text: str | None = None, - ) -> list[dict | None]: + widget_type: Optional[str] = None, + key: Optional[str] = None, + text: Optional[str] = None, + ) -> List[Optional[Dict]]: """ Returns the render tree of the root widget. @@ -287,7 +290,7 @@ def execute_flutter_command(self, scriptName: str, params: dict) -> Any: """ return self.driver.execute_script(f'flutter: {scriptName}', params) - def __get_locator_options(self, locator: Union[WebElement, 'FlutterFinder']) -> dict[str, dict | WebElement]: + def __get_locator_options(self, locator: Union[WebElement, 'FlutterFinder']) -> Dict[str, Union[dict, WebElement]]: if isinstance(locator, WebElement): return {'element': locator} return {'locator': locator.to_dict()} diff --git a/appium/webdriver/extensions/flutter_integration/flutter_finder.py b/appium/webdriver/extensions/flutter_integration/flutter_finder.py index a92425f27..0aed46a9a 100644 --- a/appium/webdriver/extensions/flutter_integration/flutter_finder.py +++ b/appium/webdriver/extensions/flutter_integration/flutter_finder.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -from typing import cast +from typing import Tuple, Union, cast from selenium.webdriver.common.by import ByType as SeleniumByType @@ -24,7 +24,7 @@ class FlutterFinder: - def __init__(self, using: SeleniumByType | AppiumByType, value: str) -> None: + def __init__(self, using: Union[SeleniumByType, AppiumByType], value: str) -> None: self.using = using self.value = value @@ -51,5 +51,5 @@ def by_text_containing(value: str) -> 'FlutterFinder': def to_dict(self) -> dict: return {'using': self.using, 'value': self.value} - def as_args(self) -> tuple[str, str]: + def as_args(self) -> Tuple[str, str]: return self.using, self.value diff --git a/appium/webdriver/extensions/hw_actions.py b/appium/webdriver/extensions/hw_actions.py index c3bc5527c..b6bbb468b 100644 --- a/appium/webdriver/extensions/hw_actions.py +++ b/appium/webdriver/extensions/hw_actions.py @@ -12,18 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Optional +from selenium.common.exceptions import UnknownMethodException from typing_extensions import Self from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from ..mobilecommand import MobileCommand as Command -class HardwareActions(CanExecuteCommands, CanExecuteScripts): - def lock(self, seconds: int | None = None) -> Self: - """Lock the device. No changes are made if the device is already locked. - Requires the Appium driver to support the `mobile: lock` execute method. +class HardwareActions(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): + def lock(self, seconds: Optional[int] = None) -> Self: + """Lock the device. No changes are made if the device is already unlocked. Args: seconds: The duration to lock the device, in seconds. @@ -36,44 +39,54 @@ def lock(self, seconds: int | None = None) -> Self: """ ext_name = 'mobile: lock' args = {'seconds': seconds or 0} - self.execute_script(ext_name, args) + try: + self.assert_extension_exists(ext_name).execute_script(ext_name, args) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.LOCK, args) return self def unlock(self) -> Self: - """Unlock the device. No changes are made if the device is already unlocked. - - Requires the Appium driver to support the `mobile: isLocked` and `mobile: unlock` execute methods. + """Unlock the device. No changes are made if the device is already locked. Returns: Union['WebDriver', 'HardwareActions']: Self instance """ ext_name = 'mobile: unlock' - if not self.execute_script('mobile: isLocked'): - return self - self.execute_script(ext_name) + try: + if not self.assert_extension_exists(ext_name).execute_script('mobile: isLocked'): + return self + self.execute_script(ext_name) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.UNLOCK) return self def is_locked(self) -> bool: """Checks whether the device is locked. - Requires the Appium driver to support the `mobile: isLocked` execute method. - Returns: `True` if the device is locked """ ext_name = 'mobile: isLocked' - return self.execute_script(ext_name) + try: + return self.assert_extension_exists(ext_name).execute_script('mobile: isLocked') + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute(Command.IS_LOCKED)['value'] def shake(self) -> Self: """Shake the device. - Requires the Appium driver to support the `mobile: shake` execute method. - Returns: Union['WebDriver', 'HardwareActions']: Self instance """ ext_name = 'mobile: shake' - self.execute_script(ext_name) + try: + self.assert_extension_exists(ext_name).execute_script(ext_name) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.SHAKE) return self def touch_id(self, match: bool) -> Self: @@ -105,17 +118,32 @@ def toggle_touch_id_enrollment(self) -> Self: return self def finger_print(self, finger_id: int) -> Self: - """Authenticate users using a fingerprint scan on supported Android emulators. - - Requires the Appium driver to support the `mobile: fingerprint` execute method. + """Authenticate users by using their finger print scans on supported Android emulators. Args: - finger_id: Fingerprint identifier stored in the Android Keystore system (from 1 to 10) - - Returns: - Union['WebDriver', 'HardwareActions']: Self instance + finger_id: Finger prints stored in Android Keystore system (from 1 to 10) """ ext_name = 'mobile: fingerprint' args = {'fingerprintId': finger_id} - self.execute_script(ext_name, args) + try: + self.assert_extension_exists(ext_name).execute_script(ext_name, args) + except UnknownMethodException: + self.mark_extension_absence(ext_name).execute(Command.FINGER_PRINT, args) return self + + def _add_commands(self) -> None: + self.command_executor.add_command(Command.LOCK, 'POST', '/session/$sessionId/appium/device/lock') + self.command_executor.add_command(Command.UNLOCK, 'POST', '/session/$sessionId/appium/device/unlock') + self.command_executor.add_command(Command.IS_LOCKED, 'POST', '/session/$sessionId/appium/device/is_locked') + self.command_executor.add_command(Command.SHAKE, 'POST', '/session/$sessionId/appium/device/shake') + self.command_executor.add_command(Command.TOUCH_ID, 'POST', '/session/$sessionId/appium/simulator/touch_id') + self.command_executor.add_command( + Command.TOGGLE_TOUCH_ID_ENROLLMENT, + 'POST', + '/session/$sessionId/appium/simulator/toggle_touch_id_enrollment', + ) + self.command_executor.add_command( + Command.FINGER_PRINT, + 'POST', + '/session/$sessionId/appium/device/finger_print', + ) diff --git a/appium/webdriver/extensions/images_comparison.py b/appium/webdriver/extensions/images_comparison.py index 98ae3ecf5..04b8f1147 100644 --- a/appium/webdriver/extensions/images_comparison.py +++ b/appium/webdriver/extensions/images_comparison.py @@ -12,17 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any +from typing import Any, Dict, Union from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from ..mobilecommand import MobileCommand as Command -Base64Payload = str | bytes +Base64Payload = Union[str, bytes] class ImagesComparison(CanExecuteCommands): - def match_images_features(self, base64_image1: Base64Payload, base64_image2: Base64Payload, **opts: Any) -> dict[str, Any]: + def match_images_features(self, base64_image1: Base64Payload, base64_image2: Base64Payload, **opts: Any) -> Dict[str, Any]: """Performs images matching by features. Read @@ -78,7 +78,7 @@ def match_images_features(self, base64_image1: Base64Payload, base64_image2: Bas def find_image_occurrence( self, base64_full_image: Base64Payload, base64_partial_image: Base64Payload, **opts: Any - ) -> dict[str, bytes | dict]: + ) -> Dict[str, Union[bytes, Dict]]: """Performs images matching by template to find possible occurrence of the partial image in the full image. @@ -112,7 +112,7 @@ def find_image_occurrence( def get_images_similarity( self, base64_image1: Base64Payload, base64_image2: Base64Payload, **opts: Any - ) -> dict[str, bytes | dict]: + ) -> Dict[str, Union[bytes, Dict]]: """Performs images matching to calculate the similarity score between them. The flow there is similar to the one used in diff --git a/appium/webdriver/extensions/keyboard.py b/appium/webdriver/extensions/keyboard.py index 725efafd0..4640b1163 100644 --- a/appium/webdriver/extensions/keyboard.py +++ b/appium/webdriver/extensions/keyboard.py @@ -12,53 +12,69 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Dict, Optional +from selenium.common.exceptions import UnknownMethodException from typing_extensions import Self from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from ..mobilecommand import MobileCommand as Command -class Keyboard(CanExecuteCommands, CanExecuteScripts): - def hide_keyboard(self, key_name: str | None = None, key: str | None = None, strategy: str | None = None) -> Self: - """Hides the software keyboard on the device. - On iOS, use `key_name` or `key` to provide a keyboard key name. - On Android, no parameters are used. `strategy` is retained for compatibility and ignored. +class Keyboard(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): + def hide_keyboard(self, key_name: Optional[str] = None, key: Optional[str] = None, strategy: Optional[str] = None) -> Self: + """Hides the software keyboard on the device. - Requires the Appium driver to support the `mobile: hideKeyboard` execute method. + In iOS, use `key_name` to press + a particular key, or `strategy`. In Android, no parameters are used. Args: - key_name: Keyboard key name to use on iOS - key: Alias for `key_name` - strategy: Legacy argument retained for compatibility; ignored by `mobile: hideKeyboard` + key_name: key to press + key: + strategy: strategy for closing the keyboard (e.g., `tapOutside`) Returns: Union['WebDriver', 'Keyboard']: Self instance """ ext_name = 'mobile: hideKeyboard' - self.execute_script(ext_name, {**({'keys': [key or key_name]} if key or key_name else {})}) + try: + self.assert_extension_exists(ext_name).execute_script( + ext_name, {**({'keys': [key or key_name]} if key or key_name else {})} + ) + except UnknownMethodException: + # TODO: Remove the fallback + data: Dict[str, Optional[str]] = {} + if key_name is not None: + data['keyName'] = key_name + elif key is not None: + data['key'] = key + elif strategy is None: + strategy = 'tapOutside' + data['strategy'] = strategy + self.mark_extension_absence(ext_name).execute(Command.HIDE_KEYBOARD, data) return self def is_keyboard_shown(self) -> bool: """Attempts to detect whether a software keyboard is present - Requires the Appium driver to support the `mobile: isKeyboardShown` execute method. - Returns: `True` if keyboard is shown """ ext_name = 'mobile: isKeyboardShown' - return self.execute_script(ext_name) + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name) + except UnknownMethodException: + return self.mark_extension_absence(ext_name).execute(Command.IS_KEYBOARD_SHOWN)['value'] - def keyevent(self, keycode: int, metastate: int | None = None) -> Self: + def keyevent(self, keycode: int, metastate: Optional[int] = None) -> Self: """Sends a keycode to the device. Android only. Possible keycodes can be found in http://developer.android.com/reference/android/view/KeyEvent.html. - Requires the Appium driver to support the `mobile: pressKey` execute method. - Args: keycode: the keycode to be sent to the device metastate: meta information about the keycode being sent @@ -68,14 +84,12 @@ def keyevent(self, keycode: int, metastate: int | None = None) -> Self: """ return self.press_keycode(keycode=keycode, metastate=metastate) - def press_keycode(self, keycode: int, metastate: int | None = None, flags: int | None = None) -> Self: + def press_keycode(self, keycode: int, metastate: Optional[int] = None, flags: Optional[int] = None) -> Self: """Sends a keycode to the device. Android only. Possible keycodes can be found in http://developer.android.com/reference/android/view/KeyEvent.html. - Requires the Appium driver to support the `mobile: pressKey` execute method. - Args: keycode: the keycode to be sent to the device metastate: meta information about the keycode being sent @@ -90,17 +104,19 @@ def press_keycode(self, keycode: int, metastate: int | None = None, flags: int | args['metastate'] = metastate if flags is not None: args['flags'] = flags - self.execute_script(ext_name, args) + try: + self.assert_extension_exists(ext_name).execute_script(ext_name, args) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.PRESS_KEYCODE, args) return self - def long_press_keycode(self, keycode: int, metastate: int | None = None, flags: int | None = None) -> Self: + def long_press_keycode(self, keycode: int, metastate: Optional[int] = None, flags: Optional[int] = None) -> Self: """Sends a long press of keycode to the device. Android only. Possible keycodes can be found in http://developer.android.com/reference/android/view/KeyEvent.html. - Requires the Appium driver to support the `mobile: pressKey` execute method. - Args: keycode: the keycode to be sent to the device metastate: meta information about the keycode being sent @@ -115,11 +131,38 @@ def long_press_keycode(self, keycode: int, metastate: int | None = None, flags: args['metastate'] = metastate if flags is not None: args['flags'] = flags - self.execute_script( - ext_name, - { - **args, - 'isLongPress': True, - }, - ) + try: + self.assert_extension_exists(ext_name).execute_script( + ext_name, + { + **args, + 'isLongPress': True, + }, + ) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute(Command.LONG_PRESS_KEYCODE, args) return self + + def _add_commands(self) -> None: + self.command_executor.add_command( + Command.HIDE_KEYBOARD, + 'POST', + '/session/$sessionId/appium/device/hide_keyboard', + ) + self.command_executor.add_command( + Command.IS_KEYBOARD_SHOWN, + 'GET', + '/session/$sessionId/appium/device/is_keyboard_shown', + ) + self.command_executor.add_command(Command.KEY_EVENT, 'POST', '/session/$sessionId/appium/device/keyevent') + self.command_executor.add_command( + Command.PRESS_KEYCODE, + 'POST', + '/session/$sessionId/appium/device/press_keycode', + ) + self.command_executor.add_command( + Command.LONG_PRESS_KEYCODE, + 'POST', + '/session/$sessionId/appium/device/long_press_keycode', + ) diff --git a/appium/webdriver/extensions/location.py b/appium/webdriver/extensions/location.py index 9dfd759a5..fc1fcae28 100644 --- a/appium/webdriver/extensions/location.py +++ b/appium/webdriver/extensions/location.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Dict, Union from typing_extensions import Self @@ -36,11 +37,11 @@ def toggle_location_services(self) -> Self: def set_location( self, - latitude: float | str, - longitude: float | str, - altitude: float | str | None = None, - speed: float | str | None = None, - satellites: float | str | None = None, + latitude: Union[float, str], + longitude: Union[float, str], + altitude: Union[float, str, None] = None, + speed: Union[float, str, None] = None, + satellites: Union[float, str, None] = None, ) -> Self: """Set the location of the device @@ -70,7 +71,7 @@ def set_location( return self @property - def location(self) -> dict[str, float]: + def location(self) -> Dict[str, float]: """Retrieves the current location Returns: diff --git a/appium/webdriver/extensions/log_event.py b/appium/webdriver/extensions/log_event.py index 549dd0555..4ca53b3fe 100644 --- a/appium/webdriver/extensions/log_event.py +++ b/appium/webdriver/extensions/log_event.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Dict, List, Union from typing_extensions import Self @@ -21,7 +22,7 @@ class LogEvent(CanExecuteCommands): - def get_events(self, type: list[str] | None = None) -> dict[str, str | int]: + def get_events(self, type: Union[List[str], None] = None) -> Dict[str, Union[str, int]]: """Retrieves events information from the current session (Since Appium 1.16.0) diff --git a/appium/webdriver/extensions/logs.py b/appium/webdriver/extensions/logs.py index 1913b79c5..53e165944 100644 --- a/appium/webdriver/extensions/logs.py +++ b/appium/webdriver/extensions/logs.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any +from typing import Any, Dict, List from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands @@ -21,7 +21,7 @@ class Logs(CanExecuteCommands): @property - def log_types(self) -> list[str]: + def log_types(self) -> List[str]: """Gets a list of the available log types. This only works with w3c compliant browsers. @@ -32,7 +32,7 @@ def log_types(self) -> list[str]: """ return self.execute(Command.GET_AVAILABLE_LOG_TYPES)['value'] - def get_log(self, log_type: str) -> list[dict[str, Any]]: + def get_log(self, log_type: str) -> List[Dict[str, Any]]: """Gets the log for a given log type. Args: diff --git a/appium/webdriver/extensions/remote_fs.py b/appium/webdriver/extensions/remote_fs.py index 83d5716e3..5ccebd37d 100644 --- a/appium/webdriver/extensions/remote_fs.py +++ b/appium/webdriver/extensions/remote_fs.py @@ -13,20 +13,22 @@ # limitations under the License. import base64 +from typing import Optional -from selenium.common.exceptions import InvalidArgumentException +from selenium.common.exceptions import InvalidArgumentException, UnknownMethodException from typing_extensions import Self from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts +from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence +from ..mobilecommand import MobileCommand as Command -class RemoteFS(CanExecuteCommands, CanExecuteScripts): + +class RemoteFS(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence): def pull_file(self, path: str) -> str: """Retrieves the file at `path`. - Requires the Appium driver to support the `mobile: pullFile` execute method. - Args: path: the path to the file on the device @@ -34,13 +36,15 @@ def pull_file(self, path: str) -> str: The file's contents encoded as Base64. """ ext_name = 'mobile: pullFile' - return self.execute_script(ext_name, {'remotePath': path}) + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name, {'remotePath': path}) + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute(Command.PULL_FILE, {'path': path})['value'] def pull_folder(self, path: str) -> str: """Retrieves a folder at `path`. - Requires the Appium driver to support the `mobile: pullFolder` execute method. - Args: path: the path to the folder on the device @@ -48,19 +52,21 @@ def pull_folder(self, path: str) -> str: The folder's contents zipped and encoded as Base64. """ ext_name = 'mobile: pullFolder' - return self.execute_script(ext_name, {'remotePath': path}) + try: + return self.assert_extension_exists(ext_name).execute_script(ext_name, {'remotePath': path}) + except UnknownMethodException: + # TODO: Remove the fallback + return self.mark_extension_absence(ext_name).execute(Command.PULL_FOLDER, {'path': path})['value'] - def push_file(self, destination_path: str, base64data: str | None = None, source_path: str | None = None) -> Self: - """Puts the data from the file at `source_path`, encoded as Base64, at `destination_path`. + def push_file(self, destination_path: str, base64data: Optional[str] = None, source_path: Optional[str] = None) -> Self: + """Puts the data from the file at `source_path`, encoded as Base64, in the file specified as `path`. - Specify either `base64data` or `source_path`. If both are provided, `source_path` takes precedence. - - Requires the Appium driver to support the `mobile: pushFile` execute method. + Specify either `base64data` or `source_path`, if both specified default to `source_path` Args: destination_path: the location on the device/simulator where the local file contents should be saved base64data: file contents, encoded as Base64, to be written - to the file on the device/simulator + to the file on the device/simulator source_path: local file path for the file to be loaded on device Returns: @@ -73,17 +79,32 @@ def push_file(self, destination_path: str, base64data: str | None = None, source try: with open(source_path, 'rb') as f: file_data = f.read() - except OSError as e: + except IOError as e: message = f'source_path "{source_path}" could not be found. Are you sure the file exists?' raise InvalidArgumentException(message) from e base64data = base64.b64encode(file_data).decode('utf-8') ext_name = 'mobile: pushFile' - self.execute_script( - ext_name, - { - 'remotePath': destination_path, - 'payload': base64data, - }, - ) + try: + self.assert_extension_exists(ext_name).execute_script( + ext_name, + { + 'remotePath': destination_path, + 'payload': base64data, + }, + ) + except UnknownMethodException: + # TODO: Remove the fallback + self.mark_extension_absence(ext_name).execute( + Command.PUSH_FILE, + { + 'path': destination_path, + 'data': base64data, + }, + ) return self + + def _add_commands(self) -> None: + self.command_executor.add_command(Command.PULL_FILE, 'POST', '/session/$sessionId/appium/device/pull_file') + self.command_executor.add_command(Command.PULL_FOLDER, 'POST', '/session/$sessionId/appium/device/pull_folder') + self.command_executor.add_command(Command.PUSH_FILE, 'POST', '/session/$sessionId/appium/device/push_file') diff --git a/appium/webdriver/extensions/screen_record.py b/appium/webdriver/extensions/screen_record.py index 418b79e60..07c9b2bae 100644 --- a/appium/webdriver/extensions/screen_record.py +++ b/appium/webdriver/extensions/screen_record.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any +from typing import Any, Union from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands @@ -20,7 +20,7 @@ class ScreenRecord(CanExecuteCommands): - def start_recording_screen(self, **options: Any) -> bytes | str: + def start_recording_screen(self, **options: Any) -> Union[bytes, str]: """Start asynchronous screen recording process. +--------------+-----+---------+-----+-------+ @@ -151,9 +151,6 @@ def start_recording_screen(self, **options: Any) -> bytes | str: Possible values are 'ultrafast', 'superfast', 'veryfast'(default), 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow' - Usage: - driver.start_recording_screen() - Returns: bytes: Base-64 encoded content of the recorded media if `stop_recording_screen` isn't called after previous `start_recording_screen`. @@ -187,12 +184,6 @@ def stop_recording_screen(self, **options: Any) -> bytes: the same key as `fileFieldName` then it is going to be ignored. (Since Appium 1.18.0) headers (dict): [multipart/form-data requests] Headers mapping (Since Appium 1.18.0) - Usage: - payload = driver.stop_recording_screen() - with open('media.mp4', "wb") as fd: - import base64 - fd.write(base64.b64decode(payload)) - Returns: bytes: Base-64 encoded content of the recorded media file or an empty string if the file has been successfully uploaded to a remote location diff --git a/appium/webdriver/extensions/session.py b/appium/webdriver/extensions/session.py index 3fb15699b..e0bcd8b60 100644 --- a/appium/webdriver/extensions/session.py +++ b/appium/webdriver/extensions/session.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Dict from appium.common.logger import logger from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands @@ -21,7 +22,7 @@ class Session(CanExecuteCommands): @property - def events(self) -> dict: + def events(self) -> Dict: """Retrieves events information from the current session Usage: diff --git a/appium/webdriver/extensions/settings.py b/appium/webdriver/extensions/settings.py index 541da5dcc..d9a55111e 100644 --- a/appium/webdriver/extensions/settings.py +++ b/appium/webdriver/extensions/settings.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any +from typing import Any, Dict from typing_extensions import Self @@ -22,7 +22,7 @@ class Settings(CanExecuteCommands): - def get_settings(self) -> dict[str, Any]: + def get_settings(self) -> Dict[str, Any]: """Returns the appium server Settings for the current session. Do not get Settings confused with Desired Capabilities, they are @@ -33,7 +33,7 @@ def get_settings(self) -> dict[str, Any]: """ return self.execute(Command.GET_SETTINGS, {})['value'] - def update_settings(self, settings: dict[str, Any]) -> Self: + def update_settings(self, settings: Dict[str, Any]) -> Self: """Set settings for the current session. For more on settings, see: https://github.com/appium/appium/blob/master/docs/en/advanced-concepts/settings.md diff --git a/appium/webdriver/locator_converter.py b/appium/webdriver/locator_converter.py index 004b5b0b5..4c6416c67 100644 --- a/appium/webdriver/locator_converter.py +++ b/appium/webdriver/locator_converter.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Tuple from selenium.webdriver.remote.locator_converter import LocatorConverter @@ -24,5 +25,5 @@ class AppiumLocatorConverter(LocatorConverter): to the Appium server as-is. """ - def convert(self, by: str, value: str) -> tuple[str, str]: + def convert(self, by: str, value: str) -> Tuple[str, str]: return (by, value) diff --git a/appium/webdriver/switch_to.py b/appium/webdriver/switch_to.py index f61425247..ed9db7c76 100644 --- a/appium/webdriver/switch_to.py +++ b/appium/webdriver/switch_to.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Optional from selenium.webdriver.remote.switch_to import SwitchTo from typing_extensions import Self @@ -20,7 +21,7 @@ class MobileSwitchTo(SwitchTo): - def context(self, context_name: str | None) -> Self: + def context(self, context_name: Optional[str]) -> Self: """Sets the context for the current session. Passing `None` is equal to switching to native context. diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py index 5410409e7..1bd549881 100644 --- a/appium/webdriver/webdriver.py +++ b/appium/webdriver/webdriver.py @@ -12,13 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -import copy -from collections.abc import Callable -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union from selenium.common.exceptions import ( InvalidArgumentException, SessionNotCreatedException, + UnknownMethodException, WebDriverException, ) from selenium.webdriver.remote.command import Command as RemoteCommand @@ -26,6 +25,7 @@ # `selenium.webdriver.Remote` could be used instead, but Pyright wouldn't locate the class properly. from selenium.webdriver.remote.webdriver import WebDriver as Remote +from typing_extensions import Self from appium.common.logger import logger from appium.options.common.base import AppiumOptions @@ -64,9 +64,6 @@ from .switch_to import MobileSwitchTo from .webelement import WebElement as MobileWebElement -if TYPE_CHECKING: - from selenium.webdriver.common.by import By - class ExtensionBase: """ @@ -165,10 +162,10 @@ def add_command(self): """ - def __init__(self, execute: Callable[[str, dict], dict[str, Any]]): + def __init__(self, execute: Callable[[str, Dict], Dict[str, Any]]): self._execute = execute - def execute(self, parameters: dict[str, Any] | None = None) -> Any: + def execute(self, parameters: Union[Dict[str, Any], None] = None) -> Any: param = {} if parameters: param = parameters @@ -184,7 +181,7 @@ def method_name(self) -> str: """ raise NotImplementedError() - def add_command(self) -> tuple[str, str]: + def add_command(self) -> Tuple[str, str]: """ Expected to define the pair of HTTP method and its URL. """ @@ -192,8 +189,8 @@ def add_command(self) -> tuple[str, str]: def _get_remote_connection_and_client_config( - command_executor: str | AppiumConnection, client_config: AppiumClientConfig | None = None -) -> tuple[AppiumConnection, AppiumClientConfig | None]: + command_executor: Union[str, AppiumConnection], client_config: Optional[AppiumClientConfig] = None +) -> tuple[AppiumConnection, Optional[AppiumClientConfig]]: """Return the pair of command executor and client config. If the given command executor is a custom one, returned client config will be None since the custom command executor has its own client config already. @@ -243,17 +240,11 @@ class WebDriver( ): def __init__( self, - command_executor: str | AppiumConnection = 'http://127.0.0.1:4723', - extensions: list[type['ExtensionBase']] | None = None, - options: AppiumOptions | list[AppiumOptions] | None = None, - client_config: AppiumClientConfig | None = None, + command_executor: Union[str, AppiumConnection] = 'http://127.0.0.1:4723', + extensions: Optional[List[Type['ExtensionBase']]] = None, + options: Union[AppiumOptions, List[AppiumOptions], None] = None, + client_config: Optional[AppiumClientConfig] = None, ): - if isinstance(options, list): - # Normalize before Selenium separates common and alternative capabilities. - options = [ - AppiumOptions().load_capabilities(AppiumOptions.as_w3c(option.to_capabilities())['capabilities']['alwaysMatch']) - for option in options - ] command_executor, client_config = _get_remote_connection_and_client_config( command_executor=command_executor, client_config=client_config ) @@ -275,7 +266,7 @@ def __init__( if client_config and client_config.direct_connection: self._update_command_executor(keep_alive=client_config.keep_alive) - self._absent_extensions: set[str] = set() + self._absent_extensions: Set[str] = set() self._extensions = extensions or [] for extension in self._extensions: @@ -291,10 +282,10 @@ def __init__( if TYPE_CHECKING: - def find_element(self, by: str = By.ID, value: str | dict | None = None) -> 'MobileWebElement': # type: ignore[override] + def find_element(self, by: str, value: Union[str, Dict, None] = None) -> 'MobileWebElement': # type: ignore[override] ... - def find_elements(self, by: str = By.ID, value: str | dict | None = None) -> list['MobileWebElement']: # type: ignore[override] + def find_elements(self, by: str, value: Union[str, Dict, None] = None) -> List['MobileWebElement']: # type: ignore[override] ... def delete_extensions(self) -> None: @@ -314,10 +305,11 @@ def _update_command_executor(self, keep_alive: bool) -> None: if not self.caps: raise ValueError('Driver capabilities must be defined') - keys = (direct_protocol, direct_host, direct_port, direct_path) - if not set(keys).issubset(self.caps): - details = ' '.join(f"{key}: '{self.caps.get(key, '')}'" for key in keys) - logger.debug(f'Direct connect capabilities from server were:\n{details} ') + if not {direct_protocol, direct_host, direct_port, direct_path}.issubset(set(self.caps)): + message = 'Direct connect capabilities from server were:\n' + for key in [direct_protocol, direct_host, direct_port, direct_path]: + message += f"{key}: '{self.caps.get(key, '')}' " + logger.debug(message) return protocol = self.caps[direct_protocol] @@ -327,24 +319,16 @@ def _update_command_executor(self, keep_alive: bool) -> None: executor = f'{protocol}://{hostname}:{port}{path}' logger.debug('Updated request endpoint to %s', executor) - # Override command executor. - # The client configuration given by a user, e.g. the read timeout, the proxy or - # the authentication credentials, must be kept as-is. Only the endpoint the client - # talks to changes, thus a copy of the current configuration is reused instead of - # building a brand-new one out of the endpoint URL. - client_config = copy.copy(self.command_executor.client_config) - client_config.remote_server_addr = executor - client_config.keep_alive = keep_alive if isinstance(self.command_executor, AppiumConnection): # type: ignore - self.command_executor = AppiumConnection(client_config=client_config) + self.command_executor = AppiumConnection(executor, keep_alive=keep_alive) else: - self.command_executor = RemoteConnection(client_config=client_config) + self.command_executor = RemoteConnection(executor, keep_alive=keep_alive) self._add_commands() # https://github.com/SeleniumHQ/selenium/blob/06fdf2966df6bca47c0ae45e8201cd30db9b9a49/py/selenium/webdriver/remote/webdriver.py#L277 # noinspection PyAttributeOutsideInit - def start_session(self, capabilities: dict | AppiumOptions, browser_profile: str | None = None) -> None: + def start_session(self, capabilities: Union[Dict, AppiumOptions], browser_profile: Optional[str] = None) -> None: """Creates a new session with the desired capabilities. Override for Appium @@ -357,17 +341,7 @@ def start_session(self, capabilities: dict | AppiumOptions, browser_profile: str if not isinstance(capabilities, (dict, AppiumOptions)): raise InvalidArgumentException('Capabilities must be a dictionary or AppiumOptions instance') - if isinstance(capabilities, AppiumOptions): - w3c_caps = capabilities.to_w3c() - elif ( - set(capabilities) == {'capabilities'} - and isinstance(capabilities['capabilities'], dict) - and {'alwaysMatch', 'firstMatch'}.issubset(capabilities['capabilities']) - ): - # Selenium already creates the W3C envelope when given a list of options. - w3c_caps = copy.deepcopy(capabilities) - else: - w3c_caps = AppiumOptions.as_w3c(capabilities) + w3c_caps = AppiumOptions.as_w3c(capabilities) if isinstance(capabilities, dict) else capabilities.to_w3c() response = self.execute(RemoteCommand.NEW_SESSION, w3c_caps) # https://w3c.github.io/webdriver/#new-session if not isinstance(response, dict): @@ -377,7 +351,7 @@ def start_session(self, capabilities: dict | AppiumOptions, browser_profile: str # Due to a W3C spec parsing misconception some servers # pack the createSession response stuff into 'value' dictionary and # some other put it to the top level of the response JSON nesting hierarchy - get_response_value: Callable[[str], Any | None] = lambda key: ( + get_response_value: Callable[[str], Optional[Any]] = lambda key: ( response.get(key) or (response['value'].get(key) if isinstance(response.get('value'), dict) else None) ) session_id = get_response_value('sessionId') @@ -388,7 +362,7 @@ def start_session(self, capabilities: dict | AppiumOptions, browser_profile: str self.session_id = session_id self.caps = get_response_value('capabilities') or {} - def get_status(self) -> dict: + def get_status(self) -> Dict: """ Get the Appium server status @@ -401,7 +375,7 @@ def get_status(self) -> dict: """ return self.execute(Command.GET_STATUS)['value'] - def create_web_element(self, element_id: int | str) -> MobileWebElement: + def create_web_element(self, element_id: Union[int, str]) -> MobileWebElement: """Creates a web element with the specified element_id. Overrides method in Selenium WebDriver in order to always give them @@ -458,10 +432,44 @@ def orientation(self, value: str) -> None: """ allowed_values = ['LANDSCAPE', 'PORTRAIT'] if value.upper() in allowed_values: - self.execute(Command.SET_SCREEN_ORIENTATION, {'orientation': value}) + self.execute(Command.SET_SCREEN_ORIENTATION, {'orientation': value.upper()}) else: raise WebDriverException("You can only set the orientation to 'LANDSCAPE' and 'PORTRAIT'") + def assert_extension_exists(self, ext_name: str) -> Self: + """ + Verifies if the given extension is not present in the list of absent extensions + for the given driver instance. + This API is designed for private usage. + + Args: + ext_name: extension name + + Returns: + self instance for chaining + + Raises: + UnknownMethodException: If the extension has been marked as absent once + """ + if ext_name in self._absent_extensions: + raise UnknownMethodException() + return self + + def mark_extension_absence(self, ext_name: str) -> Self: + """ + Marks the given extension as absent for the given driver instance. + This API is designed for private usage. + + Args: + ext_name: extension name + + Returns: + self instance for chaining + """ + logger.debug(f'Marking driver extension "{ext_name}" as absent for the current instance') + self._absent_extensions.add(ext_name) + return self + def _add_commands(self) -> None: # call the overridden command binders from all mixin classes except for # appium.webdriver.webdriver.WebDriver and its sub-classes diff --git a/appium/webdriver/webelement.py b/appium/webdriver/webelement.py index 8494af13f..61076ca37 100644 --- a/appium/webdriver/webelement.py +++ b/appium/webdriver/webelement.py @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections.abc import Callable -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Union from selenium.webdriver.common.utils import keys_to_typing from selenium.webdriver.remote.command import Command as RemoteCommand @@ -29,13 +28,13 @@ class WebElement(SeleniumWebElement): if TYPE_CHECKING: - def find_element(self, by: str, value: str | dict | None = None) -> Self: # type: ignore[override] + def find_element(self, by: str, value: Union[str, Dict, None] = None) -> Self: # type: ignore[override] ... - def find_elements(self, by: str, value: str | dict | None = None) -> list[Self]: # type: ignore[override] + def find_elements(self, by: str, value: Union[str, Dict, None] = None) -> List[Self]: # type: ignore[override] ... - def get_attribute(self, name: str) -> str | dict | None: # type: ignore[override] + def get_attribute(self, name: str) -> Optional[Union[str, Dict]]: # type: ignore[override] """Gets the given attribute or property of the element. Override for Appium @@ -103,7 +102,7 @@ def clear(self) -> Self: # type: ignore[override] return self @property - def location_in_view(self) -> dict[str, int]: + def location_in_view(self) -> Dict[str, int]: """Gets the location of an element relative to the view. Usage: diff --git a/docs/conf.py b/docs/conf.py index 10f4c7fb6..236b9e5d3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,12 +18,12 @@ # -- Project information ----------------------------------------------------- -project = 'Python client 6.0.5' -copyright = '2020-2026, Appium' +project = 'Python client 1.1' +copyright = '2020-2025, Appium' author = 'Appium' # The full version, including alpha/beta/rc tags -release = '6.0.5' +release = '1.1' language = 'en' diff --git a/pyproject.toml b/pyproject.toml index a1ce9fe73..27907b7e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "Appium-Python-Client" description = "Python client for Appium" -version = "6.0.6" +version = "5.3.1" readme = "README.md" license = "Apache-2.0" license-files = ["LICENSE"] @@ -27,8 +27,8 @@ classifiers = [ ] requires-python = ">=3.10" dependencies = [ - "selenium>=4.37.0,<5.0", - "typing-extensions~=4.16", + "selenium>=4.26,<5.0", + "typing-extensions~=4.13", ] [project.urls] @@ -41,13 +41,13 @@ Changelog = "https://github.com/appium/python-client/blob/master/CHANGELOG.md" dev = [ "httpretty~=1.1", "mock~=5.2", - "mypy>=1.17,<3.0", - "pre-commit~=4.6", - "pytest>=8.4,<10.0", + "mypy~=1.17", + "pre-commit~=4.2", + "pytest~=8.4", "pytest-cov>=6.2,<8.0", "pytest-xdist~=3.8", "python-dateutil~=2.9", - "ruff~=0.16", + "ruff~=0.15", "types-python-dateutil~=2.9", # for release diff --git a/test/functional/android/appium_service_tests.py b/test/functional/android/appium_service_tests.py index e35e5e381..409b80989 100644 --- a/test/functional/android/appium_service_tests.py +++ b/test/functional/android/appium_service_tests.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections.abc import Generator +from typing import Generator import pytest diff --git a/test/functional/android/bidi_tests.py b/test/functional/android/bidi_tests.py index 1e8b463f4..146f29a55 100644 --- a/test/functional/android/bidi_tests.py +++ b/test/functional/android/bidi_tests.py @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections.abc import Generator -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Generator import pytest from selenium.webdriver.common.bidi.common import command_builder diff --git a/test/functional/android/chrome_tests.py b/test/functional/android/chrome_tests.py index 8db22e197..52f994392 100644 --- a/test/functional/android/chrome_tests.py +++ b/test/functional/android/chrome_tests.py @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections.abc import Generator -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Generator import pytest diff --git a/test/functional/android/options.py b/test/functional/android/options.py index c9d3afda9..9c8103a75 100644 --- a/test/functional/android/options.py +++ b/test/functional/android/options.py @@ -13,12 +13,13 @@ # limitations under the License. import os +from typing import Optional from appium.options.android import UiAutomator2Options from test.functional.test_helper import get_worker_info -def make_options(app: str | None = None) -> UiAutomator2Options: +def make_options(app: Optional[str] = None) -> UiAutomator2Options: """Get UiAutomator2 options configured for Android testing with parallel execution support.""" options = UiAutomator2Options() diff --git a/test/functional/flutter_integration/conftest.py b/test/functional/flutter_integration/conftest.py index 011338bb5..128312e83 100644 --- a/test/functional/flutter_integration/conftest.py +++ b/test/functional/flutter_integration/conftest.py @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections.abc import Generator -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Generator import pytest diff --git a/test/functional/flutter_integration/helper/options.py b/test/functional/flutter_integration/helper/options.py index 5cad7de31..0a44f372b 100644 --- a/test/functional/flutter_integration/helper/options.py +++ b/test/functional/flutter_integration/helper/options.py @@ -13,7 +13,7 @@ # limitations under the License. import os -from typing import Any +from typing import Any, Dict from appium.options.flutter_integration.base import FlutterOptions from test.functional.test_helper import get_wda_port, get_worker_info @@ -43,7 +43,7 @@ def make_options() -> FlutterOptions: options.flutter_element_wait_timeout = 10000 options.flutter_server_launch_timeout = 120000 - caps: dict[str, Any] = ( + caps: Dict[str, Any] = ( { 'platformName': 'Android', 'deviceName': device_name(), @@ -82,7 +82,10 @@ def device_name() -> str: Get a unique device name for the current worker. Uses the base device name and appends the port number for uniqueness. """ - prefix = 'Android Emulator' if is_platform_android() else os.environ['IPHONE_MODEL'] + if is_platform_android(): + prefix = 'Android Emulator' + else: + prefix = os.environ['IPHONE_MODEL'] worker_info = get_worker_info() diff --git a/test/functional/ios/helper/options.py b/test/functional/ios/helper/options.py index c2d2fc3f9..53e17f251 100644 --- a/test/functional/ios/helper/options.py +++ b/test/functional/ios/helper/options.py @@ -13,12 +13,13 @@ # limitations under the License. import os +from typing import Optional from appium.options.ios import XCUITestOptions from test.functional.test_helper import get_wda_port, get_worker_info -def make_options(app: str | None = None) -> XCUITestOptions: +def make_options(app: Optional[str] = None) -> XCUITestOptions: """Get XCUITest options configured for iOS testing with parallel execution support.""" options = XCUITestOptions() @@ -28,6 +29,7 @@ def make_options(app: str | None = None) -> XCUITestOptions: options.allow_touch_id_enroll = True options.wda_local_port = get_wda_port() options.simple_is_visible_check = True + options.wda_launch_timeout = 240000 if app is not None: options.app = app diff --git a/test/functional/ios/safari_tests.py b/test/functional/ios/safari_tests.py index 211995879..c4ca0ae02 100644 --- a/test/functional/ios/safari_tests.py +++ b/test/functional/ios/safari_tests.py @@ -13,8 +13,7 @@ # limitations under the License. import time -from collections.abc import Generator -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Generator import pytest @@ -36,9 +35,10 @@ def driver() -> Generator['WebDriver', None, None]: options.native_web_tap = True options.safari_ignore_fraud_warning = True options.webview_connect_timeout = 100000 + options.new_command_timeout = 600 client_config = AppiumClientConfig(remote_server_addr=SERVER_URL_BASE) - client_config.timeout = 600 + client_config.timeout = 1200 driver = webdriver.Remote(options=options, client_config=client_config) # Fresh iOS 17.4 simulator may not show up the webview context with "safari" @@ -55,7 +55,7 @@ def driver() -> Generator['WebDriver', None, None]: def test_context(driver: 'WebDriver') -> None: """Test Safari context switching.""" contexts = driver.contexts - assert contexts[0] == 'NATIVE_APP' + assert 'NATIVE_APP' == contexts[0] assert contexts[1].startswith('WEBVIEW_') driver.switch_to.context(contexts[1]) assert 'WEBVIEW_' in driver.current_context @@ -74,7 +74,7 @@ def test_navigation(driver: 'WebDriver') -> None: driver.get('http://google.com') for _ in range(5): time.sleep(0.5) - if driver.title == 'Google': + if 'Google' == driver.title: return pytest.fail('The title was wrong') diff --git a/test/functional/mac/helper/desired_capabilities.py b/test/functional/mac/helper/desired_capabilities.py index 6b3947cf9..648420efb 100644 --- a/test/functional/mac/helper/desired_capabilities.py +++ b/test/functional/mac/helper/desired_capabilities.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any +from typing import Any, Dict -def get_desired_capabilities() -> dict[str, Any]: - desired_caps: dict[str, Any] = {'platformName': 'mac', 'automationName': 'Mac2', 'bundleId': 'com.apple.TextEdit'} +def get_desired_capabilities() -> Dict[str, Any]: + desired_caps: Dict[str, Any] = {'platformName': 'mac', 'automationName': 'Mac2', 'bundleId': 'com.apple.TextEdit'} return desired_caps diff --git a/test/functional/test_helper.py b/test/functional/test_helper.py index 550c26e4a..90500ea0a 100644 --- a/test/functional/test_helper.py +++ b/test/functional/test_helper.py @@ -1,10 +1,9 @@ import os import socket import time -from collections.abc import Callable from dataclasses import dataclass from time import sleep -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Callable, Optional from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait @@ -103,8 +102,8 @@ def wait_for_element(driver: 'WebDriver', locator: str, value: str, timeout_sec: class WorkerInfo: """Information about the current test worker in parallel execution.""" - worker_number: int | None - total_workers: int | None + worker_number: Optional[int] + total_workers: Optional[int] @property def is_parallel(self) -> bool: diff --git a/test/unit/helper/test_helper.py b/test/unit/helper/test_helper.py index b31d5457a..58e358176 100644 --- a/test/unit/helper/test_helper.py +++ b/test/unit/helper/test_helper.py @@ -13,7 +13,7 @@ # limitations under the License. import json -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Dict import httpretty @@ -199,6 +199,6 @@ def flutter_w3c_driver() -> 'WebDriver': return driver -def get_httpretty_request_body(request: 'HTTPrettyRequestEmpty') -> dict[str, Any]: +def get_httpretty_request_body(request: 'HTTPrettyRequestEmpty') -> Dict[str, Any]: """Returns utf-8 decoded request body""" return json.loads(request.body.decode('utf-8')) diff --git a/test/unit/webdriver/app_test.py b/test/unit/webdriver/app_test.py index e0051167d..347675358 100644 --- a/test/unit/webdriver/app_test.py +++ b/test/unit/webdriver/app_test.py @@ -26,10 +26,10 @@ def test_install_app(driver_func): httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') result = driver.install_app('path/to/app') - assert get_httpretty_request_body(httpretty.last_request()) == { + assert { 'args': [{'app': 'path/to/app', 'appPath': 'path/to/app'}], 'script': 'mobile: installApp', - } + } == get_httpretty_request_body(httpretty.last_request()) assert isinstance(result, WebDriver) @@ -40,10 +40,10 @@ def test_remove_app(driver_func): httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') result = driver.remove_app('com.app.id') - assert get_httpretty_request_body(httpretty.last_request()) == { + assert { 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], 'script': 'mobile: removeApp', - } + } == get_httpretty_request_body(httpretty.last_request()) assert isinstance(result, WebDriver) @@ -54,10 +54,10 @@ def test_app_installed(driver_func): httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": true}') result = driver.is_app_installed('com.app.id') - assert get_httpretty_request_body(httpretty.last_request()) == { + assert { 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], 'script': 'mobile: isAppInstalled', - } + } == get_httpretty_request_body(httpretty.last_request()) assert result is True @@ -68,10 +68,10 @@ def test_terminate_app(driver_func): httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": true}') result = driver.terminate_app('com.app.id') - assert get_httpretty_request_body(httpretty.last_request()) == { + assert { 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], 'script': 'mobile: terminateApp', - } + } == get_httpretty_request_body(httpretty.last_request()) assert result is True @@ -82,10 +82,10 @@ def test_activate_app(driver_func): httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') result = driver.activate_app('com.app.id') - assert get_httpretty_request_body(httpretty.last_request()) == { + assert { 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], 'script': 'mobile: activateApp', - } + } == get_httpretty_request_body(httpretty.last_request()) assert isinstance(result, WebDriver) @@ -96,7 +96,7 @@ def test_background_app(driver_func): httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') result = driver.background_app(0) - assert get_httpretty_request_body(httpretty.last_request()) == {'args': [{'seconds': 0}], 'script': 'mobile: backgroundApp'} + assert {'args': [{'seconds': 0}], 'script': 'mobile: backgroundApp'} == get_httpretty_request_body(httpretty.last_request()) assert isinstance(result, WebDriver) @@ -107,10 +107,10 @@ def test_query_app_state(driver_func): httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": 3}') result = driver.query_app_state('com.app.id') - assert get_httpretty_request_body(httpretty.last_request()) == { + assert { 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], 'script': 'mobile: queryAppState', - } + } == get_httpretty_request_body(httpretty.last_request()) assert result is ApplicationState.RUNNING_IN_BACKGROUND @@ -125,8 +125,8 @@ def test_app_strings(driver_func): ) result = driver.app_strings() - assert get_httpretty_request_body(httpretty.last_request()) == {'args': [{}], 'script': 'mobile: getAppStrings'} - assert result['monkey_wipe_data'] == "You can't wipe my data, you are a monkey!", result + assert {'args': [{}], 'script': 'mobile: getAppStrings'} == get_httpretty_request_body(httpretty.last_request()) + assert "You can't wipe my data, you are a monkey!" == result['monkey_wipe_data'], result @pytest.mark.parametrize('driver_func', [android_w3c_driver, ios_w3c_driver]) @@ -140,11 +140,10 @@ def test_app_strings_with_lang(driver_func): ) result = driver.app_strings('en') - assert get_httpretty_request_body(httpretty.last_request()) == { - 'args': [{'language': 'en'}], - 'script': 'mobile: getAppStrings', - } - assert result['monkey_wipe_data'] == "You can't wipe my data, you are a monkey!", result + assert {'args': [{'language': 'en'}], 'script': 'mobile: getAppStrings'} == get_httpretty_request_body( + httpretty.last_request() + ) + assert "You can't wipe my data, you are a monkey!" == result['monkey_wipe_data'], result @pytest.mark.parametrize('driver_func', [android_w3c_driver, ios_w3c_driver]) @@ -158,8 +157,8 @@ def test_app_strings_with_lang_and_file(driver_func): ) result = driver.app_strings('en', 'some_file') - assert get_httpretty_request_body(httpretty.last_request()) == { + assert { 'args': [{'language': 'en', 'stringFile': 'some_file'}], 'script': 'mobile: getAppStrings', - } - assert result['monkey_wipe_data'] == "You can't wipe my data, you are a monkey!", result + } == get_httpretty_request_body(httpretty.last_request()) + assert "You can't wipe my data, you are a monkey!" == result['monkey_wipe_data'], result diff --git a/test/unit/webdriver/appium_service_args_test.py b/test/unit/webdriver/appium_service_args_test.py deleted file mode 100644 index 97ea54aaa..000000000 --- a/test/unit/webdriver/appium_service_args_test.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest - -from appium.webdriver.appium_service import _make_server_url - - -@pytest.mark.parametrize( - ('args', 'expected_url'), - [ - ([], 'http://127.0.0.1:4723/status'), - (['--port=1234'], 'http://127.0.0.1:1234/status'), - (['--address=appium.test'], 'http://appium.test:4723/status'), - (['--base-path=/wd/hub'], 'http://127.0.0.1:4723/wd/hub/status'), - ( - ['--address=appium.test', '--port=1234', '--base-path=/wd/hub'], - 'http://appium.test:1234/wd/hub/status', - ), - ( - ['--address', 'appium.test', '--port', '1234', '--base-path', '/wd/hub'], - 'http://appium.test:1234/wd/hub/status', - ), - (['-a', 'appium.test', '-p', '1234', '-pa', '/wd/hub'], 'http://appium.test:1234/wd/hub/status'), - (['-a=appium.test', '-p=1234', '-pa=/wd/hub'], 'http://appium.test:1234/wd/hub/status'), - (['-p', '1234', '--base-path=/wd/hub'], 'http://127.0.0.1:1234/wd/hub/status'), - (['--base-path=/value=with=equals/'], 'http://127.0.0.1:4723/value=with=equals/status'), - (['--port-extra=1234', '--address-extra=appium.test'], 'http://127.0.0.1:4723/status'), - ], -) -def test_status_url_uses_cli_argument_values(args, expected_url): - assert _make_server_url(args) == expected_url diff --git a/test/unit/webdriver/appium_service_ipv6_test.py b/test/unit/webdriver/appium_service_ipv6_test.py deleted file mode 100644 index 4428cf6ec..000000000 --- a/test/unit/webdriver/appium_service_ipv6_test.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest - -from appium.webdriver.appium_service import _make_server_url - - -@pytest.mark.parametrize( - ('host', 'url_host'), - [ - ('::1', '[::1]'), - ('2001:db8::1', '[2001:db8::1]'), - ('fe80::1%en0', '[fe80::1%en0]'), - ('[::1]', '[::1]'), - ('2001:db8::g', '2001:db8::g'), - ('127.0.0.1', '127.0.0.1'), - ('appium.test', 'appium.test'), - ], -) -def test_status_url_formats_ip_literal_hosts(host, url_host): - args = ['--address', host, '--port', '8123', '--base-path', '/wd/hub'] - assert _make_server_url(args) == f'http://{url_host}:8123/wd/hub/status' diff --git a/test/unit/webdriver/appium_service_test.py b/test/unit/webdriver/appium_service_test.py index d0826c8f0..784c6e549 100644 --- a/test/unit/webdriver/appium_service_test.py +++ b/test/unit/webdriver/appium_service_test.py @@ -12,44 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from unittest.mock import Mock, call - -import pytest - -from appium.webdriver import appium_service from appium.webdriver.appium_service import AppiumService class TestAppiumService: def test_get_instance(self): assert AppiumService() - - @pytest.mark.parametrize( - ('status', 'expected_result', 'expected_elapsed', 'expected_requests'), - [(200, True, 0.0, 1), (503, False, 5.0, 10)], - ) - def test_is_listening_uses_five_second_polling_timeout( - self, monkeypatch, status, expected_result, expected_elapsed, expected_requests - ): - process = Mock() - process.poll.return_value = None - monkeypatch.setattr(appium_service.sp, 'Popen', Mock(return_value=process)) - service = AppiumService() - service.start(node='node', npm='npm', main_script='appium.js', timeout_ms=0) - - clock = Mock() - clock.perf_counter.return_value = 0.0 - - def advance_time(seconds): - clock.perf_counter.return_value += seconds - - clock.sleep.side_effect = advance_time - monkeypatch.setattr(appium_service, 'time', clock) - - connection = Mock() - connection.request.return_value.status = status - monkeypatch.setattr(appium_service.urllib3, 'PoolManager', Mock(return_value=connection)) - - assert service.is_listening is expected_result - assert clock.perf_counter.return_value == expected_elapsed - assert connection.request.call_args_list == [call('HEAD', 'http://127.0.0.1:4723/status')] * expected_requests diff --git a/test/unit/webdriver/context_test.py b/test/unit/webdriver/context_test.py index 7198984c7..b83dd82d4 100644 --- a/test/unit/webdriver/context_test.py +++ b/test/unit/webdriver/context_test.py @@ -31,7 +31,7 @@ def test_get_contexts(self): httpretty.GET, appium_command('/session/1234567890/contexts'), body='{"value": ["NATIVE_APP", "CHROMIUM"]}' ) - assert driver.contexts == ['NATIVE_APP', 'CHROMIUM'] + assert ['NATIVE_APP', 'CHROMIUM'] == driver.contexts @httpretty.activate def test_switch_to_context(self): @@ -40,7 +40,7 @@ def test_switch_to_context(self): driver.switch_to.context(None) - assert get_httpretty_request_body(httpretty.last_request()) == {'name': None} + assert {'name': None} == get_httpretty_request_body(httpretty.last_request()) @httpretty.activate def test_switch_to_context_native_app(self): @@ -49,4 +49,4 @@ def test_switch_to_context_native_app(self): driver.switch_to.context('NATIVE_APP') - assert get_httpretty_request_body(httpretty.last_request()) == {'name': 'NATIVE_APP'} + assert {'name': 'NATIVE_APP'} == get_httpretty_request_body(httpretty.last_request()) diff --git a/test/unit/webdriver/device/activities_test.py b/test/unit/webdriver/device/activities_test.py index 15df9b783..acfe3d2e5 100644 --- a/test/unit/webdriver/device/activities_test.py +++ b/test/unit/webdriver/device/activities_test.py @@ -21,6 +21,11 @@ class TestWebDriverActivities: @httpretty.activate def test_current_activity(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.GET, + appium_command('/session/1234567890/appium/device/current_activity'), + body='{"value": ".ExampleActivity"}', + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -31,6 +36,11 @@ def test_current_activity(self): @httpretty.activate def test_wait_activity(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.GET, + appium_command('/session/1234567890/appium/device/current_activity'), + body='{"value": ".ExampleActivity"}', + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), diff --git a/test/unit/webdriver/device/clipboard_test.py b/test/unit/webdriver/device/clipboard_test.py index 4f313b2fe..3708b2a3a 100644 --- a/test/unit/webdriver/device/clipboard_test.py +++ b/test/unit/webdriver/device/clipboard_test.py @@ -22,12 +22,15 @@ class TestWebDriverClipboard: @httpretty.activate def test_set_clipboard_with_url(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/set_clipboard'), body='{"value": ""}' + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}', ) - driver.set_clipboard(bytes('http://appium.io/', 'UTF-8'), ClipboardContentType.URL, 'label for android') + driver.set_clipboard(bytes(str('http://appium.io/'), 'UTF-8'), ClipboardContentType.URL, 'label for android') d = get_httpretty_request_body(httpretty.last_request()) assert d['args'][0]['content'] == 'aHR0cDovL2FwcGl1bS5pby8=' @@ -37,6 +40,9 @@ def test_set_clipboard_with_url(self): @httpretty.activate def test_set_clipboard_text(self): driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/set_clipboard'), body='{"value": ""}' + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), diff --git a/test/unit/webdriver/device/common_test.py b/test/unit/webdriver/device/common_test.py index 596e988e6..465349029 100644 --- a/test/unit/webdriver/device/common_test.py +++ b/test/unit/webdriver/device/common_test.py @@ -24,7 +24,7 @@ def test_open_notifications(self): driver = android_w3c_driver() httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.open_notifications(), WebDriver) - assert get_httpretty_request_body(httpretty.last_request()) == {'args': [], 'script': 'mobile: openNotifications'} + assert {'args': [], 'script': 'mobile: openNotifications'} == get_httpretty_request_body(httpretty.last_request()) @httpretty.activate def test_current_package(self): @@ -35,4 +35,4 @@ def test_current_package(self): body='{"value": ".ExamplePackage"}', ) assert driver.current_package == '.ExamplePackage' - assert get_httpretty_request_body(httpretty.last_request()) == {'args': [], 'script': 'mobile: getCurrentPackage'} + assert {'args': [], 'script': 'mobile: getCurrentPackage'} == get_httpretty_request_body(httpretty.last_request()) diff --git a/test/unit/webdriver/device/device_time_test.py b/test/unit/webdriver/device/device_time_test.py index 98d01336f..4d7e72110 100644 --- a/test/unit/webdriver/device/device_time_test.py +++ b/test/unit/webdriver/device/device_time_test.py @@ -21,6 +21,11 @@ class TestWebDriverDeviceTime: @httpretty.activate def test_device_time(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.GET, + appium_command('/session/1234567890/appium/device/system_time'), + body='{"value": "2019-01-05T14:46:44+09:00"}', + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -31,6 +36,11 @@ def test_device_time(self): @httpretty.activate def test_get_device_time(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.GET, + appium_command('/session/1234567890/appium/device/system_time'), + body='{"value": "2019-01-05T14:46:44+09:00"}', + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -41,6 +51,11 @@ def test_get_device_time(self): @httpretty.activate def test_get_formatted_device_time(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/system_time'), + body='{"value": "2019-01-08"}', + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -49,5 +64,4 @@ def test_get_formatted_device_time(self): assert driver.get_device_time('YYYY-MM-DD') == '2019-01-08' d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: getDeviceTime' - assert d['args'][0]['format'] == 'YYYY-MM-DD' + assert d.get('format', d['args'][0]['format']) == 'YYYY-MM-DD' diff --git a/test/unit/webdriver/device/display_test.py b/test/unit/webdriver/device/display_test.py index cc93c86a6..96cc0d019 100644 --- a/test/unit/webdriver/device/display_test.py +++ b/test/unit/webdriver/device/display_test.py @@ -21,5 +21,8 @@ class TestWebDriverDisplay: @httpretty.activate def test_get_display_density(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.GET, appium_command('/session/1234567890/appium/device/display_density'), body='{"value": 560}' + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": 560}') assert driver.get_display_density() == 560 diff --git a/test/unit/webdriver/device/fingerprint_test.py b/test/unit/webdriver/device/fingerprint_test.py index 93b027842..83a992ef8 100644 --- a/test/unit/webdriver/device/fingerprint_test.py +++ b/test/unit/webdriver/device/fingerprint_test.py @@ -22,6 +22,11 @@ class TestWebDriverFingerprint: @httpretty.activate def test_finger_print(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/finger_print'), + # body is None + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -31,5 +36,4 @@ def test_finger_print(self): assert isinstance(driver.finger_print(1), WebDriver) d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: fingerprint' - assert d['args'][0]['fingerprintId'] == 1 + assert d.get('fingerprintId', d['args'][0]['fingerprintId']) == 1 diff --git a/test/unit/webdriver/device/keyboard_test.py b/test/unit/webdriver/device/keyboard_test.py index 68f77bdbf..5ec55a2a8 100644 --- a/test/unit/webdriver/device/keyboard_test.py +++ b/test/unit/webdriver/device/keyboard_test.py @@ -23,37 +23,51 @@ class TestWebDriverKeyboardAndroid: @httpretty.activate def test_hide_keyboard(self): driver = android_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/appium/device/hide_keyboard')) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.hide_keyboard(), WebDriver) @httpretty.activate def test_press_keycode(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/press_keycode'), body='{"value": "86"}' + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": "86"}') driver.press_keycode(86) - d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: pressKey' - assert d['args'][0]['keycode'] == 86 + d = get_httpretty_request_body((httpretty.last_request())) + assert d.get('keycode', d['args'][0]['keycode']) == 86 @httpretty.activate def test_long_press_keycode(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/long_press_keycode'), + body='{"value": "86"}', + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": "86"}') driver.long_press_keycode(86) - d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: pressKey' - assert d['args'][0]['keycode'] == 86 - assert d['args'][0]['isLongPress'] is True + d = get_httpretty_request_body((httpretty.last_request())) + assert d.get('keycode', d['args'][0]['keycode']) == 86 @httpretty.activate def test_keyevent(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/keyevent'), body='{keycode: 86}' + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": "86"}') assert isinstance(driver.keyevent(86), WebDriver) @httpretty.activate def test_press_keycode_with_flags(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/press_keycode'), + body='{keycode: 86, metastate: 2097153, flags: 44}', + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) # metastate is META_SHIFT_ON and META_NUM_LOCK_ON # flags is CANCELFLAG_CANCELEDED, FLAG_KEEP_TOUCH_MODE, FLAG_FROM_SYSTEM @@ -69,6 +83,11 @@ def test_press_keycode_with_flags(self): @httpretty.activate def test_long_press_keycode_with_flags(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/long_press_keycode'), + body='{keycode: 86, metastate: 2097153, flags: 44}', + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) # metastate is META_SHIFT_ON and META_NUM_LOCK_ON # flags is CANCELFLAG_CANCELEDED, FLAG_KEEP_TOUCH_MODE, FLAG_FROM_SYSTEM @@ -88,17 +107,16 @@ def test_hide_keyboard(self): driver = ios_w3c_driver() httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.hide_keyboard(), WebDriver) - assert get_httpretty_request_body(httpretty.last_request()) == {'args': [{}], 'script': 'mobile: hideKeyboard'} + assert {'args': [{}], 'script': 'mobile: hideKeyboard'} == get_httpretty_request_body(httpretty.last_request()) @httpretty.activate def test_hide_keyboard_with_key(self): driver = ios_w3c_driver() httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.hide_keyboard(key_name='Done'), WebDriver) - assert get_httpretty_request_body(httpretty.last_request()) == { - 'args': [{'keys': ['Done']}], - 'script': 'mobile: hideKeyboard', - } + assert {'args': [{'keys': ['Done']}], 'script': 'mobile: hideKeyboard'} == get_httpretty_request_body( + httpretty.last_request() + ) @httpretty.activate def test_hide_keyboard_with_key_and_strategy(self): @@ -106,24 +124,22 @@ def test_hide_keyboard_with_key_and_strategy(self): httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.hide_keyboard(strategy='pressKey', key='Done'), WebDriver) # only 'keys' works - assert get_httpretty_request_body(httpretty.last_request()) == { - 'args': [{'keys': ['Done']}], - 'script': 'mobile: hideKeyboard', - } + assert {'args': [{'keys': ['Done']}], 'script': 'mobile: hideKeyboard'} == get_httpretty_request_body( + httpretty.last_request() + ) @httpretty.activate def test_is_keyboard_shown(self): driver = ios_w3c_driver() httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) driver.is_keyboard_shown(), WebDriver - assert get_httpretty_request_body(httpretty.last_request()) == {'script': 'mobile: isKeyboardShown', 'args': []} + assert {'script': 'mobile: isKeyboardShown', 'args': []} == get_httpretty_request_body(httpretty.last_request()) @httpretty.activate def test_press_button(self): driver = ios_w3c_driver() httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) driver.press_button('Home') - assert get_httpretty_request_body(httpretty.last_request()) == { - 'script': 'mobile: pressButton', - 'args': [{'name': 'Home'}], - } + assert {'script': 'mobile: pressButton', 'args': [{'name': 'Home'}]} == get_httpretty_request_body( + httpretty.last_request() + ) diff --git a/test/unit/webdriver/device/lock_test.py b/test/unit/webdriver/device/lock_test.py index 603efffbd..4c959dae9 100644 --- a/test/unit/webdriver/device/lock_test.py +++ b/test/unit/webdriver/device/lock_test.py @@ -22,91 +22,110 @@ class TestWebDriverLockAndroid: @httpretty.activate def test_lock(self): driver = android_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/appium/device/lock'), body='{"value": ""}') httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') driver.lock(1) d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: lock' - assert d['args'][0]['seconds'] == 1 + assert d.get('seconds', d['args'][0]['seconds']) == 1 @httpretty.activate def test_lock_no_args(self): driver = android_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/appium/device/lock'), body='{"value": ""}') httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') driver.lock() @httpretty.activate def test_islocked_false(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/is_locked'), body='{"value": false}' + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": false}') assert driver.is_locked() is False @httpretty.activate def test_islocked_true(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/is_locked'), body='{"value": true}' + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": true}') assert driver.is_locked() is True @httpretty.activate def test_unlock(self): driver = android_w3c_driver() - httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": true}') + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/unlock'), + ) + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.unlock(), WebDriver) - assert get_httpretty_request_body(httpretty.last_request())['script'] == 'mobile: unlock' class TestWebDriverLockIOS: @httpretty.activate def test_lock(self): driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/appium/device/lock'), body='{"value": ""}') httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') driver.lock(1) d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: lock' - assert d['args'][0]['seconds'] == 1 + assert d.get('seconds', d['args'][0]['seconds']) == 1 @httpretty.activate def test_lock_no_args(self): driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/appium/device/lock'), body='{"value": ""}') httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') driver.lock() @httpretty.activate def test_islocked_false(self): driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/is_locked'), body='{"value": false}' + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": false}') assert driver.is_locked() is False @httpretty.activate def test_islocked_true(self): driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/is_locked'), body='{"value": true}' + ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": true}') assert driver.is_locked() is True @httpretty.activate def test_unlock(self): driver = ios_w3c_driver() - httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": true}') + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/unlock'), + ) + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.unlock(), WebDriver) - assert get_httpretty_request_body(httpretty.last_request())['script'] == 'mobile: unlock' @httpretty.activate def test_touch_id(self): driver = ios_w3c_driver() httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.touch_id(True), WebDriver) - assert get_httpretty_request_body(httpretty.last_request()) == { + assert { 'script': 'mobile: sendBiometricMatch', 'args': [{'match': True, 'type': 'touchId'}], - } + } == get_httpretty_request_body(httpretty.last_request()) @httpretty.activate def test_enroll_biometric(self): driver = ios_w3c_driver() httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.toggle_touch_id_enrollment(), WebDriver) - assert get_httpretty_request_body(httpretty.last_request()) == { - 'script': 'mobile: enrollBiometric', - 'args': [{'isEnabled': True}], - } + assert {'script': 'mobile: enrollBiometric', 'args': [{'isEnabled': True}]} == get_httpretty_request_body( + httpretty.last_request() + ) diff --git a/test/unit/webdriver/device/power_test.py b/test/unit/webdriver/device/power_test.py index ea5fe1dc7..2d99a4a54 100644 --- a/test/unit/webdriver/device/power_test.py +++ b/test/unit/webdriver/device/power_test.py @@ -23,6 +23,10 @@ class TestWebDriverPower: @httpretty.activate def test_set_power_capacity(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/power_capacity'), + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -30,12 +34,15 @@ def test_set_power_capacity(self): assert isinstance(driver.set_power_capacity(50), WebDriver) d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: powerCapacity' assert d['args'][0]['percent'] == 50 @httpretty.activate def test_set_power_ac(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/power_ac'), + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -43,5 +50,4 @@ def test_set_power_ac(self): assert isinstance(driver.set_power_ac(Power.AC_ON), WebDriver) d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: powerAC' assert d['args'][0]['state'] == Power.AC_ON diff --git a/test/unit/webdriver/device/remote_fs_test.py b/test/unit/webdriver/device/remote_fs_test.py index f1fca0df5..93148e665 100644 --- a/test/unit/webdriver/device/remote_fs_test.py +++ b/test/unit/webdriver/device/remote_fs_test.py @@ -26,6 +26,10 @@ class TestWebDriverRemoteFs: @httpretty.activate def test_push_file(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/push_file'), + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -36,13 +40,16 @@ def test_push_file(self): assert isinstance(driver.push_file(dest_path, data), WebDriver) d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: pushFile' - assert d['args'][0]['remotePath'] == dest_path - assert d['args'][0]['payload'] == str(data) + assert d.get('path', d['args'][0]['remotePath']) == dest_path + assert d.get('data', d['args'][0]['payload']) == str(data) @httpretty.activate def test_push_file_invalid_arg_exception_without_src_path_and_base64data(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/push_file'), + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -55,6 +62,14 @@ def test_push_file_invalid_arg_exception_without_src_path_and_base64data(self): @httpretty.activate def test_push_file_invalid_arg_exception_with_src_file_not_found(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/push_file'), + ) + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/push_file'), + ) dest_path = '/dest_path/to/file.txt' src_path = '/src_path/to/file.txt' @@ -64,6 +79,11 @@ def test_push_file_invalid_arg_exception_with_src_file_not_found(self): @httpretty.activate def test_pull_file(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/pull_file'), + body='{"value": "SGVsbG9Xb3JsZA=="}', + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -74,12 +94,16 @@ def test_pull_file(self): assert driver.pull_file(dest_path) == str(base64.b64encode(bytes('HelloWorld', 'utf-8')).decode('utf-8')) d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: pullFile' - assert d['args'][0]['remotePath'] == dest_path + assert d.get('path', d['args'][0]['remotePath']) == dest_path @httpretty.activate def test_pull_folder(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/pull_folder'), + body='{"value": "base64EncodedZippedFolderData"}', + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -90,5 +114,4 @@ def test_pull_folder(self): assert driver.pull_folder(dest_path) == 'base64EncodedZippedFolderData' d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: pullFolder' - assert d['args'][0]['remotePath'] == dest_path + assert d.get('path', d['args'][0]['remotePath']) == dest_path diff --git a/test/unit/webdriver/device/shake_test.py b/test/unit/webdriver/device/shake_test.py index 8c796255a..b12d791a7 100644 --- a/test/unit/webdriver/device/shake_test.py +++ b/test/unit/webdriver/device/shake_test.py @@ -23,6 +23,10 @@ class TestWebDriverShake: @httpretty.activate def test_shake(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/shake'), + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), diff --git a/test/unit/webdriver/device/sms_test.py b/test/unit/webdriver/device/sms_test.py index a7200dca0..65e66e6bf 100644 --- a/test/unit/webdriver/device/sms_test.py +++ b/test/unit/webdriver/device/sms_test.py @@ -22,6 +22,10 @@ class TestWebDriverSms: @httpretty.activate def test_send_sms(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/send_sms'), + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -29,6 +33,5 @@ def test_send_sms(self): assert isinstance(driver.send_sms('555-123-4567', 'Hey lol'), WebDriver) d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: sendSms' assert d['args'][0]['phoneNumber'] == '555-123-4567' assert d['args'][0]['message'] == 'Hey lol' diff --git a/test/unit/webdriver/device/system_bars_test.py b/test/unit/webdriver/device/system_bars_test.py index 3592c0059..6d714c2d7 100644 --- a/test/unit/webdriver/device/system_bars_test.py +++ b/test/unit/webdriver/device/system_bars_test.py @@ -26,6 +26,11 @@ def test_get_system_bars(self): {"visible": true, "x": 0, "y": 0, "width": 1080, "height": 1920}, "navigationBar": {"visible": true, "x": 0, "y": 0, "width": 1080, "height": 126}}}""" + httpretty.register_uri( + httpretty.GET, + appium_command('/session/1234567890/appium/device/system_bars'), + body=body, + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), diff --git a/test/unit/webdriver/log_events_test.py b/test/unit/webdriver/log_events_test.py index 2469efc39..244aa8223 100644 --- a/test/unit/webdriver/log_events_test.py +++ b/test/unit/webdriver/log_events_test.py @@ -33,7 +33,7 @@ def test_get_events(self): assert events['appium:funEvent'] == [12347] d = get_httpretty_request_body(httpretty.last_request()) - assert 'type' not in d + assert 'type' not in d.keys() @httpretty.activate def test_get_events_args(self): diff --git a/test/unit/webdriver/log_test.py b/test/unit/webdriver/log_test.py index f7f554bf5..f3b76d351 100644 --- a/test/unit/webdriver/log_test.py +++ b/test/unit/webdriver/log_test.py @@ -43,4 +43,4 @@ def test_get_log(): assert log_types == ['logs as array'] d = get_httpretty_request_body(httpretty.last_request()) - assert d == {'type': 'syslog'} + assert {'type': 'syslog'} == d diff --git a/test/unit/webdriver/network_test.py b/test/unit/webdriver/network_test.py index cc1938dd3..0408d93ac 100644 --- a/test/unit/webdriver/network_test.py +++ b/test/unit/webdriver/network_test.py @@ -24,6 +24,7 @@ class TestWebDriverNetwork: @httpretty.activate def test_network_connection(self): driver = android_w3c_driver() + httpretty.register_uri(httpretty.GET, appium_command('/session/1234567890/network_connection'), body='{"value": 2}') httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -34,20 +35,24 @@ def test_network_connection(self): @httpretty.activate def test_set_network_connection(self): driver = android_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/network_connection'), body='{"value": ""}') httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": {"wifi": true, "data": false, "airplaneMode": false}}', ) - assert driver.set_network_connection(2) == 2 + driver.set_network_connection(2) - d = get_httpretty_request_body(httpretty.latest_requests()[-4]) - assert d['script'] == 'mobile: setConnectivity' + d = get_httpretty_request_body(httpretty.last_request()) assert d['args'][0]['wifi'] is True @httpretty.activate def test_set_network_speed(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/network_speed'), + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -60,6 +65,10 @@ def test_set_network_speed(self): @httpretty.activate def test_toggle_wifi(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/toggle_wifi'), + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), diff --git a/test/unit/webdriver/performance_test.py b/test/unit/webdriver/performance_test.py index c21f720ec..6bae8dd9d 100644 --- a/test/unit/webdriver/performance_test.py +++ b/test/unit/webdriver/performance_test.py @@ -21,6 +21,11 @@ class TestWebDriverPerformance: @httpretty.activate def test_get_performance_data(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/getPerformanceData'), + body='{"value": [["user", "kernel"], ["2.5", "1.3"]]}', + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), @@ -29,13 +34,17 @@ def test_get_performance_data(self): assert driver.get_performance_data('my.app.package', 'cpuinfo', 5) == [['user', 'kernel'], ['2.5', '1.3']] d = get_httpretty_request_body(httpretty.last_request()) - assert d['script'] == 'mobile: getPerformanceData' assert d['args'][0]['packageName'] == 'my.app.package' assert d['args'][0]['dataType'] == 'cpuinfo' @httpretty.activate def test_get_performance_data_types(self): driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/performanceData/types'), + body='{"value": ["cpuinfo", "memoryinfo", "batteryinfo", "networkinfo"]}', + ) httpretty.register_uri( httpretty.POST, appium_command('/session/1234567890/execute/sync'), diff --git a/test/unit/webdriver/screen_record_test.py b/test/unit/webdriver/screen_record_test.py index c56da8c73..a8ddee1b2 100644 --- a/test/unit/webdriver/screen_record_test.py +++ b/test/unit/webdriver/screen_record_test.py @@ -25,7 +25,7 @@ def test_start_recording_screen(self): d = get_httpretty_request_body(httpretty.last_request()) assert d['options']['user'] == 'userA' assert d['options']['pass'] == '12345' - assert 'password' not in d['options'] + assert 'password' not in d['options'].keys() @httpretty.activate def test_stop_recording_screen(self): @@ -40,7 +40,7 @@ def test_stop_recording_screen(self): d = get_httpretty_request_body(httpretty.last_request()) assert d['options']['user'] == 'userA' assert d['options']['pass'] == '12345' - assert 'password' not in d['options'] + assert 'password' not in d['options'].keys() class TestWebDriverScreenRecordIOS: @@ -56,7 +56,7 @@ def test_start_recording_screen(self): d = get_httpretty_request_body(httpretty.last_request()) assert d['options']['user'] == 'userA' assert d['options']['pass'] == '12345' - assert 'password' not in d['options'] + assert 'password' not in d['options'].keys() @httpretty.activate def test_stop_recording_screen(self): @@ -71,4 +71,4 @@ def test_stop_recording_screen(self): d = get_httpretty_request_body(httpretty.last_request()) assert d['options']['user'] == 'userA' assert d['options']['pass'] == '12345' - assert 'password' not in d['options'] + assert 'password' not in d['options'].keys() diff --git a/test/unit/webdriver/webdriver_options_list_test.py b/test/unit/webdriver/webdriver_options_list_test.py deleted file mode 100644 index bc54de990..000000000 --- a/test/unit/webdriver/webdriver_options_list_test.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy -import json - -import httpretty - -from appium import webdriver -from appium.options.android import UiAutomator2Options -from appium.options.ios import XCUITestOptions -from test.helpers.constants import SERVER_URL_BASE - - -def create_session(options): - httpretty.register_uri( - httpretty.POST, - f'{SERVER_URL_BASE}/session', - body=json.dumps({'value': {'sessionId': 'options-session', 'capabilities': {}}}), - ) - driver = webdriver.Remote(SERVER_URL_BASE, options=options) - assert driver.session_id == 'options-session' - return json.loads(httpretty.last_request().body.decode('utf-8'))['capabilities'] - - -@httpretty.activate -def test_options_list_preserves_common_and_alternative_capabilities(): - proxy = {'proxyType': 'manual', 'httpProxy': 'proxy.example:8080'} - options = [ - UiAutomator2Options().set_capability('deviceName', name).set_capability('proxy', proxy) - for name in ['first-device', 'second-device'] - ] - original_capabilities = copy.deepcopy([option.to_capabilities() for option in options]) - - assert create_session(options) == { - 'alwaysMatch': {'platformName': 'Android', 'appium:automationName': 'UIAutomator2', 'proxy': proxy}, - 'firstMatch': [{'appium:deviceName': 'first-device'}, {'appium:deviceName': 'second-device'}], - } - assert [option.to_capabilities() for option in options] == original_capabilities - - -@httpretty.activate -def test_options_list_normalizes_automation_overrides_before_matching(): - default_options = UiAutomator2Options() - overridden_options = UiAutomator2Options().set_capability('automationName', 'Espresso') - - assert create_session([default_options, overridden_options]) == { - 'alwaysMatch': {'platformName': 'Android'}, - 'firstMatch': [{'appium:automationName': 'UIAutomator2'}, {'appium:automationName': 'Espresso'}], - } - - -@httpretty.activate -def test_options_list_supports_different_platform_alternatives(): - assert create_session([UiAutomator2Options(), XCUITestOptions()]) == { - 'alwaysMatch': {}, - 'firstMatch': [ - {'platformName': 'Android', 'appium:automationName': 'UIAutomator2'}, - {'platformName': 'iOS', 'appium:automationName': 'XCUITest'}, - ], - } - - -@httpretty.activate -def test_single_item_options_list_matches_single_options_request(): - options = UiAutomator2Options().set_capability('deviceName', 'single-device') - assert create_session([options]) == create_session(options) diff --git a/test/unit/webdriver/webdriver_test.py b/test/unit/webdriver/webdriver_test.py index 73966a244..84851b2cd 100644 --- a/test/unit/webdriver/webdriver_test.py +++ b/test/unit/webdriver/webdriver_test.py @@ -13,10 +13,12 @@ # limitations under the License. import json -from unittest.mock import patch import httpretty +import pytest import urllib3 +from mock import patch +from selenium.common.exceptions import WebDriverException from appium import webdriver from appium.options.android import UiAutomator2Options @@ -132,67 +134,10 @@ def test_create_session_register_uridirect(self): client_config=client_config, ) - assert driver.command_executor._client_config.remote_server_addr == 'http://localhost2:4800/special/path/wd/hub' - assert driver.contexts == ['NATIVE_APP', 'CHROMIUM'] + assert 'http://localhost2:4800/special/path/wd/hub' == driver.command_executor._client_config.remote_server_addr + assert ['NATIVE_APP', 'CHROMIUM'] == driver.contexts assert isinstance(driver.command_executor, AppiumConnection) - @httpretty.activate - def test_create_session_register_uridirect_keeps_client_config(self): - """The client configuration given by a user must survive the direct connect switch. - https://github.com/appium/python-client/issues/855 - """ - httpretty.register_uri( - httpretty.POST, - f'{SERVER_URL_BASE}/session', - body=json.dumps( - { - 'sessionId': 'session-id', - 'capabilities': { - 'deviceName': 'Android Emulator', - 'directConnectProtocol': 'http', - 'directConnectHost': 'localhost2', - 'directConnectPort': 4800, - 'directConnectPath': '/special/path/wd/hub', - }, - } - ), - ) - - desired_caps = { - 'platformName': 'Android', - 'deviceName': 'Android Emulator', - 'app': 'path/to/app', - 'automationName': 'UIAutomator2', - } - client_config = AppiumClientConfig( - remote_server_addr=SERVER_URL_BASE, - direct_connection=True, - timeout=5, - username='user', - password='pass', - user_agent='custom-agent', - init_args_for_pool_manager={'init_args_for_pool_manager': {'retries': 3}}, - ) - driver = webdriver.Remote( - SERVER_URL_BASE, - options=UiAutomator2Options().load_capabilities(desired_caps), - client_config=client_config, - ) - - new_client_config = driver.command_executor.client_config - assert isinstance(new_client_config, AppiumClientConfig) - assert new_client_config.remote_server_addr == 'http://localhost2:4800/special/path/wd/hub' - assert new_client_config.timeout == 5 - assert new_client_config.username == 'user' - assert new_client_config.password == 'pass' - assert new_client_config.user_agent == 'custom-agent' - assert new_client_config.init_args_for_pool_manager == {'init_args_for_pool_manager': {'retries': 3}} - assert new_client_config.direct_connection - assert new_client_config.keep_alive == client_config.keep_alive - - # the configuration instance given by a user must not be modified in-place - assert client_config.remote_server_addr == SERVER_URL_BASE - @httpretty.activate def test_create_session_register_uridirect_no_direct_connect_path(self): httpretty.register_uri( @@ -228,8 +173,8 @@ def test_create_session_register_uridirect_no_direct_connect_path(self): SERVER_URL_BASE, options=UiAutomator2Options().load_capabilities(desired_caps), client_config=client_config ) - assert driver.command_executor._client_config.remote_server_addr == SERVER_URL_BASE - assert driver.contexts == ['NATIVE_APP', 'CHROMIUM'] + assert SERVER_URL_BASE == driver.command_executor._client_config.remote_server_addr + assert ['NATIVE_APP', 'CHROMIUM'] == driver.contexts assert isinstance(driver.command_executor, AppiumConnection) @httpretty.activate @@ -268,7 +213,7 @@ def test_create_session_remote_server_addr_treatment_with_appiumclientconfig(sel client_config=client_config, ) - assert driver.command_executor._client_config.remote_server_addr == SERVER_URL_BASE + assert SERVER_URL_BASE == driver.command_executor._client_config.remote_server_addr assert isinstance(driver.command_executor, AppiumConnection) @httpretty.activate @@ -461,6 +406,38 @@ class CustomAppiumConnection(AppiumConnection): assert isinstance(driver.command_executor, CustomAppiumConnection) + @httpretty.activate + def test_orientation_getter(self): + driver = android_w3c_driver() + httpretty.register_uri(httpretty.GET, appium_command('/session/1234567890/orientation'), body='{"value": "LANDSCAPE"}') + assert driver.orientation == 'LANDSCAPE' + + @httpretty.activate + def test_orientation_setter(self): + driver = android_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/orientation'), body='{"value": ""}') + + driver.orientation = 'LANDSCAPE' + + assert { + 'orientation': 'LANDSCAPE', + } == get_httpretty_request_body(httpretty.last_request()) + + driver.orientation = 'PORTRAIT' + + assert { + 'orientation': 'PORTRAIT', + } == get_httpretty_request_body(httpretty.last_request()) + + @httpretty.activate + def test_orientation_setter_invalid(self): + driver = android_w3c_driver() + + with pytest.raises(WebDriverException) as excinfo: + driver.orientation = 'INVALID' + + assert "You can only set the orientation to 'LANDSCAPE' and 'PORTRAIT'" in str(excinfo.value) + @httpretty.activate def test_extention_command_check(self): driver = android_w3c_driver() @@ -472,10 +449,10 @@ def test_extention_command_check(self): ) is True ) - assert get_httpretty_request_body(httpretty.last_request()) == { + assert { 'args': [{'component': 'io.appium.android.apis/.accessibility.AccessibilityNodeProviderActivity'}], 'script': 'mobile: startActivity', - } + } == get_httpretty_request_body(httpretty.last_request()) def test_get_client_config_and_connection_with_empty_config(self): command_executor, client_config = _get_remote_connection_and_client_config( diff --git a/test/unit/webdriver/webelement_test.py b/test/unit/webdriver/webelement_test.py index a37c2ec68..a37f229a4 100644 --- a/test/unit/webdriver/webelement_test.py +++ b/test/unit/webdriver/webelement_test.py @@ -49,12 +49,15 @@ def test_send_key(self): @httpretty.activate def test_send_key_with_file(self): driver = android_w3c_driver() + # Should not send this file + tmp_f = tempfile.NamedTemporaryFile() httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/element/element_id/value')) - # Should not send this file - with tempfile.NamedTemporaryFile() as tmp_f: + try: element = MobileWebElement(driver, 'element_id') element.send_keys(tmp_f.name) + finally: + tmp_f.close() d = get_httpretty_request_body(httpretty.last_request()) assert d['text'] == ''.join(d['value']) diff --git a/uv.lock b/uv.lock index fa72063de..71ee36e9b 100644 --- a/uv.lock +++ b/uv.lock @@ -2,8 +2,7 @@ version = 1 revision = 3 requires-python = ">=3.10" resolution-markers = [ - "python_full_version >= '3.15'", - "python_full_version >= '3.11' and python_full_version < '3.15'", + "python_full_version >= '3.11'", "python_full_version < '3.11'", ] @@ -27,7 +26,7 @@ wheels = [ [[package]] name = "appium-python-client" -version = "6.0.6" +version = "5.3.1" source = { editable = "." } dependencies = [ { name = "selenium" }, @@ -55,69 +54,28 @@ dev = [ [package.metadata] requires-dist = [ - { name = "selenium", specifier = ">=4.37.0,<5.0" }, - { name = "typing-extensions", specifier = "~=4.16" }, + { name = "selenium", specifier = ">=4.26,<5.0" }, + { name = "typing-extensions", specifier = "~=4.13" }, ] [package.metadata.requires-dev] dev = [ { name = "httpretty", specifier = "~=1.1" }, { name = "mock", specifier = "~=5.2" }, - { name = "mypy", specifier = ">=1.17,<3.0" }, - { name = "pre-commit", specifier = "~=4.6" }, - { name = "pytest", specifier = ">=8.4,<10.0" }, + { name = "mypy", specifier = "~=1.17" }, + { name = "pre-commit", specifier = "~=4.2" }, + { name = "pytest", specifier = "~=8.4" }, { name = "pytest-cov", specifier = ">=6.2,<8.0" }, { name = "pytest-xdist", specifier = "~=3.8" }, { name = "python-dateutil", specifier = "~=2.9" }, { name = "python-semantic-release", specifier = ">=10.3.1,<10.7.0" }, - { name = "ruff", specifier = "~=0.16" }, + { name = "ruff", specifier = "~=0.15" }, { name = "sphinx", specifier = ">=4.0,<9.0" }, { name = "sphinx-rtd-theme", specifier = "~=3.0" }, { name = "sphinxcontrib-apidoc", specifier = "~=0.6" }, { name = "types-python-dateutil", specifier = "~=2.9" }, ] -[[package]] -name = "ast-serialize" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/ad/0d70a3a2d6e01968d985415259e8ec7ad3f777903f9b1c1f3c8c44642c60/ast_serialize-0.6.0.tar.gz", hash = "sha256:aadd3ffcf4858c9726bf3515f7b199c7eadbe504f96028e4a87172c0da65a8fe", size = 61489, upload-time = "2026-06-30T20:02:55.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/12/3e5f575f156555547c250a8b0d1347517a3a20fc7f4492e9703a69d4f45e/ast_serialize-0.6.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:a7520b672827885bafeae7501f684d14d47d17e5f45256f9df547686cca52264", size = 1177640, upload-time = "2026-06-30T20:02:06.708Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a4/921a9e27951627983b0f368859ea00f8330a551dc0bf4c2fdcb11855a98b/ast_serialize-0.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a14191beec7e0c078d2fc1f6edc0aee88bcd4db9f18e1bc9f8052b559c22dddc", size = 1168111, upload-time = "2026-06-30T20:02:08.366Z" }, - { url = "https://files.pythonhosted.org/packages/00/69/950cf404de7b8782cf95e5c1237e25e2aa46177b287f39f9eeddf481fd6f/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32ef62ec34cf6be20ad77d4799556638fbdf187f3ae10698dfb20ef9f2c89516", size = 1227656, upload-time = "2026-06-30T20:02:09.843Z" }, - { url = "https://files.pythonhosted.org/packages/4c/a8/46f8f6a6479d9d2273980957bb091a506c55f5b95d3c029ee58518a78407/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13b7769970a39983b0adf2f38917b1cd3b8946f76df045756c3d741bc689f089", size = 1227706, upload-time = "2026-06-30T20:02:11.367Z" }, - { url = "https://files.pythonhosted.org/packages/b7/b9/9ac415bda0a40e49eab8fea3b2741c19c98bb84d57d62c4cfc6230eb67be/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f7a408601bb3edaefb3bc67a4c01f5235e3253653b6a5729a2ee2382b35341c", size = 1431705, upload-time = "2026-06-30T20:02:12.737Z" }, - { url = "https://files.pythonhosted.org/packages/e5/06/8807115d441444879f7561b5eede5ac18fc80392f11826d61ccf31f503b1/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8670bfa51208a2c0c8d138928e40e998fab158f9200d53bb80c088b5b8eda7b8", size = 1249533, upload-time = "2026-06-30T20:02:14.571Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c0/c2ba82ef9618650357d9421a1fdb27ffec862a7f57e8e2de82a3ccd11e12/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4826809eb8597a8cd59fd924b6d7c285b8969a1e0007e2cb652cab62376270f", size = 1252619, upload-time = "2026-06-30T20:02:16.219Z" }, - { url = "https://files.pythonhosted.org/packages/0f/a7/fa31d52dd4102cede29fb9634e98d214129b2783b4f95528c6dc6a8f6587/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:577a6c189068686869f5f1ddc38363f3ae1808a4753b577266f9202071a7bb66", size = 1242983, upload-time = "2026-06-30T20:02:17.813Z" }, - { url = "https://files.pythonhosted.org/packages/b1/20/ddf742b5ad3c4bafd3466f2265037cfd99bc1b9a5ee46a5d58c90d523242/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085de7f62dc9cc247eb01e965a362707d1d90b1d89a82c5bf78301a60a3c417b", size = 1296148, upload-time = "2026-06-30T20:02:19.146Z" }, - { url = "https://files.pythonhosted.org/packages/24/cb/9f6f217cce8b3b632c5568b478d195a35e79dce4dbe309438cb89ba6ea4f/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9f8a8b78b13173de6a9ec22111d9be674874cd5bdccda04f14ae5ebc2bef403a", size = 1403826, upload-time = "2026-06-30T20:02:20.696Z" }, - { url = "https://files.pythonhosted.org/packages/2d/f8/9d16d4f0107a183924425cc0e7618d8bf76f96b45afa9ff19f924ed1ad57/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f2ff3baffc3a29c1f15bc9098aa0c09763410262d5e6cef42116f7356c184554", size = 1502943, upload-time = "2026-06-30T20:02:22.034Z" }, - { url = "https://files.pythonhosted.org/packages/80/dd/bbc1c38756350dddf7e24acae1c9482ef42051c267417e019aecc1ed4075/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0067b25fce104eaae5b88383de9ab803faeb671831e14ca698b771b356e2600f", size = 1497632, upload-time = "2026-06-30T20:02:23.517Z" }, - { url = "https://files.pythonhosted.org/packages/42/7e/9daffefcf5b97e6bb4c3e0b3c024c1aee9722f23d3cf7cd2ff80d6fb4a40/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c617417f9cbb0cb144f6283c3cbe0d2e0f01beaf9f608f662b21191058a626ec", size = 1448858, upload-time = "2026-06-30T20:02:24.889Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1f/f9baaab81a677ea0af7d2458cac2f94ebcc85958f8a3c15ba9d9e5dab653/ast_serialize-0.6.0-cp314-cp314t-win32.whl", hash = "sha256:5337cb256dcea3df9288205213d1601581536526b8f4da44b6974f1180f3252a", size = 1052600, upload-time = "2026-06-30T20:02:26.263Z" }, - { url = "https://files.pythonhosted.org/packages/9e/1f/41b535866519512d8cf6669cb2cff7823b7672bb6279c0333b4ff89d7d9f/ast_serialize-0.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2d947e45cafc4b09bd7528917fa84c517654a43de173c79785574b7b3068ac24", size = 1095570, upload-time = "2026-06-30T20:02:27.639Z" }, - { url = "https://files.pythonhosted.org/packages/50/64/e472fe3e3a2d33d874b987e8518aedf24562919e3b6161a4fa1797e89c0f/ast_serialize-0.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6e15ec740436e1a0d62de848641abe5f3a2f89a7f94907d534795ac91bbacf14", size = 1067267, upload-time = "2026-06-30T20:02:28.949Z" }, - { url = "https://files.pythonhosted.org/packages/52/19/ac8348ae8711c9b5ae834634f635780cab62a0f5e6f988882e048b89c2ae/ast_serialize-0.6.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:093cb8bb91b720d8523580498d031791bb1bbaa048599c3d21085d380e11a596", size = 1185367, upload-time = "2026-06-30T20:02:30.427Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f6/ec7ec652c51db77c2f61d8573338e13e4704303265ccc658cb4031d9f354/ast_serialize-0.6.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e61580a69faf47e3689795367ed211f2a10fd741478cc0f36a0f128793360aad", size = 1178657, upload-time = "2026-06-30T20:02:31.964Z" }, - { url = "https://files.pythonhosted.org/packages/6f/02/613a7534a41d0122f37d1e0c64aa8ac78bfb831f8c92f6db057a311abb3c/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305802f2ce2a7c4e87835078ea85c58b586ddda8095b92fe2ead9364ae19c80a", size = 1238620, upload-time = "2026-06-30T20:02:33.664Z" }, - { url = "https://files.pythonhosted.org/packages/4d/21/087957bba486242afc52f49b2d9e21c9dad00289356cf9efe67084015a9d/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7b8b8f0c42f752ea00b2b7d7c090b3f80d9c1c5c75cadf16423790a0cc74081", size = 1236075, upload-time = "2026-06-30T20:02:34.936Z" }, - { url = "https://files.pythonhosted.org/packages/82/04/78128bbb170071c2c72a210a181f1c00e11cc1cec60a8beef747b07f9201/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd5b91b9e6f2356ace3a556963b0cd783b395fbbb0bb17b4defc283415466e77", size = 1441348, upload-time = "2026-06-30T20:02:36.245Z" }, - { url = "https://files.pythonhosted.org/packages/64/64/62fb99d6faf199b4c3e5b08a07136e9a0d7664bb249c6de3670e5b63e9b6/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d6ef91590258ada18909b9caea344dac4de2013906b035473cd674a43f4b790", size = 1258580, upload-time = "2026-06-30T20:02:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/ca/87/b4d6c38e0ccd5e85dc54cecdf933a152c60b28fe5d993a6d8a72fa6d5896/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcbed41e9386059fc0261d602445ede0976c2ecec2939688bcbcb9ed0b6f28b7", size = 1261693, upload-time = "2026-06-30T20:02:39.123Z" }, - { url = "https://files.pythonhosted.org/packages/0e/4b/3676ca2191f39bafb75f93f99b2f429ec464586158fece2165f3572805dc/ast_serialize-0.6.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:cdc4e6f930b9090c2f92c9036ad12ffb8e6e44d4a5ba06f1458a05d60f203f7b", size = 1252517, upload-time = "2026-06-30T20:02:40.511Z" }, - { url = "https://files.pythonhosted.org/packages/f3/58/494ef8c4b4acb2f4a265ac934caf45f792a08fe27d6b853de35ad991941a/ast_serialize-0.6.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:897ac47b5637be41c0c07061c8a912fafa967ef1dc73fa115e4bfa70882a093b", size = 1304843, upload-time = "2026-06-30T20:02:41.961Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f2/13736d920ab3d49bbee80ef1a277dd7b7aaf3b3545efd9d2a8114fe05525/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c4af9a1386166e40ed01464991806f89038a2d89782576c7774876fa77034e32", size = 1413698, upload-time = "2026-06-30T20:02:44.179Z" }, - { url = "https://files.pythonhosted.org/packages/a8/5a/e046f3899e2acba4677d7427b76431443a1aa1a0e583dfb05b55b69d55cf/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:c901adbd750029b9ac4ad3d6aa56853e0ad4875119fbf52b7b8298afc223828b", size = 1512209, upload-time = "2026-06-30T20:02:45.584Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c7/e42aaca7bb2d22a7c06d5a8c7930086c5a334e93d716e6fa5e6647a4515f/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae22a366b752ab4496191525b78b097b5b72d531752e3c1dd7e383a8f2c8a1a", size = 1508464, upload-time = "2026-06-30T20:02:46.942Z" }, - { url = "https://files.pythonhosted.org/packages/95/93/5524a3dc6c3f593de3228ed9cbef73afa047625b7000ec21b7f58e6eb4d4/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4ed29121da8b3fdc291002801a1de0f76248fa07dce89157a5f277842cf6126e", size = 1457164, upload-time = "2026-06-30T20:02:48.294Z" }, - { url = "https://files.pythonhosted.org/packages/4f/c0/36a6ffb4d653cf621427b4c4928671f53ad800c453474de2b82564a44ad9/ast_serialize-0.6.0-cp39-abi3-pyemscripten_2026_0_wasm32.whl", hash = "sha256:b1dac4e09d341c1300ba69cdcbe62867b32a8c75d90db9bf4d083bec3b039f0b", size = 863014, upload-time = "2026-06-30T20:02:49.742Z" }, - { url = "https://files.pythonhosted.org/packages/09/c7/7d5ad8b49e1278e1c2a1e0274bd7850560b3f09313aa00c13bc8d5544792/ast_serialize-0.6.0-cp39-abi3-win32.whl", hash = "sha256:82c312a7844d2fdeb4d5c48bd3d215bf940dafd4704e1a9bcf252a99010a99b1", size = 1063165, upload-time = "2026-06-30T20:02:50.98Z" }, - { url = "https://files.pythonhosted.org/packages/47/ae/6710c14ecb276031cf10249f6adf5a59e2d3fdb3b5183bd59f70524067ee/ast_serialize-0.6.0-cp39-abi3-win_amd64.whl", hash = "sha256:113b58346f9ceb664352032770caca817d4a3c86f611c6088e6ef65ddaa70f0e", size = 1101444, upload-time = "2026-06-30T20:02:52.554Z" }, - { url = "https://files.pythonhosted.org/packages/66/40/c53deb2cd0c9b0fb636d24d9f40924cf2e65028e6b20b10cd5c1eeb2c730/ast_serialize-0.6.0-cp39-abi3-win_arm64.whl", hash = "sha256:ccd132fe8db56f61fe743b1f644d01b8d65b83248a8da506f3132bda86d6ed5e", size = 1072965, upload-time = "2026-06-30T20:02:54.097Z" }, -] - [[package]] name = "attrs" version = "25.4.0" @@ -452,7 +410,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -491,14 +449,14 @@ wheels = [ [[package]] name = "gitpython" -version = "3.1.59" +version = "3.1.47" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/dc/126b28e76b24a9268ba931ad3e012f71ebdadf62fd9f17758f7074bb0b20/gitpython-3.1.59.tar.gz", hash = "sha256:0a1475cfdc38a5bfba1a3e9a4a9da52a39749ecec322b772915c019f94e5b7e4", size = 230445, upload-time = "2026-08-10T12:03:20.271Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/bd/50db468e9b1310529a19fce651b3b0e753b5c07954d486cba31bbee9a5d5/gitpython-3.1.47.tar.gz", hash = "sha256:dba27f922bd2b42cb54c87a8ab3cb6beb6bf07f3d564e21ac848913a05a8a3cd", size = 216978, upload-time = "2026-04-22T02:44:44.059Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/ed/ae57eb7d344f43f87b74b3a281ead6ec7d6394eef72a7b1dcb28dd089550/gitpython-3.1.59-py3-none-any.whl", hash = "sha256:67a82f537384578643624c8b2c531938a9b82be431663e575dcf638526631d4c", size = 220996, upload-time = "2026-08-10T12:03:18.804Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c5/a1bc0996af85757903cf2bf444a7824e68e0035ce63fb41d6f76f9def68b/gitpython-3.1.47-py3-none-any.whl", hash = "sha256:489f590edfd6d20571b2c0e72c6a6ac6915ee8b8cd04572330e3842207a78905", size = 209547, upload-time = "2026-04-22T02:44:41.271Z" }, ] [[package]] @@ -527,11 +485,11 @@ wheels = [ [[package]] name = "idna" -version = "3.15" +version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[package]] @@ -543,6 +501,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, ] +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, +] + [[package]] name = "iniconfig" version = "2.3.0" @@ -566,89 +533,75 @@ wheels = [ [[package]] name = "librt" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/2f/3908645ddddab7120b46295e541ead308109fa48dbec7d67d7a778870d60/librt-0.13.0.tar.gz", hash = "sha256:1d2a610c14ac0d0750ee0a3ab8548e83155258387891caaca04def4bf7289781", size = 211402, upload-time = "2026-07-08T12:26:29.834Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/2f/ec5241c38e7fa0fe6c26bfc450e78b9489a6c3c08b394b85d2c10e506975/librt-0.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:34e47058fcc69a313293d6dee94216a4f30c929ae6f2476e58c5ba635aa639d5", size = 148654, upload-time = "2026-07-08T12:24:30.622Z" }, - { url = "https://files.pythonhosted.org/packages/a5/1a/d651e18d3ee7aa2879322368c4f278bb7ecaa6b90caadfdec4ebfa8389f3/librt-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dbdd5b6509d0c2a8fe72cf494c299a61dbd58142a90a4190664ae159e4a7b547", size = 153537, upload-time = "2026-07-08T12:24:31.773Z" }, - { url = "https://files.pythonhosted.org/packages/45/18/10bff2122577246009d9619b6569596daf69b7648812f997ca9ca0426f60/librt-0.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e56ea4ee4df77585a6b5c138f6538680886024fa559f5b55bd14b12e98e67b2", size = 494336, upload-time = "2026-07-08T12:24:33.079Z" }, - { url = "https://files.pythonhosted.org/packages/67/69/87dfee871b852970f137fdeae8e2ca356c5ab38e6f21d2a3299535fc3159/librt-0.13.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f1f9cc4d09a46d9cb3c2063ae100629d3f52a6517c3c08c2f4c9828261883929", size = 485393, upload-time = "2026-07-08T12:24:34.324Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d5/625447a8c0441ff5f15f4ac5e1d323fb9d4d256ebfde7a3c8e003f646057/librt-0.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f125f5d46b20f89dc5587a55cc416b4ba2a5b2ffda36d048ee120e17598a653a", size = 515382, upload-time = "2026-07-08T12:24:35.575Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d8/1c8c49ea04235960426444deece9092a6b3a9587a850a81bae2335317411/librt-0.13.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2608d3b39f9e0b4a66a130d9150c615cba40a5090d25eeeaa225e0e46de8c0ac", size = 509483, upload-time = "2026-07-08T12:24:36.923Z" }, - { url = "https://files.pythonhosted.org/packages/6f/65/f1760fc48050e215201a03506c32b7270159088d01f64557b53e39e74a45/librt-0.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9fd35e95ab5e45c3901d37110263c7db85a961110f5460588fe37f8c131f88a7", size = 532503, upload-time = "2026-07-08T12:24:38.203Z" }, - { url = "https://files.pythonhosted.org/packages/18/1b/793e281dcf494879eff99f642b63ebc9c7c58694a1c2d1e93362a22c7041/librt-0.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5f31b0aa13c9b04370d4da6be1ab7779776b3a075cceb6747a39a4be85fe1e40", size = 537027, upload-time = "2026-07-08T12:24:39.34Z" }, - { url = "https://files.pythonhosted.org/packages/69/45/0801bbb40c9eea795d3dd3ce91c4c5f3fe7d42d23ec4be3e8cb283bcc754/librt-0.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0b795f5fc70fbbb787ceaf79bb3a0d627bcc33c53de51741755263ec406b775a", size = 517100, upload-time = "2026-07-08T12:24:40.907Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6c/eb5f514f8e29d4924bc0ff4601dd7b4175557e182e7c0721e84cffa39b8a/librt-0.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36b306a623aaad96fe4b378692b54f9c0789fccd833b9851753d5fbf6138cfde", size = 558653, upload-time = "2026-07-08T12:24:42.359Z" }, - { url = "https://files.pythonhosted.org/packages/b4/bf/f140100d1b59fe87ff40b5ecbb4e27924335b189a784e230ee465452f6c2/librt-0.13.0-cp310-cp310-win32.whl", hash = "sha256:a3762e75fcac8c9e4dacaaf438bffd9003e2ca2c531b756f3c0035deefa674c8", size = 104402, upload-time = "2026-07-08T12:24:43.668Z" }, - { url = "https://files.pythonhosted.org/packages/22/7c/57e40fef7cfb61869341cb28bdcefe8a950bebcbecca74a397bae14dce4a/librt-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:d63bae12a8aeb51380be3438e4dc4bd27354d0f8e19166b2f44e3e94d6f552dc", size = 125002, upload-time = "2026-07-08T12:24:44.793Z" }, - { url = "https://files.pythonhosted.org/packages/89/25/a6498964cfeec270c468cffdc118f69c29b412593610d55fa1327ca51ff4/librt-0.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1b5a7bbff495baedbd9b916c367d66854008f8f3b575908ded477c499dc60082", size = 148029, upload-time = "2026-07-08T12:24:45.961Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/dc86d1bffd8e0c2818bace29d9f7783cfbb8e0673bf3673b5bbd5bbe0420/librt-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34bc7938b9fdf14fe32a406c19c71faf894c5cee7e7474bd0be2f17200b82d14", size = 153036, upload-time = "2026-07-08T12:24:47.257Z" }, - { url = "https://files.pythonhosted.org/packages/29/3f/b923826660f02f286186cd9303d52bb05ced0a13708edc104dc8480920e3/librt-0.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f40e56b61b41be5f7dec938cfeffd660668cf4b5e72c78e7bd671d66b7bc2c79", size = 493062, upload-time = "2026-07-08T12:24:48.483Z" }, - { url = "https://files.pythonhosted.org/packages/88/87/6c0980a9c9b1302cb68d108906697b89eceb55889bb1dcf77c109aa56ca5/librt-0.13.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:9c5d02b89de5acd0379a51ec44a89476fb03df6145442e1c8ecd6bee2f91b176", size = 485510, upload-time = "2026-07-08T12:24:49.727Z" }, - { url = "https://files.pythonhosted.org/packages/32/81/795ae3b9df5dd94079fb807e38191855e023e8c6249014ae6bc3f0d9a490/librt-0.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7db9a3ff32ef5f7d1703d93831a3316cdf0b537de6a1cc03cc8fdd09b9194e89", size = 515909, upload-time = "2026-07-08T12:24:51.135Z" }, - { url = "https://files.pythonhosted.org/packages/20/e5/182de15abce8907108a6fdb41487de65beb5099b74dc5841b19b099168db/librt-0.13.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3dbb2a31882456cadc7053378e81ad7ed7693db4ac9f98ab5f81ef034aa8ec9f", size = 508620, upload-time = "2026-07-08T12:24:52.358Z" }, - { url = "https://files.pythonhosted.org/packages/32/03/33978d32db76e1f66377e8f78e42a2ca3c162143331677d1f50bbad36cfb/librt-0.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c6014e3c80f9c1fe268ef8b0e0ef113bac672cc032f2f93866e7ddad4f3e663d", size = 530363, upload-time = "2026-07-08T12:24:53.503Z" }, - { url = "https://files.pythonhosted.org/packages/e6/f5/b291fbd2d00f7d8287bcbf67b5aa0c6afed4bc26cef23e079629c47a2c04/librt-0.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:091b60a4d2174fc1ec5c34cdc0b72efb6224753d76b7da61ebeab7a191aec8bd", size = 534209, upload-time = "2026-07-08T12:24:55.138Z" }, - { url = "https://files.pythonhosted.org/packages/3e/03/6f41f17939d191bc21609f220da8509316bc62797f078545fe83be522e78/librt-0.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:66cb1138f384a191a6d75f986064841fcfdc0cea98f7bd9c9ab9b38049917588", size = 514254, upload-time = "2026-07-08T12:24:56.276Z" }, - { url = "https://files.pythonhosted.org/packages/af/c2/2e4befa5410a7443019c14abccc94ff619797171f6b72013635fb87f31d7/librt-0.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:17221a7569f8f292aa0014226e48aa25b8c2b08da18088cd230953d0ea0f9cd1", size = 557611, upload-time = "2026-07-08T12:24:57.561Z" }, - { url = "https://files.pythonhosted.org/packages/ab/54/8b69f81448417adbc040a2185f4e2eece1e1994b7dcfaeed4662b30f98a5/librt-0.13.0-cp311-cp311-win32.whl", hash = "sha256:fc67741da44c6eaa90e01eafb586bbba9b51eb5b6ed381ee6f5ae72eb3316d21", size = 104906, upload-time = "2026-07-08T12:24:58.806Z" }, - { url = "https://files.pythonhosted.org/packages/76/5a/f4aaf37b50f2fde12c8c663b83fdd499cdc24f957f19543d7414bfcc9e25/librt-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc99dfb62b23c9207c33d0be8a2e2af7a42e21e6ea388b380a0c948c7b88953b", size = 125852, upload-time = "2026-07-08T12:25:00.065Z" }, - { url = "https://files.pythonhosted.org/packages/f2/99/bf1820e6feeabc2f218c24450ec0c995d6a91e8ba0fd3caf042c9e8adb2a/librt-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:40ccd13c252d3fe473ffc8a57be7565abc8b64cf1b108344c859d5164f7f3e0c", size = 111832, upload-time = "2026-07-08T12:25:01.148Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f4/b2933ddae222dac338476abb872641169a5cfed2c2bb5444a5b07b32b0c3/librt-0.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30536798f4504c0fad0885b1d371b0539abb081e4570c9d7c641cb51141b49f0", size = 150990, upload-time = "2026-07-08T12:25:02.42Z" }, - { url = "https://files.pythonhosted.org/packages/90/ef/db98f744ca50e6efc9c95c70ee49b77aefac31f6a3fc7c83754a42d6a74f/librt-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93d24ebb82aa4420b1409c389e7857bc35bd0b668007ac8172427d5c73cc8cc5", size = 155238, upload-time = "2026-07-08T12:25:03.681Z" }, - { url = "https://files.pythonhosted.org/packages/03/e7/a197e7bc72baf2c61ce7fdc6906a5054dc05bd8da0819aa894e4857bf87e/librt-0.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb8a1adce42d8b75485a5d56a9623a50bcab995b6079f1dac59fc44034dd93d9", size = 503073, upload-time = "2026-07-08T12:25:05.049Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e7/7887712e27da7c1ab80fcabb1de6eb24243964f6557cae530d4b70706dbd/librt-0.13.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0763ca2ab66058174f9dee426dc64f5e0a89c24a7df8d3fe3f1836c04e25de4b", size = 496528, upload-time = "2026-07-08T12:25:06.26Z" }, - { url = "https://files.pythonhosted.org/packages/94/f0/f2283385bb6b950b26a1410f4ce51ec27231e0b3a4b925c46366d218b198/librt-0.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b222493da6e7b6199db9bd79502436cf5a27da3c1f7fa83c7e285444fc93fd03", size = 531786, upload-time = "2026-07-08T12:25:07.658Z" }, - { url = "https://files.pythonhosted.org/packages/36/11/69ac3b54766ffba5fd7e5acebfb048d66dbe1f9f2d14516c2b3edc59cf87/librt-0.13.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fadc63331f4388c3dc90090448f682a7e9feafc11481391c1e94f2f907a3976e", size = 524393, upload-time = "2026-07-08T12:25:09.121Z" }, - { url = "https://files.pythonhosted.org/packages/61/5f/d72f95fd444a926a3c14b4e24979474116988dd57a45be242077c45d3c22/librt-0.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:70d9c62a4cffd9f23396cd5ef93fc5d11b31596b9b7d6306074abe3d5fcf09bd", size = 543026, upload-time = "2026-07-08T12:25:10.459Z" }, - { url = "https://files.pythonhosted.org/packages/c4/08/dcd9993ad192737a004ba263d549f8ea605b326b952e7d6205c7d4170b76/librt-0.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66c0e7e6b02a155576df2c77ec933a70b72da726e248c494abf690923e624348", size = 546829, upload-time = "2026-07-08T12:25:11.716Z" }, - { url = "https://files.pythonhosted.org/packages/96/d5/6d9bb2f54e4109a956b7128836529653eb9d740f784bc47ed10a02c1000e/librt-0.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ac04bcd3328eb91d99dfedf6a60d9c1f15d3434e6f6daf922f0420f7d90b85c7", size = 535700, upload-time = "2026-07-08T12:25:13.144Z" }, - { url = "https://files.pythonhosted.org/packages/8c/f2/10946922503858a359492fa27f13e86228bde702116a740ac7b3cd185f24/librt-0.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db327e7271e653c32040b85ae6188059c924b57d7e1e29f935523fa017cd4e82", size = 573566, upload-time = "2026-07-08T12:25:14.336Z" }, - { url = "https://files.pythonhosted.org/packages/48/a8/94f00e3c99479a18088af3685ea016c42f3c7d5d1964d8dbb40c08d7f1aa/librt-0.13.0-cp312-cp312-win32.whl", hash = "sha256:860bd1d8ba48456ce08feaf8d343a8aaeb2fa086f2bcaa2a923fa3f7a3ff9aa3", size = 106099, upload-time = "2026-07-08T12:25:16.159Z" }, - { url = "https://files.pythonhosted.org/packages/c9/7b/2da9c74c1ed25a89cc4e1c8e007ea2eb4a0f1fafa3e70d757fe3242c5c5c/librt-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:e54a315caf843c8d77e388cadc56ea9ded569935ee2d2347d7ea94992e5aa6fa", size = 126934, upload-time = "2026-07-08T12:25:17.275Z" }, - { url = "https://files.pythonhosted.org/packages/d0/65/aead61bbf3b5358593f9d4779d2a0e88eaf6ec191a6342dde36dd1df6371/librt-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:c718e99a0992127af84385378460db624103b559ab260435abcfe77a4e4ed1c1", size = 112236, upload-time = "2026-07-08T12:25:18.425Z" }, - { url = "https://files.pythonhosted.org/packages/67/3b/18e7b63255297a2bdc9c25c8d6d4ca8eca9f63aceb1252c0f7427ac7099e/librt-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a468951af16155824e88bdd8326ebe5bdb371f3ec0ac04642994b98201d914f3", size = 151027, upload-time = "2026-07-08T12:25:19.638Z" }, - { url = "https://files.pythonhosted.org/packages/4d/68/e2248452c00d1a03b45fee1752cdc8f790a476efd2402b75181da88a9e61/librt-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ae01d8512cc17079e53425635327dbf3f7ff57a42c00dec348bf79791c56444c", size = 155152, upload-time = "2026-07-08T12:25:20.851Z" }, - { url = "https://files.pythonhosted.org/packages/0e/16/52b1c99bf19057a062aac39c900cbb81499f6f75d6c537c14463d247ba78/librt-0.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32c26893cd085c1efe83219e78d866da23fb20a066101b8f68210004361d224c", size = 502499, upload-time = "2026-07-08T12:25:22.055Z" }, - { url = "https://files.pythonhosted.org/packages/9f/54/b811151805c795f55e0dedee6ec687b75f9982a8105d240ea3910737a77b/librt-0.13.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5929da1981a46bcf4b28b1b9499905f0ff58e2419da402a048234e9783acbc4b", size = 496108, upload-time = "2026-07-08T12:25:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/8f/f8/094d6b2bd93f3fdaa54db54cc788c4a365333bddad65ab02e04da0b1d004/librt-0.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b85d664d777bab6c0d709416cb42938251fda9e221b79e3a2215d85df5f4f9", size = 531576, upload-time = "2026-07-08T12:25:24.648Z" }, - { url = "https://files.pythonhosted.org/packages/2e/40/541733d5755824f968f7ec39d78ffbd75d145964157ae5e69a09ec6d7326/librt-0.13.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:531b2df3e9fe96b1fcf73a6d165921e4656be5f58d631d384ebce344298368db", size = 524390, upload-time = "2026-07-08T12:25:25.898Z" }, - { url = "https://files.pythonhosted.org/packages/c6/b5/255673cfdbf5ba663339d36cd863c897289ab4337577e19f9405ce059f36/librt-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:109b84a9edf69ad89dc1f66358659e14a031baca95e3e5b0060bd903ede8efd6", size = 543053, upload-time = "2026-07-08T12:25:27.436Z" }, - { url = "https://files.pythonhosted.org/packages/9e/11/ab5005e9c9850710f21e354201bf090646349d3fabf5f951eaf70235729e/librt-0.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1304368a3e7ffc3e9db986796cc5326fdb5943a3567ecc137cff318e4240c0e7", size = 546387, upload-time = "2026-07-08T12:25:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/a2/04/a5d7ce1d1df1afd15ca283dcdf7530ac073e12d69ae8c40879dda96f7868/librt-0.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e4f9b472e7d308d94b62c801982065661158c6ed02790d6c7ddb4337cea0f9c1", size = 535970, upload-time = "2026-07-08T12:25:30.171Z" }, - { url = "https://files.pythonhosted.org/packages/5a/76/927e267a6daa290174ac281b23c9804c8829b042ade9c6f24a065f540958/librt-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f836c37478f167a81200d8c8b2c920a22224564bed2c23d7aeec760965c367a", size = 573582, upload-time = "2026-07-08T12:25:31.507Z" }, - { url = "https://files.pythonhosted.org/packages/10/24/b6c5213efe39c19f9e13605644d0cf063b4ddaa33ac2e45b088e23a70e2e/librt-0.13.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:4000d961ff9598ac6ea603c6c836a5ed49bc205ade5fc378b998dfe1e2c36628", size = 82189, upload-time = "2026-07-08T12:25:32.675Z" }, - { url = "https://files.pythonhosted.org/packages/4c/00/d29736be177a906ac0b84a5b04b4fbfa22c776dc2f366de4172b0f968c08/librt-0.13.0-cp313-cp313-win32.whl", hash = "sha256:79e44cff71750d299d61a678e49995b0d5935a9cda238c2574daeca3ba536927", size = 106193, upload-time = "2026-07-08T12:25:33.692Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ac/aff6fb45393cb8912f39dfb156ef6b2d1cadb207ff465fc8f66141054be8/librt-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:54dab44a847d5ad1acd05c8a83fe518ae685516ecf4d3f7cc6e3df2a66767650", size = 126962, upload-time = "2026-07-08T12:25:34.769Z" }, - { url = "https://files.pythonhosted.org/packages/d9/3a/d68cb2b334d53fd30fac81d3a489ce4ba0d9506f4df43fcf676b68352b19/librt-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:d4cb6fbfdf874340ab5e51450753c0f817b6958a3621125ee695bbc3de866566", size = 112127, upload-time = "2026-07-08T12:25:35.981Z" }, - { url = "https://files.pythonhosted.org/packages/7b/66/f49ae0d592bd45b6941e9a8bafcb6a87cddcd501ee7874707e767f01b585/librt-0.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:25218d94b1d2cbc0ba1d8a3f9dc9af578d9646e5ed16443a70cde1dfdcce6d71", size = 149818, upload-time = "2026-07-08T12:25:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/3d/50/51c76d74014d04fb95b6506d286808984b78a2f7a41039094e6b2194ac48/librt-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f26629539d4893c2957a16c41bb058e1e135c1f150f6a2e25ed047f64cf3f5c6", size = 154071, upload-time = "2026-07-08T12:25:39.399Z" }, - { url = "https://files.pythonhosted.org/packages/b8/fe/f19b0f5f82d5a1f2da736586bc840abd00ce07d6388136ae80b7333883fc/librt-0.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4517d47b2b8af26975a406fba7d314de9696d864252e0257c6ea90238cfe27f", size = 494168, upload-time = "2026-07-08T12:25:40.641Z" }, - { url = "https://files.pythonhosted.org/packages/94/bc/b8550c75775127fd31a5f20e8775997f7b527ad661fc8ddccd7497c064f7/librt-0.13.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f19e181de5b3a1148bb3420b8c4b0b0ea0fce6950099724ad151d6cea5acc180", size = 491054, upload-time = "2026-07-08T12:25:41.905Z" }, - { url = "https://files.pythonhosted.org/packages/30/14/4d0204867623df3f33f86efd3d3692ba5e01321443f4d6eab35a22697618/librt-0.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22034924f5b42d5a56371cf271771bfeaabf235a7a8b6264bef2d20013f786c6", size = 523006, upload-time = "2026-07-08T12:25:43.327Z" }, - { url = "https://files.pythonhosted.org/packages/19/0a/c45fc9a260934696bace1ac5df1e148ac92bd71767aee3bf7cd7a4534f4c/librt-0.13.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7897db4e95e22468bdda33d8e012ceacd0182abf001e6389d763f0def6286b9", size = 515058, upload-time = "2026-07-08T12:25:44.541Z" }, - { url = "https://files.pythonhosted.org/packages/13/0a/50c5ce45b326854ef8fa6ae4c36cf5142e5c55315eaf9e51d0ae73ac4da3/librt-0.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1ce61b3746545029d4f5c17d6bd74b676254ad98433086c846ffb5e8fa73f007", size = 534025, upload-time = "2026-07-08T12:25:45.825Z" }, - { url = "https://files.pythonhosted.org/packages/89/2d/08c413c8f93fc13b8103624fce38e5caa86cd08cbbc8465870ab287af54b/librt-0.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:46c330e82565962c761dbce7941be2cff7db674ee807455a8d0cadc5f9b759b0", size = 540557, upload-time = "2026-07-08T12:25:47.059Z" }, - { url = "https://files.pythonhosted.org/packages/b3/c1/93af71fb4a364952210051811dd4e40174e79656b050c89cacac18af3330/librt-0.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:375f5af8f99cbaa99dd293af986e3d57caabc9ba81a5d3f021603764854197a1", size = 523201, upload-time = "2026-07-08T12:25:48.392Z" }, - { url = "https://files.pythonhosted.org/packages/c1/6e/9766f07b676a4889d9f8bc2864e9ba5fff165653143ef4dda7df6aa34d16/librt-0.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9320d34c3376ae204b2cd176e8d4883a013934e0aef822f1aed9c536490c275d", size = 565740, upload-time = "2026-07-08T12:25:49.678Z" }, - { url = "https://files.pythonhosted.org/packages/a2/1e/664e3472ce2b6e10e9b83f29d4a36eb982ff6b5a169ae7567bba3a4c4ff5/librt-0.13.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:9af313c66157a69dc69ea0059a66961692250e0dc95af9c385a48ffb770a0d16", size = 81611, upload-time = "2026-07-08T12:25:50.857Z" }, - { url = "https://files.pythonhosted.org/packages/2f/d4/8582a4d65e2234673685e07309d02c230b28a85724eb0acbf13f019b7f6e/librt-0.13.0-cp314-cp314-win32.whl", hash = "sha256:f2a7253458e34f33543551394ae4fe104b497ec2a65ac266074de64c1df82e37", size = 100106, upload-time = "2026-07-08T12:25:52.03Z" }, - { url = "https://files.pythonhosted.org/packages/63/ce/0cb99efe6086b46cd985dc26672166fae312a239690e75871f7fafbd3fc5/librt-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:a3dfe4edf10e8ed7e55b026a8bfc2c2a8704218b659cd4bffdf604fab966dc39", size = 121209, upload-time = "2026-07-08T12:25:53.166Z" }, - { url = "https://files.pythonhosted.org/packages/26/85/4f3ccb083a3c9b0d42e223acdb3c3f507953324a59cdcab4826e8e2e3b89/librt-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:68a5faee4bba381cb93b5961f684a514cf0053cb92308ff9c792c2fea0b174c6", size = 106404, upload-time = "2026-07-08T12:25:54.253Z" }, - { url = "https://files.pythonhosted.org/packages/b2/77/333191499538c8e8189de7a4cba8e6f49ee949fd6d6e6324b21fd1522466/librt-0.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a38fb81d8376dfa2f8963b265fec07637802b0d01e2a127c19c66cb070fb24f5", size = 159231, upload-time = "2026-07-08T12:25:55.432Z" }, - { url = "https://files.pythonhosted.org/packages/7a/9e/2aa83758f22c278b837a1d8025898434ce2b8bff36678d5330ecaef56dff/librt-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d4c8d9bd5abce34b2e75edb3bf37ab0f34e49b1f915a40ae8468eb7c85bc5b46", size = 161300, upload-time = "2026-07-08T12:25:56.585Z" }, - { url = "https://files.pythonhosted.org/packages/bb/c0/86791e936553ca763d6b3c2fb4d31d596cd00e14fa631c283a40ba01559a/librt-0.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:387e2f1d27e89bffe0d3f520f0da0662c973fd607ca16c1808f8a5085419485e", size = 582056, upload-time = "2026-07-08T12:25:58.144Z" }, - { url = "https://files.pythonhosted.org/packages/a8/d3/a9ec15984a185e000c4d2a16ba28bd623124ad4c38a10974c7ff78e3a893/librt-0.13.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:4f6db193d2e5e0ed60359b9a5a682cd67205d0d3b1e459a867dd4b5c4e7eaa7a", size = 562758, upload-time = "2026-07-08T12:25:59.544Z" }, - { url = "https://files.pythonhosted.org/packages/3c/af/dbe36b78b19c06a55097f99305e4ea9458e2273e6ae16a3cbecaad7ee978/librt-0.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d38604854e8d22faadf683ec6c02bb0f886e2ba56ef981a1c36ee275f21ea22", size = 602095, upload-time = "2026-07-08T12:26:00.991Z" }, - { url = "https://files.pythonhosted.org/packages/2a/a8/2966891b4dd2830f5203fbee92ac2c4947653a2390ba73dfa44244fad025/librt-0.13.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:371f7ce73026815dafd51c50ce38416e91428b28c4b2ec97cd39271164b0045c", size = 593452, upload-time = "2026-07-08T12:26:02.352Z" }, - { url = "https://files.pythonhosted.org/packages/61/f5/4df8bfc8405ecf8c0d525b4d69636f694bdd8620b313ec8b76e54a5926cc/librt-0.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3aaedf52171bee90860704c560bc798fe83b76247df47568e0197e9b13c735a0", size = 623729, upload-time = "2026-07-08T12:26:04.294Z" }, - { url = "https://files.pythonhosted.org/packages/d6/13/9ac202dffc8db06f75d06c08c2f9f6ff054be67d21272dcc078fa1cc0c57/librt-0.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:96bad8725a4f196a798366c25ce075d1f7543a4ec045ffc13e6a7ec095cdab04", size = 617077, upload-time = "2026-07-08T12:26:05.845Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f0/ebe38610716aee5cb28efd95089bb90192096179802779381e1c5dcf239c/librt-0.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:6bf6a559ffe4a93bbea6cf31ddf01a7fd9ba342ef51f27beb178e318b74acd61", size = 599561, upload-time = "2026-07-08T12:26:07.21Z" }, - { url = "https://files.pythonhosted.org/packages/4f/5c/c2e72e236fff7abc716d5b1753b8b8cd3ea85ac46fe17d2e7c51d4e1c723/librt-0.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:301067672387902c55f94b51d5022304b36c966ea9fe1f21caab99a9bef487c9", size = 645511, upload-time = "2026-07-08T12:26:08.562Z" }, - { url = "https://files.pythonhosted.org/packages/0c/99/6203ce619dee940d6bfbe099ec3fe4be00a68e9d60f70abf906cf124fe66/librt-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:5fdcf34f86de8fb66d7dc7589f96ba91c4aa46671200d400e6fd6f109a483f18", size = 104357, upload-time = "2026-07-08T12:26:09.828Z" }, - { url = "https://files.pythonhosted.org/packages/52/dd/843b6314087c41657c7036d7914d8f294bdf9b580aa8513ea0588c8e9a3d/librt-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:260c33e92263fa629b4f6d3c51967a1c2158fe6c33237aaa3ebeac586b085259", size = 126998, upload-time = "2026-07-08T12:26:10.975Z" }, - { url = "https://files.pythonhosted.org/packages/5f/5d/3dcec2884ba1b0806d1408612555c38dd5d68e90156b59f75f6e36435c3a/librt-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2f281549a4c52ac7bb97997f14353f8bd0e53a34ca0dad1c905cfd0b4a58ae99", size = 110771, upload-time = "2026-07-08T12:26:12.303Z" }, +version = "0.7.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/24/5f3646ff414285e0f7708fa4e946b9bf538345a41d1c375c439467721a5e/librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862", size = 148323, upload-time = "2026-01-14T12:56:16.876Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/13/57b06758a13550c5f09563893b004f98e9537ee6ec67b7df85c3571c8832/librt-0.7.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b45306a1fc5f53c9330fbee134d8b3227fe5da2ab09813b892790400aa49352d", size = 56521, upload-time = "2026-01-14T12:54:40.066Z" }, + { url = "https://files.pythonhosted.org/packages/c2/24/bbea34d1452a10612fb45ac8356f95351ba40c2517e429602160a49d1fd0/librt-0.7.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:864c4b7083eeee250ed55135d2127b260d7eb4b5e953a9e5df09c852e327961b", size = 58456, upload-time = "2026-01-14T12:54:41.471Z" }, + { url = "https://files.pythonhosted.org/packages/04/72/a168808f92253ec3a810beb1eceebc465701197dbc7e865a1c9ceb3c22c7/librt-0.7.8-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6938cc2de153bc927ed8d71c7d2f2ae01b4e96359126c602721340eb7ce1a92d", size = 164392, upload-time = "2026-01-14T12:54:42.843Z" }, + { url = "https://files.pythonhosted.org/packages/14/5c/4c0d406f1b02735c2e7af8ff1ff03a6577b1369b91aa934a9fa2cc42c7ce/librt-0.7.8-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66daa6ac5de4288a5bbfbe55b4caa7bf0cd26b3269c7a476ffe8ce45f837f87d", size = 172959, upload-time = "2026-01-14T12:54:44.602Z" }, + { url = "https://files.pythonhosted.org/packages/82/5f/3e85351c523f73ad8d938989e9a58c7f59fb9c17f761b9981b43f0025ce7/librt-0.7.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4864045f49dc9c974dadb942ac56a74cd0479a2aafa51ce272c490a82322ea3c", size = 186717, upload-time = "2026-01-14T12:54:45.986Z" }, + { url = "https://files.pythonhosted.org/packages/08/f8/18bfe092e402d00fe00d33aa1e01dda1bd583ca100b393b4373847eade6d/librt-0.7.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a36515b1328dc5b3ffce79fe204985ca8572525452eacabee2166f44bb387b2c", size = 184585, upload-time = "2026-01-14T12:54:47.139Z" }, + { url = "https://files.pythonhosted.org/packages/4e/fc/f43972ff56fd790a9fa55028a52ccea1875100edbb856b705bd393b601e3/librt-0.7.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b7e7f140c5169798f90b80d6e607ed2ba5059784968a004107c88ad61fb3641d", size = 180497, upload-time = "2026-01-14T12:54:48.946Z" }, + { url = "https://files.pythonhosted.org/packages/e1/3a/25e36030315a410d3ad0b7d0f19f5f188e88d1613d7d3fd8150523ea1093/librt-0.7.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff71447cb778a4f772ddc4ce360e6ba9c95527ed84a52096bd1bbf9fee2ec7c0", size = 200052, upload-time = "2026-01-14T12:54:50.382Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b8/f3a5a1931ae2a6ad92bf6893b9ef44325b88641d58723529e2c2935e8abe/librt-0.7.8-cp310-cp310-win32.whl", hash = "sha256:047164e5f68b7a8ebdf9fae91a3c2161d3192418aadd61ddd3a86a56cbe3dc85", size = 43477, upload-time = "2026-01-14T12:54:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/fe/91/c4202779366bc19f871b4ad25db10fcfa1e313c7893feb942f32668e8597/librt-0.7.8-cp310-cp310-win_amd64.whl", hash = "sha256:d6f254d096d84156a46a84861183c183d30734e52383602443292644d895047c", size = 49806, upload-time = "2026-01-14T12:54:53.149Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a3/87ea9c1049f2c781177496ebee29430e4631f439b8553a4969c88747d5d8/librt-0.7.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3e9c11aa260c31493d4b3197d1e28dd07768594a4f92bec4506849d736248f", size = 56507, upload-time = "2026-01-14T12:54:54.156Z" }, + { url = "https://files.pythonhosted.org/packages/5e/4a/23bcef149f37f771ad30203d561fcfd45b02bc54947b91f7a9ac34815747/librt-0.7.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb52499d0b3ed4aa88746aaf6f36a08314677d5c346234c3987ddc506404eac", size = 58455, upload-time = "2026-01-14T12:54:55.978Z" }, + { url = "https://files.pythonhosted.org/packages/22/6e/46eb9b85c1b9761e0f42b6e6311e1cc544843ac897457062b9d5d0b21df4/librt-0.7.8-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e9c0afebbe6ce177ae8edba0c7c4d626f2a0fc12c33bb993d163817c41a7a05c", size = 164956, upload-time = "2026-01-14T12:54:57.311Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3f/aa7c7f6829fb83989feb7ba9aa11c662b34b4bd4bd5b262f2876ba3db58d/librt-0.7.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:631599598e2c76ded400c0a8722dec09217c89ff64dc54b060f598ed68e7d2a8", size = 174364, upload-time = "2026-01-14T12:54:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/3f/2d/d57d154b40b11f2cb851c4df0d4c4456bacd9b1ccc4ecb593ddec56c1a8b/librt-0.7.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c1ba843ae20db09b9d5c80475376168feb2640ce91cd9906414f23cc267a1ff", size = 188034, upload-time = "2026-01-14T12:55:00.141Z" }, + { url = "https://files.pythonhosted.org/packages/59/f9/36c4dad00925c16cd69d744b87f7001792691857d3b79187e7a673e812fb/librt-0.7.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b5b007bb22ea4b255d3ee39dfd06d12534de2fcc3438567d9f48cdaf67ae1ae3", size = 186295, upload-time = "2026-01-14T12:55:01.303Z" }, + { url = "https://files.pythonhosted.org/packages/23/9b/8a9889d3df5efb67695a67785028ccd58e661c3018237b73ad081691d0cb/librt-0.7.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbd79caaf77a3f590cbe32dc2447f718772d6eea59656a7dcb9311161b10fa75", size = 181470, upload-time = "2026-01-14T12:55:02.492Z" }, + { url = "https://files.pythonhosted.org/packages/43/64/54d6ef11afca01fef8af78c230726a9394759f2addfbf7afc5e3cc032a45/librt-0.7.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:87808a8d1e0bd62a01cafc41f0fd6818b5a5d0ca0d8a55326a81643cdda8f873", size = 201713, upload-time = "2026-01-14T12:55:03.919Z" }, + { url = "https://files.pythonhosted.org/packages/2d/29/73e7ed2991330b28919387656f54109139b49e19cd72902f466bd44415fd/librt-0.7.8-cp311-cp311-win32.whl", hash = "sha256:31724b93baa91512bd0a376e7cf0b59d8b631ee17923b1218a65456fa9bda2e7", size = 43803, upload-time = "2026-01-14T12:55:04.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/de/66766ff48ed02b4d78deea30392ae200bcbd99ae61ba2418b49fd50a4831/librt-0.7.8-cp311-cp311-win_amd64.whl", hash = "sha256:978e8b5f13e52cf23a9e80f3286d7546baa70bc4ef35b51d97a709d0b28e537c", size = 50080, upload-time = "2026-01-14T12:55:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e3/33450438ff3a8c581d4ed7f798a70b07c3206d298cf0b87d3806e72e3ed8/librt-0.7.8-cp311-cp311-win_arm64.whl", hash = "sha256:20e3946863d872f7cabf7f77c6c9d370b8b3d74333d3a32471c50d3a86c0a232", size = 43383, upload-time = "2026-01-14T12:55:07.49Z" }, + { url = "https://files.pythonhosted.org/packages/56/04/79d8fcb43cae376c7adbab7b2b9f65e48432c9eced62ac96703bcc16e09b/librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63", size = 57472, upload-time = "2026-01-14T12:55:08.528Z" }, + { url = "https://files.pythonhosted.org/packages/b4/ba/60b96e93043d3d659da91752689023a73981336446ae82078cddf706249e/librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93", size = 58986, upload-time = "2026-01-14T12:55:09.466Z" }, + { url = "https://files.pythonhosted.org/packages/7c/26/5215e4cdcc26e7be7eee21955a7e13cbf1f6d7d7311461a6014544596fac/librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592", size = 168422, upload-time = "2026-01-14T12:55:10.499Z" }, + { url = "https://files.pythonhosted.org/packages/0f/84/e8d1bc86fa0159bfc24f3d798d92cafd3897e84c7fea7fe61b3220915d76/librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850", size = 177478, upload-time = "2026-01-14T12:55:11.577Z" }, + { url = "https://files.pythonhosted.org/packages/57/11/d0268c4b94717a18aa91df1100e767b010f87b7ae444dafaa5a2d80f33a6/librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62", size = 192439, upload-time = "2026-01-14T12:55:12.7Z" }, + { url = "https://files.pythonhosted.org/packages/8d/56/1e8e833b95fe684f80f8894ae4d8b7d36acc9203e60478fcae599120a975/librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b", size = 191483, upload-time = "2026-01-14T12:55:13.838Z" }, + { url = "https://files.pythonhosted.org/packages/17/48/f11cf28a2cb6c31f282009e2208312aa84a5ee2732859f7856ee306176d5/librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714", size = 185376, upload-time = "2026-01-14T12:55:15.017Z" }, + { url = "https://files.pythonhosted.org/packages/b8/6a/d7c116c6da561b9155b184354a60a3d5cdbf08fc7f3678d09c95679d13d9/librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449", size = 206234, upload-time = "2026-01-14T12:55:16.571Z" }, + { url = "https://files.pythonhosted.org/packages/61/de/1975200bb0285fc921c5981d9978ce6ce11ae6d797df815add94a5a848a3/librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac", size = 44057, upload-time = "2026-01-14T12:55:18.077Z" }, + { url = "https://files.pythonhosted.org/packages/8e/cd/724f2d0b3461426730d4877754b65d39f06a41ac9d0a92d5c6840f72b9ae/librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708", size = 50293, upload-time = "2026-01-14T12:55:19.179Z" }, + { url = "https://files.pythonhosted.org/packages/bd/cf/7e899acd9ee5727ad8160fdcc9994954e79fab371c66535c60e13b968ffc/librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0", size = 43574, upload-time = "2026-01-14T12:55:20.185Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fe/b1f9de2829cf7fc7649c1dcd202cfd873837c5cc2fc9e526b0e7f716c3d2/librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc", size = 57500, upload-time = "2026-01-14T12:55:21.219Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d4/4a60fbe2e53b825f5d9a77325071d61cd8af8506255067bf0c8527530745/librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2", size = 59019, upload-time = "2026-01-14T12:55:22.256Z" }, + { url = "https://files.pythonhosted.org/packages/6a/37/61ff80341ba5159afa524445f2d984c30e2821f31f7c73cf166dcafa5564/librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3", size = 169015, upload-time = "2026-01-14T12:55:23.24Z" }, + { url = "https://files.pythonhosted.org/packages/1c/86/13d4f2d6a93f181ebf2fc953868826653ede494559da8268023fe567fca3/librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6", size = 178161, upload-time = "2026-01-14T12:55:24.826Z" }, + { url = "https://files.pythonhosted.org/packages/88/26/e24ef01305954fc4d771f1f09f3dd682f9eb610e1bec188ffb719374d26e/librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d", size = 193015, upload-time = "2026-01-14T12:55:26.04Z" }, + { url = "https://files.pythonhosted.org/packages/88/a0/92b6bd060e720d7a31ed474d046a69bd55334ec05e9c446d228c4b806ae3/librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e", size = 192038, upload-time = "2026-01-14T12:55:27.208Z" }, + { url = "https://files.pythonhosted.org/packages/06/bb/6f4c650253704279c3a214dad188101d1b5ea23be0606628bc6739456624/librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca", size = 186006, upload-time = "2026-01-14T12:55:28.594Z" }, + { url = "https://files.pythonhosted.org/packages/dc/00/1c409618248d43240cadf45f3efb866837fa77e9a12a71481912135eb481/librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93", size = 206888, upload-time = "2026-01-14T12:55:30.214Z" }, + { url = "https://files.pythonhosted.org/packages/d9/83/b2cfe8e76ff5c1c77f8a53da3d5de62d04b5ebf7cf913e37f8bca43b5d07/librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951", size = 44126, upload-time = "2026-01-14T12:55:31.44Z" }, + { url = "https://files.pythonhosted.org/packages/a9/0b/c59d45de56a51bd2d3a401fc63449c0ac163e4ef7f523ea8b0c0dee86ec5/librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34", size = 50262, upload-time = "2026-01-14T12:55:33.01Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b9/973455cec0a1ec592395250c474164c4a58ebf3e0651ee920fef1a2623f1/librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09", size = 43600, upload-time = "2026-01-14T12:55:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/1a/73/fa8814c6ce2d49c3827829cadaa1589b0bf4391660bd4510899393a23ebc/librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418", size = 57049, upload-time = "2026-01-14T12:55:35.056Z" }, + { url = "https://files.pythonhosted.org/packages/53/fe/f6c70956da23ea235fd2e3cc16f4f0b4ebdfd72252b02d1164dd58b4e6c3/librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611", size = 58689, upload-time = "2026-01-14T12:55:36.078Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4d/7a2481444ac5fba63050d9abe823e6bc16896f575bfc9c1e5068d516cdce/librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758", size = 166808, upload-time = "2026-01-14T12:55:37.595Z" }, + { url = "https://files.pythonhosted.org/packages/ac/3c/10901d9e18639f8953f57c8986796cfbf4c1c514844a41c9197cf87cb707/librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea", size = 175614, upload-time = "2026-01-14T12:55:38.756Z" }, + { url = "https://files.pythonhosted.org/packages/db/01/5cbdde0951a5090a80e5ba44e6357d375048123c572a23eecfb9326993a7/librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac", size = 189955, upload-time = "2026-01-14T12:55:39.939Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b4/e80528d2f4b7eaf1d437fcbd6fc6ba4cbeb3e2a0cb9ed5a79f47c7318706/librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398", size = 189370, upload-time = "2026-01-14T12:55:41.057Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ab/938368f8ce31a9787ecd4becb1e795954782e4312095daf8fd22420227c8/librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81", size = 183224, upload-time = "2026-01-14T12:55:42.328Z" }, + { url = "https://files.pythonhosted.org/packages/3c/10/559c310e7a6e4014ac44867d359ef8238465fb499e7eb31b6bfe3e3f86f5/librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83", size = 203541, upload-time = "2026-01-14T12:55:43.501Z" }, + { url = "https://files.pythonhosted.org/packages/f8/db/a0db7acdb6290c215f343835c6efda5b491bb05c3ddc675af558f50fdba3/librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d", size = 40657, upload-time = "2026-01-14T12:55:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/72/e0/4f9bdc2a98a798511e81edcd6b54fe82767a715e05d1921115ac70717f6f/librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44", size = 46835, upload-time = "2026-01-14T12:55:45.655Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3d/59c6402e3dec2719655a41ad027a7371f8e2334aa794ed11533ad5f34969/librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce", size = 39885, upload-time = "2026-01-14T12:55:47.138Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9c/2481d80950b83085fb14ba3c595db56330d21bbc7d88a19f20165f3538db/librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f", size = 59161, upload-time = "2026-01-14T12:55:48.45Z" }, + { url = "https://files.pythonhosted.org/packages/96/79/108df2cfc4e672336765d54e3ff887294c1cc36ea4335c73588875775527/librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde", size = 61008, upload-time = "2026-01-14T12:55:49.527Z" }, + { url = "https://files.pythonhosted.org/packages/46/f2/30179898f9994a5637459d6e169b6abdc982012c0a4b2d4c26f50c06f911/librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e", size = 187199, upload-time = "2026-01-14T12:55:50.587Z" }, + { url = "https://files.pythonhosted.org/packages/b4/da/f7563db55cebdc884f518ba3791ad033becc25ff68eb70902b1747dc0d70/librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b", size = 198317, upload-time = "2026-01-14T12:55:51.991Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6c/4289acf076ad371471fa86718c30ae353e690d3de6167f7db36f429272f1/librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666", size = 210334, upload-time = "2026-01-14T12:55:53.682Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7f/377521ac25b78ac0a5ff44127a0360ee6d5ddd3ce7327949876a30533daa/librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581", size = 211031, upload-time = "2026-01-14T12:55:54.827Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/e1e96c3e20b23d00cf90f4aad48f0deb4cdfec2f0ed8380d0d85acf98bbf/librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a", size = 204581, upload-time = "2026-01-14T12:55:56.811Z" }, + { url = "https://files.pythonhosted.org/packages/43/71/0f5d010e92ed9747e14bef35e91b6580533510f1e36a8a09eb79ee70b2f0/librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca", size = 224731, upload-time = "2026-01-14T12:55:58.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f0/07fb6ab5c39a4ca9af3e37554f9d42f25c464829254d72e4ebbd81da351c/librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365", size = 41173, upload-time = "2026-01-14T12:55:59.315Z" }, + { url = "https://files.pythonhosted.org/packages/24/d4/7e4be20993dc6a782639625bd2f97f3c66125c7aa80c82426956811cfccf/librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32", size = 47668, upload-time = "2026-01-14T12:56:00.261Z" }, + { url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" }, ] [[package]] @@ -768,69 +721,48 @@ wheels = [ [[package]] name = "mypy" -version = "2.3.1" +version = "1.19.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ast-serialize" }, { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions" }, { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/6a/878cc1097d4035f82bd516658d0c528d2a9955bc7b363afcbd0b07fea11b/mypy-2.3.1.tar.gz", hash = "sha256:47c1b1207258513a9d93495f69c8be9de73916186f0e52703e8c461b7a623419", size = 3992554, upload-time = "2026-08-15T03:03:38.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/b9/de8f67e12d721cdcc8ba6cfc440b989a4ba4dfabe4402ae94dfdd8bb30a4/mypy-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:57a936373fc690c43a8cd7e7e12a35148e4ec5aa7698ad7fc0a9f918bdc5be41", size = 14015541, upload-time = "2026-08-15T03:01:53.104Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8a/9e746ab012c67ed8ea3232a613716c306ee8c0b5682c80d8103b4f04568e/mypy-2.3.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d00d769056bde2f4e69c175071eba45cfb44fa1ed92bdfbfe64a93e0543b0cf0", size = 14248142, upload-time = "2026-08-15T03:02:43.201Z" }, - { url = "https://files.pythonhosted.org/packages/f7/5c/c99ff2d8d0e2c53393e32dfe22d9aa43a5d959d30db46c786dafd24527d3/mypy-2.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2166b29228835e1f88ff411e96639e6ca3c7fdde84b62ec211f70f86b4051167", size = 15193309, upload-time = "2026-08-15T03:01:28.714Z" }, - { url = "https://files.pythonhosted.org/packages/64/39/124638f745243faae1ff4b37d5426fe41c0f0454535edc82fe8102b56a3c/mypy-2.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:83d36c2924df7426333abe7faf4724a7e1aab0d9fd41625e81b4683034b80c13", size = 15498246, upload-time = "2026-08-15T03:02:46.29Z" }, - { url = "https://files.pythonhosted.org/packages/b2/83/31c0781e243836505c0fb5f4e865487d6df1023e4ad959f4ebd4b84a0226/mypy-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:f12fdb70459d0060dea40b29e52163a961b156106d68d57882a6a9f648983a53", size = 11155028, upload-time = "2026-08-15T03:01:39.08Z" }, - { url = "https://files.pythonhosted.org/packages/a0/ab/bc2eb0129e72d7d7d93d5e981a78084a9abefda7efa732a7e02f97d6e27d/mypy-2.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:e099200a1b1b1223a4951f0a90cbff1b8c91b250ba599dab1f7217a628144d90", size = 10151438, upload-time = "2026-08-15T03:02:19.04Z" }, - { url = "https://files.pythonhosted.org/packages/a4/be/c624d4241484f37dc62839e177ab607a9b8b3e96f0866544ca99e8e41d51/mypy-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94f04929f1c44c35fb0061e912087edaf504acede963a4a7d00680bd089d8531", size = 13936739, upload-time = "2026-08-15T03:03:26.475Z" }, - { url = "https://files.pythonhosted.org/packages/53/84/e3cf72f90dce5960871c82551c8fba6da05fc1018f79be41c047bd126bdd/mypy-2.3.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5d716048611e85ca9eefb2e1baa5d73ede389b5820ded260ea27c757d667af8", size = 14166460, upload-time = "2026-08-15T03:01:50.565Z" }, - { url = "https://files.pythonhosted.org/packages/4a/ff/6b97d58aa0f79a5ab9b472db1f6d6df1b11a51d74d0c08ab3760d3a613ba/mypy-2.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b091a455111214cb5c9d54a57b9618e9a49f9fe2a42e4e1ac86e9d104ed96ce8", size = 15100476, upload-time = "2026-08-15T03:03:12.079Z" }, - { url = "https://files.pythonhosted.org/packages/da/f0/cbb4b7d2ae3ac635f6b4f2d9b04070b8a92edf50da599d3b39e5ed109001/mypy-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df12e20c9efd614738c71b390007ecd0181125afc4ccafca04d78a1d2eed2c01", size = 15347826, upload-time = "2026-08-15T03:03:02.856Z" }, - { url = "https://files.pythonhosted.org/packages/5f/10/91dcdc6f8d43fc08e6a06ab1f9732f3abaaf835ac1b2e67b9dff56910855/mypy-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:52eaf3a155f35cf80b40220288c861eb45f14a2340c1f6cbfbdb0feff32879d1", size = 11142615, upload-time = "2026-08-15T03:03:36.316Z" }, - { url = "https://files.pythonhosted.org/packages/3d/8a/28d54535bf4b9aa43b2d8918c2ef660378b9f66b23d78dcee052744ae622/mypy-2.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9b4eacbee8a69836c06eff6d0dd4e134a07c2b047755b30c08625fe214f322c6", size = 10141145, upload-time = "2026-08-15T03:03:07.406Z" }, - { url = "https://files.pythonhosted.org/packages/85/da/d6effc4f808a842d91edc22535dc9e799d2ff6e91449168b7f47a0771f54/mypy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a32bbbb940af990d3be0b8af321c7b6815bb1b3b48142fe7459b9cc5f58959ff", size = 14047547, upload-time = "2026-08-15T03:02:57.707Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e6/478229701dab76f26485fc8ff5d6f241f393da22447400bbc56f6946aebe/mypy-2.3.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff715e45b2231a8e85de1d163d1b42791e4d7aab8f5145f85fee1b710b735aff", size = 14216515, upload-time = "2026-08-15T03:01:26.496Z" }, - { url = "https://files.pythonhosted.org/packages/8d/fe/7c42327a3b21e84681f691982cbfe43f334a3685f3b683b72c376476c4fa/mypy-2.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:858fc57d3d91fa728e33e7ad71def60fc6272694607b306cd3292db53ae39080", size = 15307789, upload-time = "2026-08-15T03:03:31.62Z" }, - { url = "https://files.pythonhosted.org/packages/59/f4/7e597edbe01b5a56fa958ce541302dcaabfed979966f1dffedbea0ea0fc2/mypy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:851833db876e7b650f93719c74b7879a08e338979c96054fdfc3bfd90a486355", size = 15548831, upload-time = "2026-08-15T03:03:15.55Z" }, - { url = "https://files.pythonhosted.org/packages/a3/52/cb31e084bc0314a1e384bdd677a4b80e55af04ccac077545e2238b9d320a/mypy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:4c5095a327483591c94e0c8d3ef9e50d4ab1369b541eae007c1f23bc2a41f6bb", size = 11226359, upload-time = "2026-08-15T03:03:29.002Z" }, - { url = "https://files.pythonhosted.org/packages/7a/47/88fcf6217b43fa2da81a8c2611370af18141536a4f0294bbf98b457d456d/mypy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:bbfe022634a2a195406bd469e888d2eaf193b02ba7e607391cd7640374aaae3b", size = 10214707, upload-time = "2026-08-15T03:02:48.807Z" }, - { url = "https://files.pythonhosted.org/packages/de/cf/862010ee800ca9c2bd0c4c0dacf0f092e5411824a09b8f97ad4be8fe250e/mypy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:114dff494000f18bd10d5d95d84b8567b26da60279ecbe838131841df20e635d", size = 13964542, upload-time = "2026-08-15T03:02:21.43Z" }, - { url = "https://files.pythonhosted.org/packages/75/5a/3f3a2107b41e3e92e617e25daaee121413b91e9784bea733131ed4fecc5d/mypy-2.3.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8637731bb5eee3671eb2c3200827aa3564ed8a9309ecee4d1afe77e6d031bdb", size = 14168922, upload-time = "2026-08-15T03:03:00.351Z" }, - { url = "https://files.pythonhosted.org/packages/8b/41/04dc4fe7e63d7820fa4eff272e95157d30cbea921388f3ab3fe77794cd0b/mypy-2.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c80fbc405ed8020f5ff3802dc18cf060197bcdd3fbdd6a26ef2fd34dfdd5226", size = 15244791, upload-time = "2026-08-15T03:02:31.089Z" }, - { url = "https://files.pythonhosted.org/packages/96/fc/c3053b26b9054949285aa868cb6af8c10e7591541cacd79c5dcc06a1fcf9/mypy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:84081f538ce27375045c02e3d7f81bd11d853400621ae245d87ce7b6c420ec74", size = 15501627, upload-time = "2026-08-15T03:03:34.128Z" }, - { url = "https://files.pythonhosted.org/packages/70/4e/d77daab008bbc4e5001374d7928f4a260d28f0e6747af444fc4763f7a310/mypy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:e9144ac16fde007096f9563eb2041b4433c2d705c4218edeb79e7e9d01035ee6", size = 11243961, upload-time = "2026-08-15T03:02:11.952Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f8/7eb68c136e4abd30569fe31ef2bfcb7eceae9952cab80017c04cd09f5d0c/mypy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:77ad9529e67dca28e511f5cd5671436584ce91f6d3bac159a353158187b986ac", size = 10213219, upload-time = "2026-08-15T03:02:26.361Z" }, - { url = "https://files.pythonhosted.org/packages/be/c4/42a49d44aeff804edf1b19acce0b49e8bd1a9c57dee9605dd8d980aa43d7/mypy-2.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:192abaedf75da1bc0b1cef104927e70ec49c1ef0031cc4825c7ee10a438ed24d", size = 13986778, upload-time = "2026-08-15T03:01:33.69Z" }, - { url = "https://files.pythonhosted.org/packages/45/13/9331fd2dfed7194d66c5304072894a8be3e51e9deda6863c1eceaa35a43d/mypy-2.3.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf678dffd16efcda2c15cbd30e9ecc0081388e29ea23687a88e686ed92638dc3", size = 14188467, upload-time = "2026-08-15T03:02:40.554Z" }, - { url = "https://files.pythonhosted.org/packages/78/f7/f4a34edab45667c5465855dc585a20e87978ffa8aee711445b7239d120c6/mypy-2.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e036f06b41630f4c8a1d48f9ac6aa26acc65f8be089973f5519da643318f03f", size = 15225538, upload-time = "2026-08-15T03:03:09.761Z" }, - { url = "https://files.pythonhosted.org/packages/40/05/534b3590757bd05794f73e07f6666c2a77b8597ffed795c94ce570096aa0/mypy-2.3.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:71af9c8a894e862b58e92abb08e53b05a384a1e5e5d6dc7cda59126211a53d82", size = 15480805, upload-time = "2026-08-15T03:01:41.134Z" }, - { url = "https://files.pythonhosted.org/packages/55/da/bdfba852e2562f599624af5bb7d29e36b0b4f526f2b8bac85efe0dd1803d/mypy-2.3.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:3c80cd23d85368bdd9f37d5231dfd97d35bcbf5bf41af96ef3a9b078ad1957f9", size = 7761712, upload-time = "2026-08-15T03:02:36.008Z" }, - { url = "https://files.pythonhosted.org/packages/98/31/60fc64a74cdba4f2a5d642d32317993e479163e1ac7d91b695e5d15e2264/mypy-2.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:4956f34d145e145562a0a0bf367f642bbc85c04ec2baf47ae015947c3169a85d", size = 11423968, upload-time = "2026-08-15T03:02:06.931Z" }, - { url = "https://files.pythonhosted.org/packages/a9/23/eb5950b24cd26ba3b78f87707a275568d633c77dae8e61c9661be6055ca6/mypy-2.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:cfb12e360242d23d91f5e978d94f58ea66acf5804c4fb6f2f794a20d4cb1b595", size = 10399323, upload-time = "2026-08-15T03:02:33.671Z" }, - { url = "https://files.pythonhosted.org/packages/82/c7/f80f4e46c0b9a00eb5f78a79d49dda8bdf56a5230f7257fb33e76be04da7/mypy-2.3.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5f1c50bb05b64e2026b52867e8d21106f01313c744a2c4ecc34c90d12e8d6e2", size = 15121308, upload-time = "2026-08-15T03:01:46.053Z" }, - { url = "https://files.pythonhosted.org/packages/5d/74/9b04f17c7074cc5188f02fb63a2ca1d43fedf479e84fe3091c39061a1d7f/mypy-2.3.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:667196b352f4cf304ded4c10f90cfc179263a1acfb3cdcfa984bdfd340d498bc", size = 15536590, upload-time = "2026-08-15T03:01:35.941Z" }, - { url = "https://files.pythonhosted.org/packages/26/04/c837ef6208e567774e2ed1f863f8ba6ec4817b1b6dd426315e5d559b6ec9/mypy-2.3.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9c53e395c12cad2c6d4b67d5da7c6057638a132d85c08b73646b18f802a0045", size = 16791074, upload-time = "2026-08-15T03:01:31.073Z" }, - { url = "https://files.pythonhosted.org/packages/37/68/48730230afa45192d5bd429a6a2ff24a6f8dedda90fdf2b221792b54518f/mypy-2.3.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:18162b128c3f9c703cd35f5537446900b0d21a2549aa7a95d21380d2ef643fb0", size = 17069183, upload-time = "2026-08-15T03:02:28.566Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ea/ca23fc9c20eeda09a15c9cbcf50015d0e73f409f6ead059e42aa69a608ff/mypy-2.3.1-cp314-cp314t-win_amd64.whl", hash = "sha256:30c0477d4aab7b7f39c8397dc877f2c96b9fe5588ec379f372c56eb63d599f63", size = 12154679, upload-time = "2026-08-15T03:02:04.809Z" }, - { url = "https://files.pythonhosted.org/packages/3b/67/8d982126034990869466f73b8db80dcb2234a7ac39b4dad093e047a79835/mypy-2.3.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6941ab3619377bc3f32ca02876b07d27f216f5201604b664d3937ea0fdd23bb4", size = 10969159, upload-time = "2026-08-15T03:02:38.152Z" }, - { url = "https://files.pythonhosted.org/packages/ee/f7/41e7f2d8117fbc7a7587286162ffe2f688984b69c46ed63cf5f2e4fc3bae/mypy-2.3.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:6f041a6de52c9217ca125e78ba0a335cb7fd98a1c0580978e49ab2b126f70b57", size = 13990694, upload-time = "2026-08-15T03:03:21.919Z" }, - { url = "https://files.pythonhosted.org/packages/06/85/8f665811a0c8f3bf6fa1d9acd665ec2d97a2bcc453ae68dcd92340941cd6/mypy-2.3.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5159ae60f5dbc3a498af5ba8365505808ac8031bc63f9e00304ad545d40bdd9b", size = 14203518, upload-time = "2026-08-15T03:01:48.455Z" }, - { url = "https://files.pythonhosted.org/packages/2d/82/91b866c8546b120bff83b73a439d90d2d63ef3aff113599e6b8e4d566848/mypy-2.3.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47a8a7a0a7f6f6e63995c0ac36fa0c07b127413fdc81f0439b7f3dccafd33561", size = 15220224, upload-time = "2026-08-15T03:01:23.577Z" }, - { url = "https://files.pythonhosted.org/packages/c8/78/c226c99208ee40de7c768369fa533f933afa003dfdc606ff021450724e91/mypy-2.3.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2329c0501293d4e1f33bc15d04d6304d65a1cdda967ee93a05c1e681a3923133", size = 15501512, upload-time = "2026-08-15T03:02:09.453Z" }, - { url = "https://files.pythonhosted.org/packages/a9/e7/7cfb3f106c393979f4cc37ad6c0586044d50401e3c35b0c003e4f3ba6bc9/mypy-2.3.1-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:bb26deed807bdb0457cf3e3f1cd7c4a1cf9d66864eaf1b4a61e06805d4c6b1f9", size = 7761913, upload-time = "2026-08-15T03:01:55.65Z" }, - { url = "https://files.pythonhosted.org/packages/99/3c/52affefa273b97939a1f474ae4a349c8718635c15b941112dfab4291b0c1/mypy-2.3.1-cp315-cp315-win_amd64.whl", hash = "sha256:375d7013876a8233b2d05be185bfa09f689696cd999ce8b1cfe6acac5c80e8a3", size = 11422533, upload-time = "2026-08-15T03:03:24.101Z" }, - { url = "https://files.pythonhosted.org/packages/2a/b7/75643e70c72a5b346d8a9b1543c967ea8824df2ee3fb7ccba652c272b7bb/mypy-2.3.1-cp315-cp315-win_arm64.whl", hash = "sha256:586b3612214cceabb3c0f588c97e7d1e535393f06a60e912e994f6b3ace97523", size = 10397931, upload-time = "2026-08-15T03:02:55.265Z" }, - { url = "https://files.pythonhosted.org/packages/10/ce/53be21f2d4adfcd26f63f1184a13ed797015ab463853f117e2e11e4d726f/mypy-2.3.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:ef0c6335cda9d807f8193d8ff6204a72bc909fa9882aacbca14f43cdb7188306", size = 15118669, upload-time = "2026-08-15T03:02:51.479Z" }, - { url = "https://files.pythonhosted.org/packages/62/43/20de757cd42989d291a17fad607742c4c74e875ce5cea00e5a5225020ac1/mypy-2.3.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e598c8c66401d26b150872154a286e6d484cf2789c3bb28a7556806298423021", size = 15545627, upload-time = "2026-08-15T03:03:05.132Z" }, - { url = "https://files.pythonhosted.org/packages/7e/fc/092bdf77ad280eaf501422f0f3b966012b528076cc13e41a774861c907d1/mypy-2.3.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eda22fd4efa9dcd39331d1dede9b5b8b8a7fd69af07592e778433da98610d29e", size = 16764157, upload-time = "2026-08-15T03:02:23.958Z" }, - { url = "https://files.pythonhosted.org/packages/94/5c/c94c4d62d909b07f552d0d9356d7acc943825558e602a64822ffa2231536/mypy-2.3.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:2a0ba2e57847849fb0d1fcdabb32786d223095ed8bc121dfe322bcdb3d9c46bc", size = 17073258, upload-time = "2026-08-15T03:02:14.573Z" }, - { url = "https://files.pythonhosted.org/packages/c0/f7/511a88b89e478053c02d22039bb8f3ce4183efe8fd7a4f0a5910a8bb0a32/mypy-2.3.1-cp315-cp315t-win_amd64.whl", hash = "sha256:3f7e865dd51f235f60a2dbcd8728a1c095f5ca28f095d48a725b84cd935735c4", size = 12135505, upload-time = "2026-08-15T03:02:16.714Z" }, - { url = "https://files.pythonhosted.org/packages/71/bf/02573b56964ecb0f7c644f915f53c325ae15c3faec521c5adf11599a32df/mypy-2.3.1-cp315-cp315t-win_arm64.whl", hash = "sha256:8ad80807dc3ab8ea978b1b2b6e4a657194ace1d4ef03e0e731aff1abd517da29", size = 10962647, upload-time = "2026-08-15T03:01:43.712Z" }, - { url = "https://files.pythonhosted.org/packages/8e/41/9675c7a1e78edecfba0b79e587a52594c56e189368261dc7b3a7fffb9527/mypy-2.3.1-py3-none-any.whl", hash = "sha256:6ed5c7e3419083268e5c9258bd1c1ef91af44a9e89374dbcaf37b775716e72eb", size = 2754338, upload-time = "2026-08-15T03:02:53.4Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, + { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, + { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, + { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, ] [[package]] @@ -913,7 +845,7 @@ wheels = [ [[package]] name = "pre-commit" -version = "4.6.2" +version = "4.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -922,9 +854,9 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/89/1f3e8e1fc3e97de0fa963495832f581f025f29471602a309e48808244292/pre_commit-4.6.2.tar.gz", hash = "sha256:8f5d7bfb021ecdbcd9d49d89847082dd24172ccde534390081a679ad046e2441", size = 198670, upload-time = "2026-08-10T22:07:18.421Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/e2/bbb7129c9e7999a6b8ee9cca3b66486c25c423ab5a75f34071798b74ce94/pre_commit-4.6.2-py2.py3-none-any.whl", hash = "sha256:e2dde9a75d3bce11bd3831c26d134df00a2803c1d818be6a0383c3dcda25dc4e", size = 226202, upload-time = "2026-08-10T22:07:16.942Z" }, + { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, ] [[package]] @@ -1071,11 +1003,11 @@ wheels = [ [[package]] name = "pygments" -version = "2.20.0" +version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] @@ -1089,7 +1021,7 @@ wheels = [ [[package]] name = "pytest" -version = "9.1.1" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1100,9 +1032,9 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] @@ -1159,7 +1091,7 @@ wheels = [ [[package]] name = "python-semantic-release" -version = "10.6.2" +version = "10.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -1167,6 +1099,7 @@ dependencies = [ { name = "deprecated" }, { name = "dotty-dict" }, { name = "gitpython" }, + { name = "importlib-resources" }, { name = "jinja2" }, { name = "pydantic" }, { name = "python-gitlab" }, @@ -1175,9 +1108,9 @@ dependencies = [ { name = "shellingham" }, { name = "tomlkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/b5/5d45f47204604ee0f4522070e06ead23d5d343d6932fcb194f9e4dee4885/python_semantic_release-10.6.2.tar.gz", hash = "sha256:ae35cde167c407deab88759da942712a32e6abf1759273740581a4d1d6601a7b", size = 628792, upload-time = "2026-08-28T20:23:03.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f6/06d5aa54b46bb192b00ef3e74234300ea5932dd310d142a3c8070e770e93/python_semantic_release-10.6.1.tar.gz", hash = "sha256:ee6369238f72e75a009b3724481232c8b813416191be099bc0266e375fd02b2b", size = 626453, upload-time = "2026-07-06T06:14:35.507Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/3b/228224f38e672d945de79cc5756fe2be57129660f6e755768f0f2560f8d9/python_semantic_release-10.6.2-py3-none-any.whl", hash = "sha256:4f430cf5efe9ac0587c6bd7b8b2bb689baf6cb92da8bb50827a1923163371a12", size = 156461, upload-time = "2026-08-28T20:23:01.954Z" }, + { url = "https://files.pythonhosted.org/packages/18/97/be812cd1eb350551d2f3cc38426864cce493a03ed31f4749133bcff8d053/python_semantic_release-10.6.1-py3-none-any.whl", hash = "sha256:36f7319515f218719d0972bc9535813a930f04cd19556767599e9863242efcdc", size = 155674, upload-time = "2026-07-06T06:14:33.575Z" }, ] [[package]] @@ -1246,7 +1179,7 @@ wheels = [ [[package]] name = "requests" -version = "2.33.0" +version = "2.32.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1254,9 +1187,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] [[package]] @@ -1273,15 +1206,15 @@ wheels = [ [[package]] name = "rich" -version = "15.0.0" +version = "14.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, + { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, ] [[package]] @@ -1298,7 +1231,7 @@ name = "roman-numerals-py" version = "4.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "roman-numerals" }, + { name = "roman-numerals", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/de96fca640f4f656eb79bbee0e79aeec52e3e0e359f8a3e6a0d366378b64/roman_numerals_py-4.1.0.tar.gz", hash = "sha256:f5d7b2b4ca52dd855ef7ab8eb3590f428c0b1ea480736ce32b01fef2a5f8daf9", size = 4274, upload-time = "2025-12-17T18:25:41.153Z" } wheels = [ @@ -1307,32 +1240,32 @@ wheels = [ [[package]] name = "ruff" -version = "0.16.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a4/7c/6adb35d70e7c027e308274557901c7e00fb3407750faf3620c184ae058cb/ruff-0.16.6.tar.gz", hash = "sha256:dcf8a73d2ff77e99dde91244b4da16feba7f14e6beeb4015dee7c5a909e99050", size = 4921251, upload-time = "2026-09-03T16:57:29.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/28/9cc1b79639e284ec103f43c88c644db4eb58cbd0ea1ca11f1193435369ac/ruff-0.16.6-py3-none-linux_armv6l.whl", hash = "sha256:61c368c26bf8e973e5ab14a2772de587bc068ea3f9a277f673380749b4898fb8", size = 10015638, upload-time = "2026-09-03T16:56:40.986Z" }, - { url = "https://files.pythonhosted.org/packages/71/11/627d342ef727ea7794edf74fe23d60a074b02c3acc2e9436684e782286ca/ruff-0.16.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ecf4f068e2e123e43a26e9db4e19524cc56563912404e83bbfca375757e45a32", size = 10220762, upload-time = "2026-09-03T16:56:44.681Z" }, - { url = "https://files.pythonhosted.org/packages/43/d9/b75668ce41e4c8d073d18d6d08672ba6906ce45d5c06ea4fdb2e84ce3853/ruff-0.16.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:99b62ea33baf130f50368798d841f0d95527b6d817bf31817b65dd058f1d314c", size = 9835082, upload-time = "2026-09-03T16:56:47.142Z" }, - { url = "https://files.pythonhosted.org/packages/99/97/123ab10b05cde889c107c20f5a9774955104b5552796a2a8584b089ae8eb/ruff-0.16.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fbf89013f2bb3f6835a6038ff658dc8a1b38c98dc8e724b964168ad4e881876", size = 9949304, upload-time = "2026-09-03T16:56:49.813Z" }, - { url = "https://files.pythonhosted.org/packages/3e/58/a4a2c59dd2e5b85929c912d9cac3056eb9ee8c7e75e9b9fe3e109174966b/ruff-0.16.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56a67065e22efa6bc4d498299d3bb06c0c90aace8fac2068b5a12f9dc4d8d51d", size = 9840612, upload-time = "2026-09-03T16:56:52.368Z" }, - { url = "https://files.pythonhosted.org/packages/61/6a/ff8c8626a786c4f49d48ced4a752dadbca65f5263005f9c2416578194694/ruff-0.16.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e25cc89174874b176a157e4428d66761c2c0c006654419bf384f967f361ff1b1", size = 10543465, upload-time = "2026-09-03T16:56:55.089Z" }, - { url = "https://files.pythonhosted.org/packages/ad/bb/c47535923365f337b82e28192e4e9eef2176511007cfd99a62fc22df5dad/ruff-0.16.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700580ed5303723cb3c11c2f1d2a8913ce77b7ea86646dddb887f5417a9ba70", size = 11267576, upload-time = "2026-09-03T16:56:57.791Z" }, - { url = "https://files.pythonhosted.org/packages/ba/50/e5119a5212b5cd63b51e1f4b25e7bd636a6668fc069a3160b108ad7e3c16/ruff-0.16.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15f1d0b6e165a6e56567befb6629f8209271311d990bae0f37e6d065035ef5f3", size = 10781993, upload-time = "2026-09-03T16:57:00.666Z" }, - { url = "https://files.pythonhosted.org/packages/8b/98/083d8b4ef3c51a0d19db84367791cbe9f44e4b53343d19dfa83556e1cd9a/ruff-0.16.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d72c591a96986ee4268860e2b7235082129ca5e4cb9cbba653a4b57c11893757", size = 10317748, upload-time = "2026-09-03T16:57:03.428Z" }, - { url = "https://files.pythonhosted.org/packages/9a/29/68f7ff2c5ad95f19f00627ac2de95644e25fe47371ea60b2db1fd952315e/ruff-0.16.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:65a006baa18f33324325814c864daef03541d51564b98c517610ea756ab7003e", size = 10540096, upload-time = "2026-09-03T16:57:06.182Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f9/79a8f6de85968641d68a7863aeec577551924ef066a990a48ff93167beab/ruff-0.16.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:cd02a7bf1a21a8735228a3e8c95a9dc5cf86bd2a52194f4aaae2a5755b4de0f4", size = 10100494, upload-time = "2026-09-03T16:57:09.194Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e8/b81a22d9b90c00b892ccf2fa2ac36fa95de4c13ab85aea3e73795cfe4651/ruff-0.16.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:31b36f1e5ad85e0737f09d2be4e512e2e283583c14015da3b9dc07359ac0fc88", size = 9843663, upload-time = "2026-09-03T16:57:12.168Z" }, - { url = "https://files.pythonhosted.org/packages/39/aa/54f516ec5e5a11c4afdceb1c454ebb054ffb96e4f4a1705580b4346abd35/ruff-0.16.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:61029b4ab4aa723fd3064fab96b1d814492596bf0c792679fffcbde1e1679953", size = 10282461, upload-time = "2026-09-03T16:57:15.077Z" }, - { url = "https://files.pythonhosted.org/packages/52/0b/38d0aa8aa32372b96dc44f97b22e576c4147808271aab7b2cb1e353d4445/ruff-0.16.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9ac8998457832c2061709d900856b7ad271dace0cb41f346588d540162bfa718", size = 10728808, upload-time = "2026-09-03T16:57:17.797Z" }, - { url = "https://files.pythonhosted.org/packages/5e/e5/9e274e24eeb027640ffc7442f21239f16d17f47acec15ae34f32e03a5c79/ruff-0.16.6-py3-none-win32.whl", hash = "sha256:0b87d9d16fcb63e8018423ca1d50b7260f15cb2da33e30db4baad4183a948c25", size = 10049212, upload-time = "2026-09-03T16:57:20.55Z" }, - { url = "https://files.pythonhosted.org/packages/22/31/72472449414223ed1a2da236b992adbb1a2ae59e34794574810f60ce068e/ruff-0.16.6-py3-none-win_amd64.whl", hash = "sha256:10d21c51c3495d8eaea7b703a16592117ea6eb1d649e36335aa965ff1173eb39", size = 10556402, upload-time = "2026-09-03T16:57:23.501Z" }, - { url = "https://files.pythonhosted.org/packages/fc/07/d781f8f8e1ac24bef9f3269cf62ffb1407ca24c3a8f12e5e22874f90528c/ruff-0.16.6-py3-none-win_arm64.whl", hash = "sha256:7a976c79b958f94e50a022a19f0f8c87387448020935ec14fc74331bd0a7f2c5", size = 10412850, upload-time = "2026-09-03T16:57:26.416Z" }, +version = "0.15.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/06/ae069393fc66e8ff33036d4b368003833bf6e88ccf182e17e7a2f1c754fd/ruff-0.15.22.tar.gz", hash = "sha256:3f15175b1fb580126f58285a5dae6b2ea89000136d980c64499211f116b54809", size = 4785063, upload-time = "2026-07-16T15:14:13.244Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/18/ee54b7ae1e121be7a28ea6da4b67564ebb0530e183a54415ab7e3bcd2c4e/ruff-0.15.22-py3-none-linux_armv6l.whl", hash = "sha256:44423e73493737f5e7c5b41d475483898ff37afcdae38bc3da5085e29af1c2d8", size = 10781258, upload-time = "2026-07-16T15:13:19.452Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d2/2520cb14761ddbeaf57642a76942fc36adcbdbe53b4532241995f6fc485c/ruff-0.15.22-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b82c6482946e9eda7ff2e091d25b8bad3f718684e1916d41bd56873cee05b697", size = 10999477, upload-time = "2026-07-16T15:13:23.318Z" }, + { url = "https://files.pythonhosted.org/packages/c9/10/74e53572aa758dfaa678c2a2646b5c5515d884b7ca56be4d2ce03ca4b560/ruff-0.15.22-py3-none-macosx_11_0_arm64.whl", hash = "sha256:11c1c715af53a09f714e011106bffc419751ec8232fcb5da42173284ea3fec6f", size = 10466716, upload-time = "2026-07-16T15:13:26.162Z" }, + { url = "https://files.pythonhosted.org/packages/1e/cc/44eaaf0844e028182f2d0a8f2190d0f359159aed0a9e5ab861d892f1ae2a/ruff-0.15.22-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742a29cf29bddb7c8327895d6a10e0e6c5b38a96dd407af9b5d0857f809c0576", size = 10892644, upload-time = "2026-07-16T15:13:29.229Z" }, + { url = "https://files.pythonhosted.org/packages/9f/21/8edf559014d2b0f82beea19cfb713993ad802ccda16868769979c6090a84/ruff-0.15.22-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72af58b951b0ae395935ae79763dc349bc0eb706319d28f7a33ad2cfb3cfc178", size = 10576719, upload-time = "2026-07-16T15:13:32.35Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1e/3a13abd392a3b50b62e5938a831f9ab6e588358cacad5c18545b716d2182/ruff-0.15.22-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d425005c1835eb24e2ee4161cb90e8db263415f4a71c8c72c33abaa6c0c224", size = 11376494, upload-time = "2026-07-16T15:13:35.958Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3e/422d3d95bcf04dd78e1aeac22184d4f9a8fb2c01865d39d44618484a0317/ruff-0.15.22-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b9b3f8779a4f08c969defc3c8c35abffaa757e601ed5ae66d6d1db6519969a", size = 12208370, upload-time = "2026-07-16T15:13:39.185Z" }, + { url = "https://files.pythonhosted.org/packages/1e/91/5d065a0e0a02bf4813f5119ad278462eed081d2b832eb7c021ade0ec9e65/ruff-0.15.22-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e0dd1b2e4d3d585f897a0d137cbf4eaf6223bef4e8ce34d6bb12556c5f9249e", size = 11581098, upload-time = "2026-07-16T15:13:42.132Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f9/a0d4871d12fae702eb1f41b686caf05f1f8b124dc6db6f784f53d74918fa/ruff-0.15.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365523eb91d9224e1bcb03b022fbf0facb8f9e23792a2c53d9d4b3924bdbdebb", size = 11399422, upload-time = "2026-07-16T15:13:45.2Z" }, + { url = "https://files.pythonhosted.org/packages/18/80/c843a5176cddbceb0b7e8dd41cf9993490796c1c469348d384f5a5c13c56/ruff-0.15.22-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fabfd168afdf29fee5be98b831efa9683c94d7c5a3b58b9ce5a2e38444589a74", size = 11381683, upload-time = "2026-07-16T15:13:48.46Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/8485de0ae92239438a36cfc51350db9b9e85c9ebdfaea91b18e422706662/ruff-0.15.22-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:225dbf095a87f1d9f90f5fd7924d2613ee452a75a4308c63a8f50f761787aa7c", size = 10850295, upload-time = "2026-07-16T15:13:51.655Z" }, + { url = "https://files.pythonhosted.org/packages/fa/91/24977ec2ec72eaf15e4394ace2959fdff2dd1e14f03e005e838023407169/ruff-0.15.22-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1877d63b9d24ed278744f1523fd11b85540566d54641f97c566d7d9dc5ca5296", size = 10579640, upload-time = "2026-07-16T15:13:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/9c/47/9b51216951974df1f263ac19da550d34252e0ed7218c25f10c5ef9ed7517/ruff-0.15.22-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a1606c510bd7215680d32efab38965f7cdec3ef69f5170a3f4791404ffdd5262", size = 11105077, upload-time = "2026-07-16T15:13:57.915Z" }, + { url = "https://files.pythonhosted.org/packages/c2/47/20e9d4a3b8016778acea5fc32bb50d35d207500a17ddb529ffa6996feef8/ruff-0.15.22-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:630479b18625f5ffc373f77603a22a9f8ac0acd7ff0501178b5db28ec71e9c64", size = 11490980, upload-time = "2026-07-16T15:14:01.032Z" }, + { url = "https://files.pythonhosted.org/packages/4d/76/3f72d8fc38c1cb77b38c56a70da9d0c17700cc1cc50f9649c9d3c8f5ba71/ruff-0.15.22-py3-none-win32.whl", hash = "sha256:e5ba0e4a13fd14abbed2a77b517a3911290c6c6c59ef67784328d1668fab76cf", size = 10789165, upload-time = "2026-07-16T15:14:04.16Z" }, + { url = "https://files.pythonhosted.org/packages/cb/46/4965251734c2b6fcdca1b1b187d20bcac3af0ee5b083b89c910bb961ce3a/ruff-0.15.22-py3-none-win_amd64.whl", hash = "sha256:9be63ba1eb936acd2d1342fb8337c356353706fce233b2a15a09a97037e6acde", size = 11938297, upload-time = "2026-07-16T15:14:07.316Z" }, + { url = "https://files.pythonhosted.org/packages/57/c9/e69b1ff4c8b69093ef08b8919ab767af0569666865b39c30a8795d88d3c6/ruff-0.15.22-py3-none-win_arm64.whl", hash = "sha256:e1168075b72158510839f250027659cdd78476f40507dd517892304c41318661", size = 11298172, upload-time = "2026-07-16T15:14:10.51Z" }, ] [[package]] name = "selenium" -version = "4.48.0" +version = "4.46.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1342,18 +1275,18 @@ dependencies = [ { name = "urllib3", extra = ["socks"] }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7f/8c/db97bdc1a8b41e7b6bf9d3099722ed8e7ac61328af8637f20004015c642b/selenium-4.48.0.tar.gz", hash = "sha256:045c1ec054c94e3be6c10febc509aa513b4c05e9146d1a9cf3de5375ec6ca2a1", size = 1055269, upload-time = "2026-08-27T20:05:51.235Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/53/053df98ef0c38535a74ea732217eb2dbfddc7ff5442a47a4b315ba577f85/selenium-4.46.0.tar.gz", hash = "sha256:54f7e1a4df5f7508ac8c38ce2ea584db1b27083dd79962b22524219219df5cbe", size = 1006000, upload-time = "2026-07-11T00:38:41.11Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/ee/5d1b0e9cb43965902f0a9f7add527134db71cabf2f26766ae2fdb7774b9a/selenium-4.48.0-py3-none-any.whl", hash = "sha256:b2a1d77019db92513e59aa2376710fe3d42b65a4d493dccd0c799c1a0d574d93", size = 9561411, upload-time = "2026-08-27T20:05:48.778Z" }, + { url = "https://files.pythonhosted.org/packages/30/f4/365c723a37c7e9ba6a6a19bf0c9e407137703f56a72c6201d0e4cb1432cb/selenium-4.46.0-py3-none-any.whl", hash = "sha256:f03e864770a21ab32009961c9d015cb2c6275eba45c926c272e02d90af423aad", size = 9428587, upload-time = "2026-07-11T00:38:38.84Z" }, ] [[package]] name = "setuptools" -version = "83.0.0" +version = "80.10.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/ff/f75651350db3cf2ef767371307eb163f3cc1ac03e16fdf3ac347607f7edb/setuptools-80.10.1.tar.gz", hash = "sha256:bf2e513eb8144c3298a3bd28ab1a5edb739131ec5c22e045ff93cd7f5319703a", size = 1229650, upload-time = "2026-01-21T09:42:03.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/e0/76/f963c61683a39084aa575f98089253e1e852a4417cb8a3a8a422923a5246/setuptools-80.10.1-py3-none-any.whl", hash = "sha256:fc30c51cbcb8199a219c12cc9c281b5925a4978d212f84229c909636d9f6984e", size = 1099859, upload-time = "2026-01-21T09:42:00.688Z" }, ] [[package]] @@ -1418,23 +1351,23 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, - { name = "tomli" }, + { name = "alabaster", marker = "python_full_version < '3.11'" }, + { name = "babel", marker = "python_full_version < '3.11'" }, + { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "imagesize", marker = "python_full_version < '3.11'" }, + { name = "jinja2", marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "requests", marker = "python_full_version < '3.11'" }, + { name = "snowballstemmer", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } wheels = [ @@ -1446,27 +1379,26 @@ name = "sphinx" version = "8.2.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.15'", - "python_full_version >= '3.11' and python_full_version < '3.15'", + "python_full_version >= '3.11'", ] dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "roman-numerals-py" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, + { name = "alabaster", marker = "python_full_version >= '3.11'" }, + { name = "babel", marker = "python_full_version >= '3.11'" }, + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version >= '3.11'" }, + { name = "imagesize", marker = "python_full_version >= '3.11'" }, + { name = "jinja2", marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "requests", marker = "python_full_version >= '3.11'" }, + { name = "roman-numerals-py", marker = "python_full_version >= '3.11'" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } wheels = [ @@ -1625,11 +1557,11 @@ wheels = [ [[package]] name = "tomlkit" -version = "0.15.1" +version = "0.13.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/96/e07752635b98536177fa1f37671c8f3cdde2e724c6bcf6034b2cfb571565/tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97", size = 180129, upload-time = "2026-07-17T01:48:04.562Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/bc/8c13eb66537dce1d2bd3a57132902f38d0e7f5bb46fa9f4daed9fe9d76ee/tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304", size = 49449, upload-time = "2026-07-17T01:48:05.728Z" }, + { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, ] [[package]] @@ -1667,11 +1599,11 @@ wheels = [ [[package]] name = "types-python-dateutil" -version = "2.9.0.20260807" +version = "2.9.0.20260124" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/4e/b3fa538f9cb38dfece0d6ccf6d3d0d925bdedb144fb9c8129dfc007cd003/types_python_dateutil-2.9.0.20260807.tar.gz", hash = "sha256:e0b8a90d464c8684c66b7b8e4556d9074afdddcc56ca45323f0987134f9e7034", size = 17618, upload-time = "2026-08-07T04:17:13.491Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/41/4f8eb1ce08688a9e3e23709ed07089ccdeaf95b93745bfb768c6da71197d/types_python_dateutil-2.9.0.20260124.tar.gz", hash = "sha256:7d2db9f860820c30e5b8152bfe78dbdf795f7d1c6176057424e8b3fdd1f581af", size = 16596, upload-time = "2026-01-24T03:18:42.975Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/5e/3715867caea2f4cea56ccb04c851cde23ed063449c3b004c7a047f20dd48/types_python_dateutil-2.9.0.20260807-py3-none-any.whl", hash = "sha256:54aa3707350ed7a9cc0776fd2f6739679d6967d11b40150985e81edcb86df4db", size = 18486, upload-time = "2026-08-07T04:17:12.504Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c2/aa5e3f4103cc8b1dcf92432415dde75d70021d634ecfd95b2e913cf43e17/types_python_dateutil-2.9.0.20260124-py3-none-any.whl", hash = "sha256:f802977ae08bf2260142e7ca1ab9d4403772a254409f7bbdf652229997124951", size = 18266, upload-time = "2026-01-24T03:18:42.155Z" }, ] [[package]] @@ -1697,11 +1629,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.7.0" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [package.optional-dependencies]