From 7a922d4fb6e5e70afbdde844827a12de4bf785bb Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Wed, 23 Oct 2024 13:20:56 +0000 Subject: [PATCH] Initial commit --- .devcontainer.json | 63 +++ .github/CODE_OF_CONDUCT.md | 74 +++ .github/CONTRIBUTING.md | 29 ++ .github/ISSUE_TEMPLATE.md | 20 + .github/PULL_REQUEST_TEMPLATE.md | 9 + .github/labels.yml | 85 ++++ .github/release-drafter.yml | 57 +++ .github/renovate.json | 32 ++ .github/workflows/ci.yaml | 86 ++++ .github/workflows/labels.yaml | 23 + .github/workflows/lock.yaml | 21 + .github/workflows/pr-labels.yaml | 27 ++ .github/workflows/release-drafter.yaml | 19 + .github/workflows/stale.yaml | 40 ++ .gitignore | 162 +++++++ .pre-commit-config.yaml | 140 ++++++ .python-version | 1 + .yamllint | 66 +++ LICENSE | 201 +++++++++ README.md | 86 ++++ pyproject.toml | 177 ++++++++ tests/__init__.py | 9 + tests/__snapshots__/test_init.ambr | 131 ++++++ tests/conftest.py | 12 + tests/fixtures/RTCConfiguration_empty.json | 1 + .../RTCConfiguration_multiple_iceServers.json | 12 + .../RTCConfiguration_one_iceServer.json | 7 + .../fixtures/RTCIceServer_only_urls_list.json | 3 + .../RTCIceServer_only_urls_string.json | 3 + tests/fixtures/RTCIceServer_urls_list.json | 5 + tests/fixtures/RTCIceServer_urls_string.json | 5 + tests/syrupy.py | 66 +++ tests/test_init.py | 48 ++ uv.lock | 421 ++++++++++++++++++ webrtc_models/__init__.py | 42 ++ 35 files changed, 2183 insertions(+) create mode 100644 .devcontainer.json create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/labels.yml create mode 100644 .github/release-drafter.yml create mode 100644 .github/renovate.json create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/labels.yaml create mode 100644 .github/workflows/lock.yaml create mode 100644 .github/workflows/pr-labels.yaml create mode 100644 .github/workflows/release-drafter.yaml create mode 100644 .github/workflows/stale.yaml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .python-version create mode 100644 .yamllint create mode 100644 LICENSE create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 tests/__init__.py create mode 100644 tests/__snapshots__/test_init.ambr create mode 100644 tests/conftest.py create mode 100644 tests/fixtures/RTCConfiguration_empty.json create mode 100644 tests/fixtures/RTCConfiguration_multiple_iceServers.json create mode 100644 tests/fixtures/RTCConfiguration_one_iceServer.json create mode 100644 tests/fixtures/RTCIceServer_only_urls_list.json create mode 100644 tests/fixtures/RTCIceServer_only_urls_string.json create mode 100644 tests/fixtures/RTCIceServer_urls_list.json create mode 100644 tests/fixtures/RTCIceServer_urls_string.json create mode 100644 tests/syrupy.py create mode 100644 tests/test_init.py create mode 100644 uv.lock create mode 100644 webrtc_models/__init__.py diff --git a/.devcontainer.json b/.devcontainer.json new file mode 100644 index 0000000..2b9960f --- /dev/null +++ b/.devcontainer.json @@ -0,0 +1,63 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.177.0/containers/python-3 +{ + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + "extensions": [ + "charliermarsh.ruff", + "eamodio.gitlens", + "github.vscode-pull-request-github", + "ms-python.python", + "ms-python.vscode-pylance", + "ms-python.mypy-type-checker", + "ms-vscode.makefile-tools", + "ryanluker.vscode-coverage-gutters", + "visualstudioexptteam.vscodeintellicode" + ], + // Set *default* container specific settings.json values on container create. + "settings": { + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff" + }, + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "editor.tabSize": 4, + "files.eol": "\n", + "files.trimTrailingWhitespace": true, + "python.analysis.autoImportCompletions": true, + "python.analysis.typeCheckingMode": "basic", + "python.defaultInterpreterPath": "/home/vscode/.local/venv/bin/python", + "python.languageServer": "Pylance", + "python.pythonPath": "/home/vscode/.local/venv/bin/python", + "python.terminal.activateEnvInCurrentTerminal": true, + "python.testing.pytestArgs": ["--no-cov"], + "python.testing.pytestEnabled": true, + "python.testing.unittestEnabled": false, + "terminal.integrated.defaultProfile.linux": "zsh", + "terminal.integrated.profiles.linux": { + "zsh": { + "path": "/usr/bin/zsh" + } + } + } + } + }, + // Add the IDs of extensions you want installed when the container is created. + "features": {}, + "image": "mcr.microsoft.com/devcontainers/base:debian", + "name": "webrtc-models", + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "curl -LsSf https://astral.sh/uv/install.sh | sh && uv sync --frozen --dev && pre-commit install", + "postStartCommand": "uv sync --dev", + "remoteEnv": { + "UV_PROJECT_ENVIRONMENT": "/home/vscode/.local/venv", + "PATH": "/home/vscode/.cargo/bin:/home/vscode/.local/venv/bin:${containerEnv:PATH}" + }, + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + "runArgs": ["-e", "GIT_EDITOR=code --wait"] +} diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0ac232b --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Code of conduct + +## Our pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention + or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or + electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate + in a professional setting + +## Our responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project lead at frenck@addons.community. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project lead is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..6615398 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish +to make via issue, email, or any other method with the owners of this repository +before making a change. + +Please note we have a code of conduct, please follow it in all your interactions +with the project. + +## Issues and feature requests + +You've found a bug in the source code, a mistake in the documentation or maybe +you'd like a new feature? You can help us by submitting an issue to our +[GitHub Repository][github]. Before you create an issue, make sure you search +the archive, maybe your question was already answered. + +Even better: You could submit a pull request with a fix / new feature! + +## Pull request process + +1. Search our repository for open or closed [pull requests][prs] that relates + to your submission. You don't want to duplicate effort. + +2. You may merge the pull request in once you have the sign-off of two other + developers, or if you do not have permission to do that, you may request + the second reviewer to merge it for you. + +[github]: https://github.com/home-assistant-libs/python-webrtc-models/issues +[prs]: https://github.com/home-assistant-libs/python-webrtc-models/pulls diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..544da8f --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,20 @@ +# Problem/Motivation + +> (Why the issue was filed) + +## Expected behavior + +> (What you expected to happen) + +## Actual behavior + +> (What actually happened) + +## Steps to reproduce + +> (How can someone else make/see it happen) + +## Proposed changes + +> (If you have a proposed change, workaround or fix, +> describe the rationale behind it) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..14f7b5b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +# Proposed Changes + +> (Describe the changes and rationale behind them) + +## Related Issues + +> ([Github link][autolink-references] to related issues or pull requests) + +[autolink-references]: https://help.github.com/articles/autolinked-references-and-urls/ diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 0000000..203676d --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,85 @@ +--- +- name: "breaking-change" + color: ee0701 + description: "A breaking change for existing users." +- name: "bugfix" + color: ee0701 + description: "Inconsistencies or issues which will cause a problem for users or implementers." +- name: "documentation" + color: 0052cc + description: "Solely about the documentation of the project." +- name: "enhancement" + color: 1d76db + description: "Enhancement of the code, not introducing new features." +- name: "refactor" + color: 1d76db + description: "Improvement of existing code, not introducing new features." +- name: "performance" + color: 1d76db + description: "Improving performance, not introducing new features." +- name: "new-feature" + color: 0e8a16 + description: "New features or options." +- name: "maintenance" + color: 2af79e + description: "Generic maintenance tasks." +- name: "ci" + color: 1d76db + description: "Work that improves the continue integration." +- name: "dependencies" + color: 1d76db + description: "Upgrade or downgrade of project dependencies." + +- name: "in-progress" + color: fbca04 + description: "Issue is currently being resolved by a developer." +- name: "stale" + color: fef2c0 + description: "There has not been activity on this issue or PR for quite some time." +- name: "no-stale" + color: fef2c0 + description: "This issue or PR is exempted from the stable bot." + +- name: "security" + color: ee0701 + description: "Marks a security issue that needs to be resolved asap." +- name: "incomplete" + color: fef2c0 + description: "Marks a PR or issue that is missing information." +- name: "invalid" + color: fef2c0 + description: "Marks a PR or issue that is missing information." + +- name: "beginner-friendly" + color: 0e8a16 + description: "Good first issue for people wanting to contribute to the project." +- name: "help-wanted" + color: 0e8a16 + description: "We need some extra helping hands or expertise in order to resolve this." + +- name: "hacktoberfest" + description: "Issues/PRs are participating in the Hacktoberfest." + color: fbca04 +- name: "hacktoberfest-accepted" + description: "Issues/PRs are participating in the Hacktoberfest." + color: fbca04 + +- name: "priority-critical" + color: ee0701 + description: "This should be dealt with ASAP. Not fixing this issue would be a serious error." +- name: "priority-high" + color: b60205 + description: "After critical issues are fixed, these should be dealt with before any further issues." +- name: "priority-medium" + color: 0e8a16 + description: "This issue may be useful, and needs some attention." +- name: "priority-low" + color: e4ea8a + description: "Nice addition, maybe... someday..." + +- name: "major" + color: b60205 + description: "This PR causes a major version bump in the version number." +- name: "minor" + color: 0e8a16 + description: "This PR causes a minor version bump in the version number." diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..cb404ea --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,57 @@ +--- +name-template: "v$RESOLVED_VERSION" +tag-template: "v$RESOLVED_VERSION" +change-template: "- $TITLE @$AUTHOR (#$NUMBER)" +sort-direction: ascending + +categories: + - title: "๐Ÿšจ Breaking changes" + labels: + - "breaking-change" + - title: "โœจ New features" + labels: + - "new-feature" + - title: "๐Ÿ› Bug fixes" + labels: + - "bugfix" + - title: "๐Ÿš€ Enhancements" + labels: + - "enhancement" + - "refactor" + - "performance" + - title: "๐Ÿงฐ Maintenance" + labels: + - "maintenance" + - "ci" + - title: "๐Ÿ“š Documentation" + labels: + - "documentation" + - title: "โฌ†๏ธ Dependency updates" + labels: + - "dependencies" + +version-resolver: + major: + labels: + - "major" + - "breaking-change" + minor: + labels: + - "minor" + - "new-feature" + patch: + labels: + - "bugfix" + - "chore" + - "ci" + - "dependencies" + - "documentation" + - "enhancement" + - "performance" + - "refactor" + default: patch + +template: | + ## Whatโ€™s changed + + $CHANGES diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..0fdb8fc --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "commitMessagePrefix": "โฌ†๏ธ", + "configMigration": true, + "dependencyDashboard": true, + "labels": ["dependencies", "no-stale"], + "lockFileMaintenance": { + "enabled": true + }, + "packageRules": [ + { + "addLabels": ["python"], + "matchManagers": ["pep621"] + }, + { + "matchDepTypes": ["dev"], + "matchManagers": ["pep621"], + "rangeStrategy": "pin" + }, + { + "addLabels": ["github_actions"], + "matchManagers": ["github-actions"], + "rangeStrategy": "pin" + }, + { + "matchManagers": ["github-actions"], + "matchUpdateTypes": ["minor", "patch"], + "automerge": true + } + ], + "rebaseWhen": "behind-base-branch" +} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..6560a6f --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,86 @@ +--- +name: CI + +# yamllint disable-line rule:truthy +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + pre-commit: + name: pre-commit + runs-on: ubuntu-latest + env: + PRE_COMMIT_HOME: /tmp/pre-commit-cache + steps: + - name: โคต๏ธ Check out code from GitHub + uses: actions/checkout@v4.2.0 + - name: ๐Ÿ— Set up uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + - name: ๐Ÿ— Set pre-commit cache + uses: actions/cache@v4 + with: + path: ${{ env.PRE_COMMIT_HOME }} + key: ${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml','.python-version') }} + - name: ๐Ÿš€ Run pre-commit + env: + # Skipping branch check and pytest as pytest is run in a separate job + SKIP: no-commit-to-branch,pytest + run: uv run --frozen pre-commit run -a + + pytest: + name: pytest + runs-on: ubuntu-latest + strategy: + matrix: + python: ["3.12"] + steps: + - name: โคต๏ธ Check out code from GitHub + uses: actions/checkout@v4.2.0 + - name: ๐Ÿ— Set up uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + - name: ๐Ÿ— Set up Python ${{ matrix.python }} + run: uv python install ${{ matrix.python }} + - name: ๐Ÿ— Install dependencies + run: uv sync --frozen --dev + - name: ๐Ÿš€ Run pytest + run: uv run pytest --cov webrtc_models tests + - name: โฌ†๏ธ Upload coverage artifact + uses: actions/upload-artifact@v4.4.0 + with: + include-hidden-files: true + name: coverage-${{ matrix.python }} + path: .coverage + + coverage: + runs-on: ubuntu-latest + needs: pytest + steps: + - name: โคต๏ธ Check out code from GitHub + uses: actions/checkout@v4.2.0 + with: + fetch-depth: 0 + - name: โฌ‡๏ธ Download coverage data + uses: actions/download-artifact@v4.1.8 + - name: ๐Ÿ— Set up uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + - name: ๐Ÿ— Install dependencies + run: uv sync --frozen --dev + - name: ๐Ÿš€ Process coverage results + run: | + uv run --frozen coverage combine coverage*/.coverage* + uv run --frozen coverage xml -i + - name: โฌ†๏ธ Upload coverage report + uses: codecov/codecov-action@v4.5.0 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/labels.yaml b/.github/workflows/labels.yaml new file mode 100644 index 0000000..18ece2a --- /dev/null +++ b/.github/workflows/labels.yaml @@ -0,0 +1,23 @@ +--- +name: Sync labels + +# yamllint disable-line rule:truthy +on: + push: + branches: + - main + paths: + - .github/labels.yml + workflow_dispatch: + +jobs: + labels: + name: โ™ป๏ธ Sync labels + runs-on: ubuntu-latest + steps: + - name: โคต๏ธ Check out code from GitHub + uses: actions/checkout@v4.2.0 + - name: ๐Ÿš€ Run Label Syncer + uses: micnncim/action-label-syncer@v1.3.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lock.yaml b/.github/workflows/lock.yaml new file mode 100644 index 0000000..60a5f7f --- /dev/null +++ b/.github/workflows/lock.yaml @@ -0,0 +1,21 @@ +--- +name: Lock + +# yamllint disable-line rule:truthy +on: + schedule: + - cron: "0 9 * * *" + workflow_dispatch: + +jobs: + lock: + name: ๐Ÿ”’ Lock closed issues and PRs + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v5.0.1 + with: + github-token: ${{ github.token }} + issue-inactive-days: "30" + issue-lock-reason: "" + pr-inactive-days: "1" + pr-lock-reason: "" diff --git a/.github/workflows/pr-labels.yaml b/.github/workflows/pr-labels.yaml new file mode 100644 index 0000000..7c2b074 --- /dev/null +++ b/.github/workflows/pr-labels.yaml @@ -0,0 +1,27 @@ +--- +name: PR Labels + +# yamllint disable-line rule:truthy +on: + pull_request_target: + types: + - opened + - labeled + - unlabeled + - synchronize + workflow_call: + +jobs: + pr_labels: + name: Verify + runs-on: ubuntu-latest + steps: + - name: ๐Ÿท Verify PR has a valid label + uses: jesusvasquez333/verify-pr-label-action@v1.4.0 + with: + pull-request-number: "${{ github.event.pull_request.number }}" + github-token: "${{ secrets.GITHUB_TOKEN }}" + valid-labels: >- + breaking-change, bugfix, documentation, enhancement, + refactor, performance, new-feature, maintenance, ci, dependencies + disable-reviews: true diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml new file mode 100644 index 0000000..9d1d445 --- /dev/null +++ b/.github/workflows/release-drafter.yaml @@ -0,0 +1,19 @@ +--- +name: Release Drafter + +# yamllint disable-line rule:truthy +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + update_release_draft: + name: โœ๏ธ Draft release + runs-on: ubuntu-latest + steps: + - name: ๐Ÿš€ Run Release Drafter + uses: release-drafter/release-drafter@v6.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml new file mode 100644 index 0000000..7bf1116 --- /dev/null +++ b/.github/workflows/stale.yaml @@ -0,0 +1,40 @@ +--- +name: Stale + +# yamllint disable-line rule:truthy +on: + schedule: + - cron: "0 8 * * *" + workflow_dispatch: + +jobs: + stale: + name: ๐Ÿงน Clean up stale issues and PRs + runs-on: ubuntu-latest + steps: + - name: ๐Ÿš€ Run stale + uses: actions/stale@v9.0.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + days-before-stale: 30 + days-before-close: 7 + remove-stale-when-updated: true + stale-issue-label: "stale" + exempt-issue-labels: "no-stale,help-wanted" + stale-issue-message: > + There hasn't been any activity on this issue recently, so we + clean up some of the older and inactive issues. + + Please make sure to update to the latest version and + check if that solves the issue. Let us know if that works for you + by leaving a comment ๐Ÿ‘ + + This issue has now been marked as stale and will be closed if no + further activity occurs. Thanks! + stale-pr-label: "stale" + exempt-pr-labels: "no-stale" + stale-pr-message: > + There hasn't been any activity on this pull request recently. This + pull request has been automatically marked as stale because of that + and will be closed if no further activity occurs within 7 days. + Thank you for your contributions. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82f9275 --- /dev/null +++ b/.gitignore @@ -0,0 +1,162 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..097d6f1 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,140 @@ +--- +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.6.4 + hooks: + - id: ruff + name: ๐Ÿถ Ruff lint + args: + - --fix + # - --unsafe-fixes + + - id: ruff-format + name: ๐Ÿถ Ruff format + + - repo: https://github.com/asottile/pyupgrade + rev: v3.17.0 + hooks: + - id: pyupgrade + name: โฌ†๏ธ Upgrade Python syntax + args: + - --py312-plus + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-added-large-files + name: ๐Ÿ—œ๏ธ Check for added large files + + - id: check-ast + name: ๐Ÿ Check python ast + + - id: check-builtin-literals + name: ๐Ÿง Check builtin type constructor use + + - id: check-case-conflict + name: ๐Ÿ”  Check for case conflicts + + - id: check-docstring-first + name: โ„น๏ธ Check docstring is first + + - id: check-executables-have-shebangs + name: ๐Ÿง Check that executables have shebangs + + - id: check-shebang-scripts-are-executable + name: ๐Ÿง Check that scripts with shebangs are executable + + - id: check-merge-conflict + name: ๐Ÿ’ฅ Check for merge conflicts + + - id: check-symlinks + name: ๐Ÿ”— Check for broken symlinks + + - id: check-toml + name: โœ… Check toml + + - id: check-xml + name: โœ… Check xml + + - id: check-yaml + name: โœ… Check yaml + + - id: debug-statements + name: ๐Ÿ” Debug statements (Python) + + - id: destroyed-symlinks + name: ๐Ÿ”— Detect destroyed symlinks + + - id: detect-private-key + name: ๐Ÿ•ต๏ธ Detect private key + + - id: end-of-file-fixer + name: โฎ Fix End of Files + + - id: fix-byte-order-marker + name: ๐Ÿง Fix utf-8 byte order marker + + - id: name-tests-test + name: ๐Ÿ”  Python tests naming + args: + - --pytest-test-first + exclude: ^tests/syrupy.py + + - id: no-commit-to-branch + name: ๐Ÿ›‘ Don't commit to main branch + + - id: trailing-whitespace + name: โœ„ Trim trailing whitespace + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.1.0 + hooks: + - id: prettier + name: ๐Ÿ’„ Ensuring files are prettier + additional_dependencies: + - prettier@3.3.3 + - prettier-plugin-sort-json@3.1.0 + exclude_types: + - python + exclude: ^uv.lock$ + + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.35.1 + hooks: + - id: yamllint + name: ๐ŸŽ— Check YAML files with yamllint + + - repo: local + hooks: + - id: mypy + name: ๐Ÿ†Ž Static type checking using mypy + language: system + types: + - python + entry: uv run mypy + require_serial: true + + - id: prettier + name: ๐Ÿ’„ Ensuring files are prettier + language: system + types: + - yaml + - json + - markdown + entry: npm run prettier + pass_filenames: false + + - id: pylint + name: ๐ŸŒŸ Starring code with pylint + language: system + types: + - python + entry: uv run pylint + + - id: pytest + name: ๐Ÿงช Running tests and test coverage with pytest + language: system + types: + - python + entry: uv run pytest + pass_filenames: false diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..e6b1813 --- /dev/null +++ b/.yamllint @@ -0,0 +1,66 @@ +--- +ignore: + - .venv +rules: + braces: + level: error + min-spaces-inside: 0 + max-spaces-inside: 1 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + brackets: + level: error + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + colons: + level: error + max-spaces-before: 0 + max-spaces-after: 1 + commas: + level: error + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + level: error + require-starting-space: true + min-spaces-from-content: 1 + comments-indentation: + level: error + document-end: + level: error + present: false + document-start: + level: error + present: true + empty-lines: + level: error + max: 1 + max-start: 0 + max-end: 1 + hyphens: + level: error + max-spaces-after: 1 + indentation: + level: error + spaces: 2 + indent-sequences: true + check-multi-line-strings: false + key-duplicates: + level: error + line-length: + level: warning + max: 120 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + new-line-at-end-of-file: + level: error + new-lines: + level: error + type: unix + trailing-spaces: + level: error + truthy: + level: error diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d3fc0f --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# Python: WebRTC models + +This package provides some data classes for the [WebRTC spec][webrtc-spec] + +## Installation + +```bash +uv pip install webrtc-models +``` + +## Changelog & Releases + +This repository keeps a change log using [GitHub's releases][releases] +functionality. The format of the log is based on +[Keep a Changelog][keepchangelog]. + +Releases are based on [Semantic Versioning][semver], and use the format +of `MAJOR.MINOR.PATCH`. In a nutshell, the version will be incremented +based on the following: + +- `MAJOR`: Incompatible or major changes. +- `MINOR`: Backwards-compatible new features and enhancements. +- `PATCH`: Backwards-compatible bugfixes and package updates. + +## Contributing + +This is an active open-source project. We are always open to people who want to +use the code or contribute to it. + +We've set up a separate document for our +[contribution guidelines](.github/CONTRIBUTING.md). + +Thank you for being involved! :heart_eyes: + +## Setting up development environment + +This Python project is fully managed using the [uv][uv] dependency manager. + +You need at least: + +- [uv][uv-install] + +To install all packages, including all development requirements: + +```bash +uv sync --dev +``` + +As this repository uses the [pre-commit][pre-commit] framework, all changes +are linted and tested with each commit. You can run all checks and tests +manually, using the following commands: + +In the project venv + +```bash +pre-commit run -a +``` + +or with + +```bash +uv run pre-commit run -a +``` + +To run just the Python tests: + +```bash +uv run pytest +``` + +## Authors & contributors + +The content is by [Robert Resch][edenhaus]. + +For a full list of all authors and contributors, +check [the contributor's page][contributors]. + +[contributors]: https://github.com/home-assistant-libs/python-webrtc-models/graphs/contributors +[edenhaus]: https://github.com/edenhaus +[keepchangelog]: http://keepachangelog.com/en/1.0.0/ +[uv]: https://docs.astral.sh/uv/ +[uv-install]: https://docs.astral.sh/uv/getting-started/installation/ +[pre-commit]: https://pre-commit.com/ +[releases]: https://github.com/home-assistant-libs/python-webrtc-models/releases +[semver]: http://semver.org/spec/v2.0.0.html +[webrtc-spec]: https://www.w3.org/TR/webrtc/ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..814e866 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,177 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "webrtc-models" +license = {text = "Apache-2.0"} +description = "Python WebRTC models" +readme = "README.md" +authors = [ + {name = "Robert Resch", email = "robert@resch.dev"} +] +keywords = ["webrtc"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.12", +] +requires-python = ">=3.12.0" +dependencies = [ + "mashumaro~=3.13", + "orjson>=3.10.7", +] +version = "0.0.0" + +[project.urls] +"Homepage" = "https://pypi.org/project/webrtc-models" +"Source Code" = "https://github.com/home-assistant-libs/python-webrtc-models" +"Bug Reports" = "https://github.com/home-assistant-libs/python-webrtc-models/issues" + +[tool.uv] +dev-dependencies = [ + "covdefaults>=2.3.0", + "mypy==1.11.2", + "pre-commit==3.8.0", + "pylint-per-file-ignores>=1.3.2", + "pylint==3.2.7", + "pytest-cov==5.0.0", + "pytest==8.3.3", + "syrupy>=4.7.1", +] + + +[tool.hatch.build.targets.sdist] +include = [ + "/webrtc_models", +] + + +[tool.coverage.report] +show_missing = true +fail_under = 99 + +[tool.coverage.run] +plugins = ["covdefaults"] +source = ["webrtc_models"] + +[tool.mypy] +# Specify the target platform details in config, so your developers are +# free to run mypy on Windows, Linux, or macOS and get consistent +# results. +platform = "linux" +python_version = "3.12" + +# show error messages from unrelated files +follow_imports = "normal" + +# suppress errors about unsatisfied imports +ignore_missing_imports = true + +# be strict +check_untyped_defs = true +disallow_any_generics = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +strict_optional = true +warn_incomplete_stub = true +warn_no_return = true +warn_redundant_casts = true +warn_return_any = true +warn_unused_configs = true +warn_unused_ignores = true + +[tool.ruff.lint.per-file-ignores] +"tests/**" = [ + "D100", # Missing docstring in public module + "D103", # Missing docstring in public function + "D104", # Missing docstring in public package + "N802", # Function name {name} should be lowercase + "N816", # Variable {name} in global scope should not be mixedCase + "PLR0913", # Too many arguments in function definition + "S101", # Use of assert detected + "SLF001", # Private member accessed: {access} + "T201", # print found +] + +[tool.pylint.BASIC] +good-names = [ + "_", + "ex", + "fp", + "i", + "id", + "j", + "k", + "on", + "Run", + "T", +] + +[tool.pylint.DESIGN] +max-attributes = 8 + +[tool.pylint.MASTER] +load-plugins=[ + "pylint_per_file_ignores", +] + +[tool.pylint."MESSAGES CONTROL"] +disable = [ + "duplicate-code", + "format", + "unsubscriptable-object", + "too-few-public-methods", + "too-many-instance-attributes", + "too-many-arguments", + "too-many-public-methods", + "wrong-import-order", +] + +per-file-ignores = [ + # redefined-outer-name: Tests reference fixtures in the test function + "/tests/:redefined-outer-name", +] + + +[tool.pylint.SIMILARITIES] +ignore-imports = true + +[tool.pylint.FORMAT] +max-line-length = 88 + +[tool.pytest.ini_options] +addopts = "--cov" +asyncio_mode = "auto" + +[tool.ruff.lint] +ignore = [ + "ANN101", # Self... explanatory + "ANN102", # cls... just as useless + "ANN401", # Opinioated warning on disallowing dynamically typed expressions + "COM812", # Recommended to disable due conflicts with ruff format + "D203", # Conflicts with other rules + "D213", # Conflicts with other rules + "D417", # False positives in some occasions + "ISC001", # Recommended to disable due conflicts with ruff format + "PLR2004", # Just annoying, not really useful + "PLR0913", # Too many arguments +] +select = ["ALL"] + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false + +[tool.ruff.lint.isort] +known-first-party = ["webrtc_models"] +force-sort-within-sections = true +split-on-trailing-comma = false +combine-as-imports = true + +[tool.ruff.lint.mccabe] +max-complexity = 25 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..31eb50f --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,9 @@ +"""WebRTC models tests.""" + +from pathlib import Path + + +def load_fixture(filename: str) -> str: + """Load a fixture.""" + path = Path(__package__) / "fixtures" / filename + return path.read_text(encoding="utf-8") diff --git a/tests/__snapshots__/test_init.ambr b/tests/__snapshots__/test_init.ambr new file mode 100644 index 0000000..cd0c30a --- /dev/null +++ b/tests/__snapshots__/test_init.ambr @@ -0,0 +1,131 @@ +# serializer version: 1 +# name: test_decoding_and_encoding[RTCConfiguration-RTCConfiguration_empty.json][dataclass] + dict({ + 'ice_servers': list([ + ]), + }) +# --- +# name: test_decoding_and_encoding[RTCConfiguration-RTCConfiguration_empty.json][dict] + dict({ + }) +# --- +# name: test_decoding_and_encoding[RTCConfiguration-RTCConfiguration_multiple_iceServers.json][dataclass] + dict({ + 'ice_servers': list([ + dict({ + 'credential': None, + 'urls': 'stun:stun.home-assisant.io:80', + 'username': None, + }), + dict({ + 'credential': 'credential', + 'urls': list([ + 'stun:stun.home-assisant.io:80', + 'stun:stun.l.google.com:19302', + ]), + 'username': 'username', + }), + ]), + }) +# --- +# name: test_decoding_and_encoding[RTCConfiguration-RTCConfiguration_multiple_iceServers.json][dict] + dict({ + 'iceServers': list([ + dict({ + 'urls': 'stun:stun.home-assisant.io:80', + }), + dict({ + 'credential': 'credential', + 'urls': list([ + 'stun:stun.home-assisant.io:80', + 'stun:stun.l.google.com:19302', + ]), + 'username': 'username', + }), + ]), + }) +# --- +# name: test_decoding_and_encoding[RTCConfiguration-RTCConfiguration_one_iceServer.json][dataclass] + dict({ + 'ice_servers': list([ + dict({ + 'credential': None, + 'urls': 'stun:stun.home-assisant.io:80', + 'username': None, + }), + ]), + }) +# --- +# name: test_decoding_and_encoding[RTCConfiguration-RTCConfiguration_one_iceServer.json][dict] + dict({ + 'iceServers': list([ + dict({ + 'urls': 'stun:stun.home-assisant.io:80', + }), + ]), + }) +# --- +# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_only_urls_list.json][dataclass] + dict({ + 'credential': None, + 'urls': list([ + 'stun:stun.home-assisant.io:80', + 'stun:stun.l.google.com:19302', + ]), + 'username': None, + }) +# --- +# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_only_urls_list.json][dict] + dict({ + 'urls': list([ + 'stun:stun.home-assisant.io:80', + 'stun:stun.l.google.com:19302', + ]), + }) +# --- +# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_only_urls_string.json][dataclass] + dict({ + 'credential': None, + 'urls': 'stun:stun.home-assisant.io:80', + 'username': None, + }) +# --- +# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_only_urls_string.json][dict] + dict({ + 'urls': 'stun:stun.home-assisant.io:80', + }) +# --- +# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_urls_list.json][dataclass] + dict({ + 'credential': 'credential', + 'urls': list([ + 'stun:stun.home-assisant.io:80', + 'stun:stun.l.google.com:19302', + ]), + 'username': 'username', + }) +# --- +# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_urls_list.json][dict] + dict({ + 'credential': 'credential', + 'urls': list([ + 'stun:stun.home-assisant.io:80', + 'stun:stun.l.google.com:19302', + ]), + 'username': 'username', + }) +# --- +# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_urls_string.json][dataclass] + dict({ + 'credential': 'credential', + 'urls': 'stun:stun.home-assisant.io:80', + 'username': 'username', + }) +# --- +# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_urls_string.json][dict] + dict({ + 'credential': 'credential', + 'urls': 'stun:stun.home-assisant.io:80', + 'username': 'username', + }) +# --- diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..8e91490 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,12 @@ +"""Configuration for the tests.""" + +import pytest +from syrupy import SnapshotAssertion + +from .syrupy import CustomSnapshotExtension + + +@pytest.fixture(name="snapshot") +def snapshot_assertion(snapshot: SnapshotAssertion) -> SnapshotAssertion: + """Return snapshot assertion fixture with the custom extension.""" + return snapshot.use_extension(CustomSnapshotExtension) diff --git a/tests/fixtures/RTCConfiguration_empty.json b/tests/fixtures/RTCConfiguration_empty.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/fixtures/RTCConfiguration_empty.json @@ -0,0 +1 @@ +{} diff --git a/tests/fixtures/RTCConfiguration_multiple_iceServers.json b/tests/fixtures/RTCConfiguration_multiple_iceServers.json new file mode 100644 index 0000000..e16f5f7 --- /dev/null +++ b/tests/fixtures/RTCConfiguration_multiple_iceServers.json @@ -0,0 +1,12 @@ +{ + "iceServers": [ + { + "urls": "stun:stun.home-assisant.io:80" + }, + { + "urls": ["stun:stun.home-assisant.io:80", "stun:stun.l.google.com:19302"], + "username": "username", + "credential": "credential" + } + ] +} diff --git a/tests/fixtures/RTCConfiguration_one_iceServer.json b/tests/fixtures/RTCConfiguration_one_iceServer.json new file mode 100644 index 0000000..9f2d9be --- /dev/null +++ b/tests/fixtures/RTCConfiguration_one_iceServer.json @@ -0,0 +1,7 @@ +{ + "iceServers": [ + { + "urls": "stun:stun.home-assisant.io:80" + } + ] +} diff --git a/tests/fixtures/RTCIceServer_only_urls_list.json b/tests/fixtures/RTCIceServer_only_urls_list.json new file mode 100644 index 0000000..09020f5 --- /dev/null +++ b/tests/fixtures/RTCIceServer_only_urls_list.json @@ -0,0 +1,3 @@ +{ + "urls": ["stun:stun.home-assisant.io:80", "stun:stun.l.google.com:19302"] +} diff --git a/tests/fixtures/RTCIceServer_only_urls_string.json b/tests/fixtures/RTCIceServer_only_urls_string.json new file mode 100644 index 0000000..d80e280 --- /dev/null +++ b/tests/fixtures/RTCIceServer_only_urls_string.json @@ -0,0 +1,3 @@ +{ + "urls": "stun:stun.home-assisant.io:80" +} diff --git a/tests/fixtures/RTCIceServer_urls_list.json b/tests/fixtures/RTCIceServer_urls_list.json new file mode 100644 index 0000000..5f9fa7c --- /dev/null +++ b/tests/fixtures/RTCIceServer_urls_list.json @@ -0,0 +1,5 @@ +{ + "urls": ["stun:stun.home-assisant.io:80", "stun:stun.l.google.com:19302"], + "username": "username", + "credential": "credential" +} diff --git a/tests/fixtures/RTCIceServer_urls_string.json b/tests/fixtures/RTCIceServer_urls_string.json new file mode 100644 index 0000000..806a218 --- /dev/null +++ b/tests/fixtures/RTCIceServer_urls_string.json @@ -0,0 +1,5 @@ +{ + "urls": "stun:stun.home-assisant.io:80", + "username": "username", + "credential": "credential" +} diff --git a/tests/syrupy.py b/tests/syrupy.py new file mode 100644 index 0000000..32c0729 --- /dev/null +++ b/tests/syrupy.py @@ -0,0 +1,66 @@ +"""Asynchronous Python client for go2rtc.""" + +from __future__ import annotations + +from dataclasses import asdict, is_dataclass +from typing import TYPE_CHECKING, Any + +from syrupy.extensions import AmberSnapshotExtension +from syrupy.extensions.amber import AmberDataSerializer + +if TYPE_CHECKING: + from syrupy.types import ( + PropertyFilter, + PropertyMatcher, + PropertyPath, + SerializableData, + ) + + +class CustomSnapshotSerializer(AmberDataSerializer): + """Custom snapshot serializer for Syrupy. + + Handles special cases for data structures. + """ + + @classmethod + def _serialize( # pylint: disable=too-many-arguments + cls, + data: SerializableData, + *, + depth: int = 0, + exclude: PropertyFilter | None = None, + include: PropertyFilter | None = None, + matcher: PropertyMatcher | None = None, + path: PropertyPath = (), + visited: set[Any] | None = None, + ) -> str: + """Pre-process data before serializing. + + This allows us to handle specific cases for data structures. + """ + serializable_data = data + if is_dataclass(type(data)): + serializable_data = asdict(data) + + return super()._serialize( + serializable_data, + depth=depth, + exclude=exclude, + include=include, + matcher=matcher, + path=path, + visited=visited, + ) + + +class CustomSnapshotExtension(AmberSnapshotExtension): + """Custom extension for Syrupy.""" + + VERSION = "1" + """Current version of serialization format. + + Need to be bumped when we change the CustomSnapshotSerializer. + """ + + serializer_class: type[AmberDataSerializer] = CustomSnapshotSerializer diff --git a/tests/test_init.py b/tests/test_init.py new file mode 100644 index 0000000..6d809b1 --- /dev/null +++ b/tests/test_init.py @@ -0,0 +1,48 @@ +"""WebRTC models tests.""" + +from __future__ import annotations + +import re +from typing import TYPE_CHECKING + +import pytest + +from tests import load_fixture +from webrtc_models import RTCConfiguration, RTCIceServer + +if TYPE_CHECKING: + from mashumaro.mixins.orjson import DataClassORJSONMixin + from syrupy import SnapshotAssertion + + +@pytest.mark.parametrize( + ("clazz", "filename"), + [ + # RTCIceServer + (RTCIceServer, "RTCIceServer_only_urls_string.json"), + (RTCIceServer, "RTCIceServer_only_urls_list.json"), + (RTCIceServer, "RTCIceServer_urls_string.json"), + (RTCIceServer, "RTCIceServer_urls_list.json"), + # RTCConfiguration + (RTCConfiguration, "RTCConfiguration_empty.json"), + (RTCConfiguration, "RTCConfiguration_one_iceServer.json"), + (RTCConfiguration, "RTCConfiguration_multiple_iceServers.json"), + ], +) +def test_decoding_and_encoding( + snapshot: SnapshotAssertion, + clazz: type[DataClassORJSONMixin], + filename: str, +) -> None: + """Test decoding/encoding.""" + # Json section + file_content = load_fixture(filename) + ice_server = clazz.from_json(file_content) + assert ice_server == snapshot(name="dataclass") + # replace spaces and newlines + assert ice_server.to_json() == re.sub(r"\s", "", file_content) + + # Dict section + ice_server_dict = ice_server.to_dict() + assert ice_server_dict == snapshot(name="dict") + assert ice_server == clazz.from_dict(ice_server_dict) diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..11cfe75 --- /dev/null +++ b/uv.lock @@ -0,0 +1,421 @@ +version = 1 +requires-python = ">=3.12.0" + +[[package]] +name = "astroid" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/53/1067e1113ecaf58312357f2cd93063674924119d80d173adc3f6f2387aa2/astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a", size = 397576 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/96/b32bbbb46170a1c8b8b1f28c794202e25cfe743565e9d3469b8eb1e0cc05/astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25", size = 276348 }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "covdefaults" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/ee/9a6f2611f72e4c5657ae5542a510cf4164d2c673687c0ea73bb1cbd85b4d/covdefaults-2.3.0.tar.gz", hash = "sha256:4e99f679f12d792bc62e5510fa3eb59546ed47bd569e36e4fddc4081c9c3ebf7", size = 4835 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/4c/823bc951445aa97e5a1b7e337690db3abf85212c8d138e170922e7916ac8/covdefaults-2.3.0-py2.py3-none-any.whl", hash = "sha256:2832961f6ffcfe4b57c338bc3418a3526f495c26fb9c54565409c5532f7c41be", size = 5144 }, +] + +[[package]] +name = "coverage" +version = "7.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/300fc921dff243cd518c7db3a4c614b7e4b2431b0d1145c1e274fd99bd70/coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", size = 206983 }, + { url = "https://files.pythonhosted.org/packages/e1/ab/6bf00de5327ecb8db205f9ae596885417a31535eeda6e7b99463108782e1/coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", size = 207221 }, + { url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342 }, + { url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371 }, + { url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455 }, + { url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924 }, + { url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252 }, + { url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897 }, + { url = "https://files.pythonhosted.org/packages/b6/e9/d9cc3deceb361c491b81005c668578b0dfa51eed02cd081620e9a62f24ec/coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", size = 209606 }, + { url = "https://files.pythonhosted.org/packages/47/c8/5a2e41922ea6740f77d555c4d47544acd7dc3f251fe14199c09c0f5958d3/coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", size = 210373 }, + { url = "https://files.pythonhosted.org/packages/8c/f9/9aa4dfb751cb01c949c990d136a0f92027fbcc5781c6e921df1cb1563f20/coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", size = 207007 }, + { url = "https://files.pythonhosted.org/packages/b9/67/e1413d5a8591622a46dd04ff80873b04c849268831ed5c304c16433e7e30/coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", size = 207269 }, + { url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886 }, + { url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037 }, + { url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038 }, + { url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690 }, + { url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765 }, + { url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611 }, + { url = "https://files.pythonhosted.org/packages/74/e4/7ff20d6a0b59eeaab40b3140a71e38cf52547ba21dbcf1d79c5a32bba61b/coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", size = 209671 }, + { url = "https://files.pythonhosted.org/packages/35/59/1812f08a85b57c9fdb6d0b383d779e47b6f643bc278ed682859512517e83/coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", size = 210368 }, + { url = "https://files.pythonhosted.org/packages/9c/15/08913be1c59d7562a3e39fce20661a98c0a3f59d5754312899acc6cb8a2d/coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", size = 207758 }, + { url = "https://files.pythonhosted.org/packages/c4/ae/b5d58dff26cade02ada6ca612a76447acd69dccdbb3a478e9e088eb3d4b9/coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", size = 208035 }, + { url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839 }, + { url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569 }, + { url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927 }, + { url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401 }, + { url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301 }, + { url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 }, + { url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307 }, + { url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453 }, +] + +[[package]] +name = "dill" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, +] + +[[package]] +name = "distlib" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/91/e2df406fb4efacdf46871c25cde65d3c6ee5e173b7e5a4547a47bae91920/distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64", size = 609931 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/41/9307e4f5f9976bc8b7fea0b66367734e8faf3ec84bc0d412d8cfabbb66cd/distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", size = 468850 }, +] + +[[package]] +name = "filelock" +version = "3.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, +] + +[[package]] +name = "identify" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/bb/25024dbcc93516c492b75919e76f389bac754a3e4248682fba32b250c880/identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98", size = 99097 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/0c/4ef72754c050979fdcc06c744715ae70ea37e734816bb6514f79df77a42f/identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0", size = 98972 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "isort" +version = "5.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, +] + +[[package]] +name = "mashumaro" +version = "3.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/40/753855b41db0db9a2bed0c83aaf9432df724acb21ab779f2a20b73f613ae/mashumaro-3.13.1.tar.gz", hash = "sha256:169f0290253b3e6077bcb39c14a9dd0791a3fdedd9e286e536ae561d4ff1975b", size = 165897 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/e4/23e8febb328c8b63b2f99083a3eec271e466d8d22b0726110143863b36e9/mashumaro-3.13.1-py3-none-any.whl", hash = "sha256:ad0a162b8f4ea232dadd2891d77ff20165b855b9d84610f36ac84462d4576aa0", size = 92063 }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 }, +] + +[[package]] +name = "mypy" +version = "1.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/86/5d7cbc4974fd564550b80fbb8103c05501ea11aa7835edf3351d90095896/mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79", size = 3078806 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/3a/ed7b12ecc3f6db2f664ccf85cb2e004d3e90bec928e9d7be6aa2f16b7cdf/mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318", size = 10990335 }, + { url = "https://files.pythonhosted.org/packages/04/e4/1a9051e2ef10296d206519f1df13d2cc896aea39e8683302f89bf5792a59/mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36", size = 10007119 }, + { url = "https://files.pythonhosted.org/packages/f3/3c/350a9da895f8a7e87ade0028b962be0252d152e0c2fbaafa6f0658b4d0d4/mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987", size = 12506856 }, + { url = "https://files.pythonhosted.org/packages/b6/49/ee5adf6a49ff13f4202d949544d3d08abb0ea1f3e7f2a6d5b4c10ba0360a/mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca", size = 12952066 }, + { url = "https://files.pythonhosted.org/packages/27/c0/b19d709a42b24004d720db37446a42abadf844d5c46a2c442e2a074d70d9/mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70", size = 9664000 }, + { url = "https://files.pythonhosted.org/packages/42/3a/bdf730640ac523229dd6578e8a581795720a9321399de494374afc437ec5/mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12", size = 2619625 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "orjson" +version = "3.10.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/03/821c8197d0515e46ea19439f5c5d5fd9a9889f76800613cfac947b5d7845/orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3", size = 5056450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/7c/b4ecc2069210489696a36e42862ccccef7e49e1454a3422030ef52881b01/orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f", size = 251409 }, + { url = "https://files.pythonhosted.org/packages/60/84/e495edb919ef0c98d054a9b6d05f2700fdeba3886edd58f1c4dfb25d514a/orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3", size = 147913 }, + { url = "https://files.pythonhosted.org/packages/c5/27/e40bc7d79c4afb7e9264f22320c285d06d2c9574c9c682ba0f1be3012833/orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93", size = 147390 }, + { url = "https://files.pythonhosted.org/packages/30/be/fd646fb1a461de4958a6eacf4ecf064b8d5479c023e0e71cc89b28fa91ac/orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313", size = 152973 }, + { url = "https://files.pythonhosted.org/packages/b1/00/414f8d4bc5ec3447e27b5c26b4e996e4ef08594d599e79b3648f64da060c/orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864", size = 164039 }, + { url = "https://files.pythonhosted.org/packages/a0/6b/34e6904ac99df811a06e42d8461d47b6e0c9b86e2fe7ee84934df6e35f0d/orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09", size = 142035 }, + { url = "https://files.pythonhosted.org/packages/17/7e/254189d9b6df89660f65aec878d5eeaa5b1ae371bd2c458f85940445d36f/orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5", size = 169941 }, + { url = "https://files.pythonhosted.org/packages/02/1a/d11805670c29d3a1b29fc4bd048dc90b094784779690592efe8c9f71249a/orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b", size = 167994 }, + { url = "https://files.pythonhosted.org/packages/20/5f/03d89b007f9d6733dc11bc35d64812101c85d6c4e9c53af9fa7e7689cb11/orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb", size = 143130 }, + { url = "https://files.pythonhosted.org/packages/c6/9d/9b9fb6c60b8a0e04031ba85414915e19ecea484ebb625402d968ea45b8d5/orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1", size = 137326 }, + { url = "https://files.pythonhosted.org/packages/15/05/121af8a87513c56745d01ad7cf215c30d08356da9ad882ebe2ba890824cd/orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149", size = 251331 }, + { url = "https://files.pythonhosted.org/packages/73/7f/8d6ccd64a6f8bdbfe6c9be7c58aeb8094aa52a01fbbb2cda42ff7e312bd7/orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe", size = 142012 }, + { url = "https://files.pythonhosted.org/packages/04/65/f2a03fd1d4f0308f01d372e004c049f7eb9bc5676763a15f20f383fa9c01/orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c", size = 169920 }, + { url = "https://files.pythonhosted.org/packages/e2/1c/3ef8d83d7c6a619ad3d69a4d5318591b4ce5862e6eda7c26bbe8208652ca/orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad", size = 167916 }, + { url = "https://files.pythonhosted.org/packages/f2/0d/820a640e5a7dfbe525e789c70871ebb82aff73b0c7bf80082653f86b9431/orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2", size = 143089 }, + { url = "https://files.pythonhosted.org/packages/1a/72/a424db9116c7cad2950a8f9e4aeb655a7b57de988eb015acd0fcd1b4609b/orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024", size = 137081 }, +] + +[[package]] +name = "packaging" +version = "24.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pre-commit" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/10/97ee2fa54dff1e9da9badbc5e35d0bbaef0776271ea5907eccf64140f72f/pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", size = 177815 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/92/caae8c86e94681b42c246f0bca35c059a2f0529e5b92619f6aba4cf7e7b6/pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f", size = 204643 }, +] + +[[package]] +name = "pylint" +version = "3.2.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/e8/d59ce8e54884c9475ed6510685ef4311a10001674c28703b23da30f3b24d/pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e", size = 1511922 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/4d/c73bc0fca447b918611985c325cd7017fb762050eb9c6ac6fa7d9ac6fbe4/pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b", size = 519906 }, +] + +[[package]] +name = "pylint-per-file-ignores" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/4d/44bc55d37c4d8abe96b1a3b57baecc0a1534264ec85b946bbcf35fa21618/pylint_per_file_ignores-1.3.2.tar.gz", hash = "sha256:3c641f69c316770749a8a353556504dae7469541cdaef38e195fe2228841451e", size = 4361 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/89/61ca7d4fa42a235dbe1fd72a025c019e7a2e380cef31219073c933e3d5ee/pylint_per_file_ignores-1.3.2-py3-none-any.whl", hash = "sha256:4a2a2d7b88484ef1d1b1170029e542954f70efbab13ac3b977606ea5617d04c1", size = 4686 }, +] + +[[package]] +name = "pytest" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, +] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "syrupy" +version = "4.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ac/105c151335bf71ddf7f3c77118438cad77d4cf092559a6b429bca1bb436b/syrupy-4.7.1.tar.gz", hash = "sha256:f9d4485f3f27d0e5df6ed299cac6fa32eb40a441915d988e82be5a4bdda335c8", size = 49117 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/0d/af9adb7a0e4420dcf249653f589cd27152fa6daab5cfd84e6d665dcd7df5/syrupy-4.7.1-py3-none-any.whl", hash = "sha256:be002267a512a4bedddfae2e026c93df1ea928ae10baadc09640516923376d41", size = 49135 }, +] + +[[package]] +name = "tomlkit" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "virtualenv" +version = "20.26.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/4c/66ce54c8736ff164e85117ca36b02a1e14c042a6963f85eeda82664fda4e/virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4", size = 9371932 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/1d/e1a44fdd6d30829ba21fc58b5d98a67e7aae8f4165f11d091e53aec12560/virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6", size = 5999288 }, +] + +[[package]] +name = "webrtc-models" +version = "0.0.0" +source = { editable = "." } +dependencies = [ + { name = "mashumaro" }, + { name = "orjson" }, +] + +[package.dev-dependencies] +dev = [ + { name = "covdefaults" }, + { name = "mypy" }, + { name = "pre-commit" }, + { name = "pylint" }, + { name = "pylint-per-file-ignores" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "syrupy" }, +] + +[package.metadata] +requires-dist = [ + { name = "mashumaro", specifier = "~=3.13" }, + { name = "orjson", specifier = ">=3.10.7" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "covdefaults", specifier = ">=2.3.0" }, + { name = "mypy", specifier = "==1.11.2" }, + { name = "pre-commit", specifier = "==3.8.0" }, + { name = "pylint", specifier = "==3.2.7" }, + { name = "pylint-per-file-ignores", specifier = ">=1.3.2" }, + { name = "pytest", specifier = "==8.3.3" }, + { name = "pytest-cov", specifier = "==5.0.0" }, + { name = "syrupy", specifier = ">=4.7.1" }, +] diff --git a/webrtc_models/__init__.py b/webrtc_models/__init__.py new file mode 100644 index 0000000..5ecf269 --- /dev/null +++ b/webrtc_models/__init__.py @@ -0,0 +1,42 @@ +"""WebRTC models.""" + +from dataclasses import dataclass, field + +from mashumaro import field_options +from mashumaro.config import BaseConfig +from mashumaro.mixins.orjson import DataClassORJSONMixin + + +class _RTCBaseModel(DataClassORJSONMixin): + """Base class for RTC models.""" + + class Config(BaseConfig): + """Mashumaro config.""" + + # Serialize to spec conform names and omit default values + omit_default = True + serialize_by_alias = True + + +@dataclass +class RTCIceServer(_RTCBaseModel): + """RTC Ice Server. + + See https://www.w3.org/TR/webrtc/#rtciceserver-dictionary + """ + + urls: str | list[str] + username: str | None = None + credential: str | None = None + + +@dataclass +class RTCConfiguration(_RTCBaseModel): + """RTC Configuration. + + See https://www.w3.org/TR/webrtc/#rtcconfiguration-dictionary + """ + + ice_servers: list[RTCIceServer] = field( + metadata=field_options(alias="iceServers"), default_factory=list + )