mirror of
https://github.com/element-hq/synapse.git
synced 2025-12-09 01:30:18 +00:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b0083cad9 | ||
|
|
09fd2645c2 | ||
|
|
891983f3f4 | ||
|
|
a096fba969 | ||
|
|
e8710e7c5e | ||
|
|
978ae0b080 | ||
|
|
93e658bd13 | ||
|
|
989c4d2585 | ||
|
|
4cd05baaec | ||
|
|
d688daf41c | ||
|
|
aff90a5245 | ||
|
|
83023ce1e0 | ||
|
|
39316672da | ||
|
|
f86918e562 | ||
|
|
3d28e2213f | ||
|
|
0dfc21ca9f | ||
|
|
ffd0b4c079 | ||
|
|
5fe4b7ed60 | ||
|
|
2862c77837 | ||
|
|
022e56cce3 | ||
|
|
a8e5c319ab | ||
|
|
88310fe7ed | ||
|
|
08e1b63b30 | ||
|
|
afdf9af6b5 | ||
|
|
3cf21bc649 | ||
|
|
e0e7a44fe9 | ||
|
|
c09298eeaf | ||
|
|
38588f9462 | ||
|
|
c20dd888bd | ||
|
|
d435cfc125 | ||
|
|
58dd25976c | ||
|
|
bf6163c8bf | ||
|
|
b4ee0bf71e | ||
|
|
119f02e3b3 | ||
|
|
1bddd25a85 | ||
|
|
d143276bda | ||
|
|
034c5e625c | ||
|
|
778897a4e9 | ||
|
|
78ec3043d6 | ||
|
|
566670c363 | ||
|
|
52089f1f79 | ||
|
|
703464c1f7 | ||
|
|
c928347779 | ||
|
|
b74c29f694 | ||
|
|
2741ead569 | ||
|
|
ba65d8c351 | ||
|
|
ae98771fea | ||
|
|
b7e592a88c | ||
|
|
db975ea10d | ||
|
|
8b79583643 | ||
|
|
9fb2b1731b | ||
|
|
f3975ce247 | ||
|
|
1b78f0318a | ||
|
|
a5d946bfcb | ||
|
|
ea3e08c49c | ||
|
|
54c93a1372 | ||
|
|
e39fba61a7 | ||
|
|
7a9660367a |
69
.github/dependabot.yml
vendored
69
.github/dependabot.yml
vendored
@@ -1,23 +1,92 @@
|
|||||||
version: 2
|
version: 2
|
||||||
|
# As dependabot is currently only run on a weekly basis, we raise the
|
||||||
|
# open-pull-requests-limit to 10 (from the default of 5) to better ensure we
|
||||||
|
# don't continuously grow a backlog of updates.
|
||||||
updates:
|
updates:
|
||||||
- # "pip" is the correct setting for poetry, per https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
|
- # "pip" is the correct setting for poetry, per https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
|
||||||
package-ecosystem: "pip"
|
package-ecosystem: "pip"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
# Group patch updates to packages together into a single PR, as they rarely
|
||||||
|
# if ever contain breaking changes that need to be reviewed separately.
|
||||||
|
#
|
||||||
|
# Less PRs means a streamlined review process.
|
||||||
|
#
|
||||||
|
# Python packages follow semantic versioning, and tend to only introduce
|
||||||
|
# breaking changes in major version bumps. Thus, we'll group minor and patch
|
||||||
|
# versions together.
|
||||||
|
groups:
|
||||||
|
minor-and-patches:
|
||||||
|
applies-to: version-updates
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
- "patch"
|
||||||
|
# Prevent pulling packages that were recently updated to help mitigate
|
||||||
|
# supply chain attacks. 14 days was taken from the recommendation at
|
||||||
|
# https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns
|
||||||
|
# where the author noted that 9/10 attacks would have been mitigated by a
|
||||||
|
# two week cooldown.
|
||||||
|
#
|
||||||
|
# The cooldown only applies to general updates; security updates will still
|
||||||
|
# be pulled in as soon as possible.
|
||||||
|
cooldown:
|
||||||
|
default-days: 14
|
||||||
|
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/docker"
|
directory: "/docker"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
# For container versions, breaking changes are also typically only introduced in major
|
||||||
|
# package bumps.
|
||||||
|
groups:
|
||||||
|
minor-and-patches:
|
||||||
|
applies-to: version-updates
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
- "patch"
|
||||||
|
cooldown:
|
||||||
|
default-days: 14
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
# Similarly for GitHub Actions, breaking changes are typically only introduced in major
|
||||||
|
# package bumps.
|
||||||
|
groups:
|
||||||
|
minor-and-patches:
|
||||||
|
applies-to: version-updates
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
- "patch"
|
||||||
|
cooldown:
|
||||||
|
default-days: 14
|
||||||
|
|
||||||
- package-ecosystem: "cargo"
|
- package-ecosystem: "cargo"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
versioning-strategy: "lockfile-only"
|
versioning-strategy: "lockfile-only"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
# The Rust ecosystem is special in that breaking changes are often introduced
|
||||||
|
# in minor version bumps, as packages typically stay pre-1.0 for a long time.
|
||||||
|
# Thus we specifically keep minor version bumps separate in their own PRs.
|
||||||
|
groups:
|
||||||
|
patches:
|
||||||
|
applies-to: version-updates
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
update-types:
|
||||||
|
- "patch"
|
||||||
|
cooldown:
|
||||||
|
default-days: 14
|
||||||
|
|||||||
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Extract version from pyproject.toml
|
- name: Extract version from pyproject.toml
|
||||||
# Note: explicitly requesting bash will mean bash is invoked with `-eo pipefail`, see
|
# Note: explicitly requesting bash will mean bash is invoked with `-eo pipefail`, see
|
||||||
@@ -123,7 +123,7 @@ jobs:
|
|||||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||||
|
|
||||||
- name: Calculate docker image tag
|
- name: Calculate docker image tag
|
||||||
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5.9.0
|
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||||
with:
|
with:
|
||||||
images: ${{ matrix.repository }}
|
images: ${{ matrix.repository }}
|
||||||
flavor: |
|
flavor: |
|
||||||
|
|||||||
34
.github/workflows/docs-pr-netlify.yaml
vendored
34
.github/workflows/docs-pr-netlify.yaml
vendored
@@ -1,34 +0,0 @@
|
|||||||
name: Deploy documentation PR preview
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows: [ "Prepare documentation PR preview" ]
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
netlify:
|
|
||||||
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
# There's a 'download artifact' action, but it hasn't been updated for the workflow_run action
|
|
||||||
# (https://github.com/actions/download-artifact/issues/60) so instead we get this mess:
|
|
||||||
- name: 📥 Download artifact
|
|
||||||
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
|
|
||||||
with:
|
|
||||||
workflow: docs-pr.yaml
|
|
||||||
run_id: ${{ github.event.workflow_run.id }}
|
|
||||||
name: book
|
|
||||||
path: book
|
|
||||||
|
|
||||||
- name: 📤 Deploy to Netlify
|
|
||||||
uses: matrix-org/netlify-pr-preview@9805cd123fc9a7e421e35340a05e1ebc5dee46b5 # v3
|
|
||||||
with:
|
|
||||||
path: book
|
|
||||||
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
|
|
||||||
branch: ${{ github.event.workflow_run.head_branch }}
|
|
||||||
revision: ${{ github.event.workflow_run.head_sha }}
|
|
||||||
token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
||||||
site_id: ${{ secrets.NETLIFY_SITE_ID }}
|
|
||||||
desc: Documentation preview
|
|
||||||
deployment_env: PR Documentation Preview
|
|
||||||
6
.github/workflows/docs-pr.yaml
vendored
6
.github/workflows/docs-pr.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
name: GitHub Pages
|
name: GitHub Pages
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
# Fetch all history so that the schema_versions script works.
|
# Fetch all history so that the schema_versions script works.
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
mdbook-version: '0.4.17'
|
mdbook-version: '0.4.17'
|
||||||
|
|
||||||
- name: Setup python
|
- name: Setup python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
name: Check links in documentation
|
name: Check links in documentation
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Setup mdbook
|
- name: Setup mdbook
|
||||||
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2.0.0
|
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2.0.0
|
||||||
|
|||||||
4
.github/workflows/docs.yaml
vendored
4
.github/workflows/docs.yaml
vendored
@@ -50,7 +50,7 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- pre
|
- pre
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
# Fetch all history so that the schema_versions script works.
|
# Fetch all history so that the schema_versions script works.
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@@ -64,7 +64,7 @@ jobs:
|
|||||||
run: echo 'window.SYNAPSE_VERSION = "${{ needs.pre.outputs.branch-version }}";' > ./docs/website_files/version.js
|
run: echo 'window.SYNAPSE_VERSION = "${{ needs.pre.outputs.branch-version }}";' > ./docs/website_files/version.js
|
||||||
|
|
||||||
- name: Setup python
|
- name: Setup python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/fix_lint.yaml
vendored
4
.github/workflows/fix_lint.yaml
vendored
@@ -18,14 +18,14 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
components: clippy, rustfmt
|
components: clippy, rustfmt
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: Setup Poetry
|
- name: Setup Poetry
|
||||||
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
|
|||||||
20
.github/workflows/latest_deps.yml
vendored
20
.github/workflows/latest_deps.yml
vendored
@@ -42,12 +42,12 @@ jobs:
|
|||||||
if: needs.check_repo.outputs.should_run_workflow == 'true'
|
if: needs.check_repo.outputs.should_run_workflow == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
# The dev dependencies aren't exposed in the wheel metadata (at least with current
|
# The dev dependencies aren't exposed in the wheel metadata (at least with current
|
||||||
# poetry-core versions), so we install with poetry.
|
# poetry-core versions), so we install with poetry.
|
||||||
@@ -77,13 +77,13 @@ jobs:
|
|||||||
postgres-version: "14"
|
postgres-version: "14"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- run: sudo apt-get -qq install xmlsec1
|
- run: sudo apt-get -qq install xmlsec1
|
||||||
- name: Set up PostgreSQL ${{ matrix.postgres-version }}
|
- name: Set up PostgreSQL ${{ matrix.postgres-version }}
|
||||||
@@ -93,7 +93,7 @@ jobs:
|
|||||||
-e POSTGRES_PASSWORD=postgres \
|
-e POSTGRES_PASSWORD=postgres \
|
||||||
-e POSTGRES_INITDB_ARGS="--lc-collate C --lc-ctype C --encoding UTF8" \
|
-e POSTGRES_INITDB_ARGS="--lc-collate C --lc-ctype C --encoding UTF8" \
|
||||||
postgres:${{ matrix.postgres-version }}
|
postgres:${{ matrix.postgres-version }}
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: pip install .[all,test]
|
- run: pip install .[all,test]
|
||||||
@@ -152,13 +152,13 @@ jobs:
|
|||||||
BLACKLIST: ${{ matrix.workers && 'synapse-blacklist-with-workers' }}
|
BLACKLIST: ${{ matrix.workers && 'synapse-blacklist-with-workers' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: Ensure sytest runs `pip install`
|
- name: Ensure sytest runs `pip install`
|
||||||
# Delete the lockfile so sytest will `pip install` rather than `poetry install`
|
# Delete the lockfile so sytest will `pip install` rather than `poetry install`
|
||||||
@@ -202,14 +202,14 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out synapse codebase
|
- name: Check out synapse codebase
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
path: synapse
|
path: synapse
|
||||||
|
|
||||||
- name: Prepare Complement's Prerequisites
|
- name: Prepare Complement's Prerequisites
|
||||||
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
||||||
|
|
||||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||||
with:
|
with:
|
||||||
cache-dependency-path: complement/go.sum
|
cache-dependency-path: complement/go.sum
|
||||||
go-version-file: complement/go.mod
|
go-version-file: complement/go.mod
|
||||||
@@ -234,7 +234,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2
|
- uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
4
.github/workflows/poetry_lockfile.yaml
vendored
4
.github/workflows/poetry_lockfile.yaml
vendored
@@ -16,8 +16,8 @@ jobs:
|
|||||||
name: "Check locked dependencies have sdists"
|
name: "Check locked dependencies have sdists"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- run: pip install tomli
|
- run: pip install tomli
|
||||||
|
|||||||
8
.github/workflows/push_complement_image.yml
vendored
8
.github/workflows/push_complement_image.yml
vendored
@@ -33,17 +33,17 @@ jobs:
|
|||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout specific branch (debug build)
|
- name: Checkout specific branch (debug build)
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.branch }}
|
ref: ${{ inputs.branch }}
|
||||||
- name: Checkout clean copy of develop (scheduled build)
|
- name: Checkout clean copy of develop (scheduled build)
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
if: github.event_name == 'schedule'
|
if: github.event_name == 'schedule'
|
||||||
with:
|
with:
|
||||||
ref: develop
|
ref: develop
|
||||||
- name: Checkout clean copy of master (on-push)
|
- name: Checkout clean copy of master (on-push)
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Work out labels for complement image
|
- name: Work out labels for complement image
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5.9.0
|
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository }}/complement-synapse
|
images: ghcr.io/${{ github.repository }}/complement-synapse
|
||||||
tags: |
|
tags: |
|
||||||
|
|||||||
23
.github/workflows/release-artifacts.yml
vendored
23
.github/workflows/release-artifacts.yml
vendored
@@ -27,8 +27,8 @@ jobs:
|
|||||||
name: "Calculate list of debian distros"
|
name: "Calculate list of debian distros"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- id: set-distros
|
- id: set-distros
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
path: src
|
path: src
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
${{ runner.os }}-buildx-
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
- name: Set up python
|
- name: Set up python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
@@ -114,27 +114,20 @@ jobs:
|
|||||||
os:
|
os:
|
||||||
- ubuntu-24.04
|
- ubuntu-24.04
|
||||||
- ubuntu-24.04-arm
|
- ubuntu-24.04-arm
|
||||||
- macos-14 # This uses arm64
|
|
||||||
- macos-15-intel # This uses x86-64
|
|
||||||
# is_pr is a flag used to exclude certain jobs from the matrix on PRs.
|
# is_pr is a flag used to exclude certain jobs from the matrix on PRs.
|
||||||
# It is not read by the rest of the workflow.
|
# It is not read by the rest of the workflow.
|
||||||
is_pr:
|
is_pr:
|
||||||
- ${{ startsWith(github.ref, 'refs/pull/') }}
|
- ${{ startsWith(github.ref, 'refs/pull/') }}
|
||||||
|
|
||||||
exclude:
|
exclude:
|
||||||
# Don't build macos wheels on PR CI.
|
|
||||||
- is_pr: true
|
|
||||||
os: "macos-15-intel"
|
|
||||||
- is_pr: true
|
|
||||||
os: "macos-14"
|
|
||||||
# Don't build aarch64 wheels on PR CI.
|
# Don't build aarch64 wheels on PR CI.
|
||||||
- is_pr: true
|
- is_pr: true
|
||||||
os: "ubuntu-24.04-arm"
|
os: "ubuntu-24.04-arm"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
# setup-python@v4 doesn't impose a default python version. Need to use 3.x
|
# setup-python@v4 doesn't impose a default python version. Need to use 3.x
|
||||||
# here, because `python` on osx points to Python 2.7.
|
# here, because `python` on osx points to Python 2.7.
|
||||||
@@ -170,8 +163,8 @@ jobs:
|
|||||||
if: ${{ !startsWith(github.ref, 'refs/pull/') }}
|
if: ${{ !startsWith(github.ref, 'refs/pull/') }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
|
|
||||||
|
|||||||
8
.github/workflows/schema.yaml
vendored
8
.github/workflows/schema.yaml
vendored
@@ -14,8 +14,8 @@ jobs:
|
|||||||
name: Ensure Synapse config schema is valid
|
name: Ensure Synapse config schema is valid
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Install check-jsonschema
|
- name: Install check-jsonschema
|
||||||
@@ -40,8 +40,8 @@ jobs:
|
|||||||
name: Ensure generated documentation is up-to-date
|
name: Ensure generated documentation is up-to-date
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Install PyYAML
|
- name: Install PyYAML
|
||||||
|
|||||||
87
.github/workflows/tests.yml
vendored
87
.github/workflows/tests.yml
vendored
@@ -86,12 +86,12 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.linting == 'true' }}
|
if: ${{ needs.changes.outputs.linting == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
@@ -106,18 +106,18 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.linting == 'true' }}
|
if: ${{ needs.changes.outputs.linting == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: "pip install 'click==8.1.1' 'GitPython>=3.1.20'"
|
- run: "pip install 'click==8.1.1' 'GitPython>=3.1.20' 'sqlglot>=28.0.0'"
|
||||||
- run: scripts-dev/check_schema_delta.py --force-colors
|
- run: scripts-dev/check_schema_delta.py --force-colors
|
||||||
|
|
||||||
check-lockfile:
|
check-lockfile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: .ci/scripts/check_lockfile.py
|
- run: .ci/scripts/check_lockfile.py
|
||||||
@@ -129,7 +129,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Setup Poetry
|
- name: Setup Poetry
|
||||||
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
@@ -151,13 +151,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: Setup Poetry
|
- name: Setup Poetry
|
||||||
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
@@ -187,19 +187,20 @@ jobs:
|
|||||||
lint-crlf:
|
lint-crlf:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Check line endings
|
- name: Check line endings
|
||||||
run: scripts-dev/check_line_terminators.sh
|
run: scripts-dev/check_line_terminators.sh
|
||||||
|
|
||||||
lint-newsfile:
|
lint-newsfile:
|
||||||
if: ${{ (github.base_ref == 'develop' || contains(github.base_ref, 'release-')) && github.actor != 'dependabot[bot]' }}
|
# Only run on pull_request events, targeting develop/release branches, and skip when the PR author is dependabot[bot].
|
||||||
|
if: ${{ github.event_name == 'pull_request' && (github.base_ref == 'develop' || contains(github.base_ref, 'release-')) && github.event.pull_request.user.login != 'dependabot[bot]' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: "pip install 'towncrier>=18.6.0rc1'"
|
- run: "pip install 'towncrier>=18.6.0rc1'"
|
||||||
@@ -213,14 +214,14 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.rust == 'true' }}
|
if: ${{ needs.changes.outputs.rust == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- run: cargo clippy -- -D warnings
|
- run: cargo clippy -- -D warnings
|
||||||
|
|
||||||
@@ -232,14 +233,14 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.rust == 'true' }}
|
if: ${{ needs.changes.outputs.rust == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: nightly-2025-04-23
|
toolchain: nightly-2025-04-23
|
||||||
components: clippy
|
components: clippy
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- run: cargo clippy --all-features -- -D warnings
|
- run: cargo clippy --all-features -- -D warnings
|
||||||
|
|
||||||
@@ -250,13 +251,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: Setup Poetry
|
- name: Setup Poetry
|
||||||
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
@@ -286,7 +287,7 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.rust == 'true' }}
|
if: ${{ needs.changes.outputs.rust == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
@@ -295,7 +296,7 @@ jobs:
|
|||||||
# `.rustfmt.toml`.
|
# `.rustfmt.toml`.
|
||||||
toolchain: nightly-2025-04-23
|
toolchain: nightly-2025-04-23
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- run: cargo fmt --check
|
- run: cargo fmt --check
|
||||||
|
|
||||||
@@ -306,8 +307,8 @@ jobs:
|
|||||||
needs: changes
|
needs: changes
|
||||||
if: ${{ needs.changes.outputs.linting_readme == 'true' }}
|
if: ${{ needs.changes.outputs.linting_readme == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: "pip install rstcheck"
|
- run: "pip install rstcheck"
|
||||||
@@ -354,8 +355,8 @@ jobs:
|
|||||||
needs: linting-done
|
needs: linting-done
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- id: get-matrix
|
- id: get-matrix
|
||||||
@@ -375,7 +376,7 @@ jobs:
|
|||||||
job: ${{ fromJson(needs.calculate-test-jobs.outputs.trial_test_matrix) }}
|
job: ${{ fromJson(needs.calculate-test-jobs.outputs.trial_test_matrix) }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- run: sudo apt-get -qq install xmlsec1
|
- run: sudo apt-get -qq install xmlsec1
|
||||||
- name: Set up PostgreSQL ${{ matrix.job.postgres-version }}
|
- name: Set up PostgreSQL ${{ matrix.job.postgres-version }}
|
||||||
if: ${{ matrix.job.postgres-version }}
|
if: ${{ matrix.job.postgres-version }}
|
||||||
@@ -393,7 +394,7 @@ jobs:
|
|||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
@@ -431,13 +432,13 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
# There aren't wheels for some of the older deps, so we need to install
|
# There aren't wheels for some of the older deps, so we need to install
|
||||||
# their build dependencies
|
# their build dependencies
|
||||||
@@ -446,7 +447,7 @@ jobs:
|
|||||||
sudo apt-get -qq install build-essential libffi-dev python3-dev \
|
sudo apt-get -qq install build-essential libffi-dev python3-dev \
|
||||||
libxml2-dev libxslt-dev xmlsec1 zlib1g-dev libjpeg-dev libwebp-dev
|
libxml2-dev libxslt-dev xmlsec1 zlib1g-dev libjpeg-dev libwebp-dev
|
||||||
|
|
||||||
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
|
|
||||||
@@ -496,7 +497,7 @@ jobs:
|
|||||||
extras: ["all"]
|
extras: ["all"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
# Install libs necessary for PyPy to build binary wheels for dependencies
|
# Install libs necessary for PyPy to build binary wheels for dependencies
|
||||||
- run: sudo apt-get -qq install xmlsec1 libxml2-dev libxslt-dev
|
- run: sudo apt-get -qq install xmlsec1 libxml2-dev libxslt-dev
|
||||||
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
@@ -546,7 +547,7 @@ jobs:
|
|||||||
job: ${{ fromJson(needs.calculate-test-jobs.outputs.sytest_test_matrix) }}
|
job: ${{ fromJson(needs.calculate-test-jobs.outputs.sytest_test_matrix) }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Prepare test blacklist
|
- name: Prepare test blacklist
|
||||||
run: cat sytest-blacklist .ci/worker-blacklist > synapse-blacklist-with-workers
|
run: cat sytest-blacklist .ci/worker-blacklist > synapse-blacklist-with-workers
|
||||||
|
|
||||||
@@ -554,7 +555,7 @@ jobs:
|
|||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: Run SyTest
|
- name: Run SyTest
|
||||||
run: /bootstrap.sh synapse
|
run: /bootstrap.sh synapse
|
||||||
@@ -593,7 +594,7 @@ jobs:
|
|||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- run: sudo apt-get -qq install xmlsec1 postgresql-client
|
- run: sudo apt-get -qq install xmlsec1 postgresql-client
|
||||||
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
@@ -637,7 +638,7 @@ jobs:
|
|||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Add PostgreSQL apt repository
|
- name: Add PostgreSQL apt repository
|
||||||
# We need a version of pg_dump that can handle the version of
|
# We need a version of pg_dump that can handle the version of
|
||||||
# PostgreSQL being tested against. The Ubuntu package repository lags
|
# PostgreSQL being tested against. The Ubuntu package repository lags
|
||||||
@@ -692,7 +693,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout synapse codebase
|
- name: Checkout synapse codebase
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
path: synapse
|
path: synapse
|
||||||
|
|
||||||
@@ -700,12 +701,12 @@ jobs:
|
|||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: Prepare Complement's Prerequisites
|
- name: Prepare Complement's Prerequisites
|
||||||
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
||||||
|
|
||||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||||
with:
|
with:
|
||||||
cache-dependency-path: complement/go.sum
|
cache-dependency-path: complement/go.sum
|
||||||
go-version-file: complement/go.mod
|
go-version-file: complement/go.mod
|
||||||
@@ -728,13 +729,13 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- run: cargo test
|
- run: cargo test
|
||||||
|
|
||||||
@@ -748,13 +749,13 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: nightly-2022-12-01
|
toolchain: nightly-2022-12-01
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- run: cargo bench --no-run
|
- run: cargo bench --no-run
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/triage_labelled.yml
vendored
2
.github/workflows/triage_labelled.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
# This field is case-sensitive.
|
# This field is case-sensitive.
|
||||||
TARGET_STATUS: Needs info
|
TARGET_STATUS: Needs info
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
# Only clone the script file we care about, instead of the whole repo.
|
# Only clone the script file we care about, instead of the whole repo.
|
||||||
sparse-checkout: .ci/scripts/triage_labelled_issue.sh
|
sparse-checkout: .ci/scripts/triage_labelled_issue.sh
|
||||||
|
|||||||
18
.github/workflows/twisted_trunk.yml
vendored
18
.github/workflows/twisted_trunk.yml
vendored
@@ -43,13 +43,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
@@ -70,14 +70,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- run: sudo apt-get -qq install xmlsec1
|
- run: sudo apt-get -qq install xmlsec1
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
@@ -117,13 +117,13 @@ jobs:
|
|||||||
- ${{ github.workspace }}:/src
|
- ${{ github.workspace }}:/src
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.RUST_VERSION }}
|
||||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
|
||||||
- name: Patch dependencies
|
- name: Patch dependencies
|
||||||
# Note: The poetry commands want to create a virtualenv in /src/.venv/,
|
# Note: The poetry commands want to create a virtualenv in /src/.venv/,
|
||||||
@@ -175,14 +175,14 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Run actions/checkout@v4 for synapse
|
- name: Run actions/checkout@v4 for synapse
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
with:
|
with:
|
||||||
path: synapse
|
path: synapse
|
||||||
|
|
||||||
- name: Prepare Complement's Prerequisites
|
- name: Prepare Complement's Prerequisites
|
||||||
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
||||||
|
|
||||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||||
with:
|
with:
|
||||||
cache-dependency-path: complement/go.sum
|
cache-dependency-path: complement/go.sum
|
||||||
go-version-file: complement/go.mod
|
go-version-file: complement/go.mod
|
||||||
@@ -217,7 +217,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2
|
- uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
63
CHANGES.md
63
CHANGES.md
@@ -1,3 +1,64 @@
|
|||||||
|
# Synapse 1.144.0rc1 (2025-12-02)
|
||||||
|
|
||||||
|
Admins using the unstable [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666) endpoint (`/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`), please check [the relevant section in the upgrade notes](https://github.com/element-hq/synapse/blob/develop/docs/upgrade.md#upgrading-to-v11440) as this release contains changes that disable that endpoint by default.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Add experimentatal implememntation of [MSC4380](https://github.com/matrix-org/matrix-spec-proposals/pull/4380) (invite blocking). ([\#19203](https://github.com/element-hq/synapse/issues/19203))
|
||||||
|
- Allow restarting delayed event timeouts on workers. ([\#19207](https://github.com/element-hq/synapse/issues/19207))
|
||||||
|
|
||||||
|
## Bugfixes
|
||||||
|
|
||||||
|
- Fix a bug in the database function for fetching state deltas that could result in unnecessarily long query times. ([\#18960](https://github.com/element-hq/synapse/issues/18960))
|
||||||
|
- Fix v12 rooms when running with `use_frozen_dicts: True`. ([\#19235](https://github.com/element-hq/synapse/issues/19235))
|
||||||
|
- Fix bug where invalid `canonical_alias` content would return 500 instead of 400. ([\#19240](https://github.com/element-hq/synapse/issues/19240))
|
||||||
|
- Fix bug where `Duration` was logged incorrectly. ([\#19267](https://github.com/element-hq/synapse/issues/19267))
|
||||||
|
|
||||||
|
## Improved Documentation
|
||||||
|
|
||||||
|
- Document in the `--config-path` help how multiple files are merged - by merging them shallowly. ([\#19243](https://github.com/element-hq/synapse/issues/19243))
|
||||||
|
|
||||||
|
## Deprecations and Removals
|
||||||
|
|
||||||
|
- Stop building release wheels for MacOS. ([\#19225](https://github.com/element-hq/synapse/issues/19225))
|
||||||
|
|
||||||
|
## Internal Changes
|
||||||
|
|
||||||
|
- Improve event filtering for Simplified Sliding Sync. ([\#17782](https://github.com/element-hq/synapse/issues/17782))
|
||||||
|
- Export `SYNAPSE_SUPPORTED_COMPLEMENT_TEST_PACKAGES` environment variable from `scripts-dev/complement.sh`. ([\#19208](https://github.com/element-hq/synapse/issues/19208))
|
||||||
|
- Refactor `scripts-dev/complement.sh` logic to avoid `exit` to facilitate being able to source it from other scripts (composable). ([\#19209](https://github.com/element-hq/synapse/issues/19209))
|
||||||
|
- Expire sliding sync connections that are too old or have too much pending data. ([\#19211](https://github.com/element-hq/synapse/issues/19211))
|
||||||
|
- Require an experimental feature flag to be enabled in order for the unstable [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666) endpoint (`/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`) to be available. ([\#19219](https://github.com/element-hq/synapse/issues/19219))
|
||||||
|
- Prevent changelog check CI running on @dependabot's PRs even when a human has modified the branch. ([\#19220](https://github.com/element-hq/synapse/issues/19220))
|
||||||
|
- Auto-fix trailing spaces in multi-line strings and comments when running the lint script. ([\#19221](https://github.com/element-hq/synapse/issues/19221))
|
||||||
|
- Move towards using a dedicated `Duration` type. ([\#19223](https://github.com/element-hq/synapse/issues/19223), [\#19229](https://github.com/element-hq/synapse/issues/19229))
|
||||||
|
- Improve robustness of the SQL schema linting in CI. ([\#19224](https://github.com/element-hq/synapse/issues/19224))
|
||||||
|
- Add log to determine whether clients are using `/messages` as expected. ([\#19226](https://github.com/element-hq/synapse/issues/19226))
|
||||||
|
- Simplify README and add ESS Getting started section. ([\#19228](https://github.com/element-hq/synapse/issues/19228), [\#19259](https://github.com/element-hq/synapse/issues/19259))
|
||||||
|
- Add a unit test for ensuring associated refresh tokens are erased when a device is deleted. ([\#19230](https://github.com/element-hq/synapse/issues/19230))
|
||||||
|
- Prompt user to consider adding future deprecations to the changelog in release script. ([\#19239](https://github.com/element-hq/synapse/issues/19239))
|
||||||
|
- Fix check of the Rust compiled code being outdated when using source checkout and `.egg-info`. ([\#19251](https://github.com/element-hq/synapse/issues/19251))
|
||||||
|
- Stop building macos wheels in CI pipeline. ([\#19263](https://github.com/element-hq/synapse/issues/19263))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump Swatinem/rust-cache from 2.8.1 to 2.8.2. ([\#19244](https://github.com/element-hq/synapse/issues/19244))
|
||||||
|
* Bump actions/checkout from 5.0.0 to 6.0.0. ([\#19213](https://github.com/element-hq/synapse/issues/19213))
|
||||||
|
* Bump actions/setup-go from 6.0.0 to 6.1.0. ([\#19214](https://github.com/element-hq/synapse/issues/19214))
|
||||||
|
* Bump actions/setup-python from 6.0.0 to 6.1.0. ([\#19245](https://github.com/element-hq/synapse/issues/19245))
|
||||||
|
* Bump attrs from 25.3.0 to 25.4.0. ([\#19215](https://github.com/element-hq/synapse/issues/19215))
|
||||||
|
* Bump docker/metadata-action from 5.9.0 to 5.10.0. ([\#19246](https://github.com/element-hq/synapse/issues/19246))
|
||||||
|
* Bump http from 1.3.1 to 1.4.0. ([\#19249](https://github.com/element-hq/synapse/issues/19249))
|
||||||
|
* Bump pydantic from 2.12.4 to 2.12.5. ([\#19250](https://github.com/element-hq/synapse/issues/19250))
|
||||||
|
* Bump pyopenssl from 25.1.0 to 25.3.0. ([\#19248](https://github.com/element-hq/synapse/issues/19248))
|
||||||
|
* Bump rpds-py from 0.28.0 to 0.29.0. ([\#19216](https://github.com/element-hq/synapse/issues/19216))
|
||||||
|
* Bump rpds-py from 0.29.0 to 0.30.0. ([\#19247](https://github.com/element-hq/synapse/issues/19247))
|
||||||
|
* Bump sentry-sdk from 2.44.0 to 2.46.0. ([\#19218](https://github.com/element-hq/synapse/issues/19218))
|
||||||
|
* Bump types-bleach from 6.2.0.20250809 to 6.3.0.20251115. ([\#19217](https://github.com/element-hq/synapse/issues/19217))
|
||||||
|
* Bump types-jsonschema from 4.25.1.20250822 to 4.25.1.20251009. ([\#19252](https://github.com/element-hq/synapse/issues/19252))
|
||||||
|
|
||||||
# Synapse 1.143.0 (2025-11-25)
|
# Synapse 1.143.0 (2025-11-25)
|
||||||
|
|
||||||
## Dropping support for PostgreSQL 13
|
## Dropping support for PostgreSQL 13
|
||||||
@@ -11,7 +72,7 @@ No significant changes since 1.143.0rc2.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# synapse 1.143.0rc2 (2025-11-18)
|
# Synapse 1.143.0rc2 (2025-11-18)
|
||||||
|
|
||||||
## Dropping support for PostgreSQL 13
|
## Dropping support for PostgreSQL 13
|
||||||
|
|
||||||
|
|||||||
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -374,12 +374,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.3.1"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
|
||||||
"itoa",
|
"itoa",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
242
README.rst
242
README.rst
@@ -7,170 +7,48 @@
|
|||||||
|
|
||||||
Synapse is an open source `Matrix <https://matrix.org>`__ homeserver
|
Synapse is an open source `Matrix <https://matrix.org>`__ homeserver
|
||||||
implementation, written and maintained by `Element <https://element.io>`_.
|
implementation, written and maintained by `Element <https://element.io>`_.
|
||||||
`Matrix <https://github.com/matrix-org>`__ is the open standard for
|
`Matrix <https://github.com/matrix-org>`__ is the open standard for secure and
|
||||||
secure and interoperable real-time communications. You can directly run
|
interoperable real-time communications. You can directly run and manage the
|
||||||
and manage the source code in this repository, available under an AGPL
|
source code in this repository, available under an AGPL license (or
|
||||||
license (or alternatively under a commercial license from Element).
|
alternatively under a commercial license from Element).
|
||||||
There is no support provided by Element unless you have a
|
|
||||||
subscription from Element.
|
|
||||||
|
|
||||||
Subscription
|
There is no support provided by Element unless you have a subscription from
|
||||||
============
|
Element.
|
||||||
|
|
||||||
For those that need an enterprise-ready solution, Element
|
🚀 Getting started
|
||||||
Server Suite (ESS) is `available via subscription <https://element.io/pricing>`_.
|
==================
|
||||||
ESS builds on Synapse to offer a complete Matrix-based backend including the full
|
|
||||||
`Admin Console product <https://element.io/enterprise-functionality/admin-console>`_,
|
|
||||||
giving admins the power to easily manage an organization-wide
|
|
||||||
deployment. It includes advanced identity management, auditing,
|
|
||||||
moderation and data retention options as well as Long-Term Support and
|
|
||||||
SLAs. ESS supports any Matrix-compatible client.
|
|
||||||
|
|
||||||
.. contents::
|
This component is developed and maintained by `Element <https://element.io>`_.
|
||||||
|
It gets shipped as part of the **Element Server Suite (ESS)** which provides the
|
||||||
|
official means of deployment.
|
||||||
|
|
||||||
🛠️ Installation and configuration
|
ESS is a Matrix distribution from Element with focus on quality and ease of use.
|
||||||
==================================
|
It ships a full Matrix stack tailored to the respective use case.
|
||||||
|
|
||||||
The Synapse documentation describes `how to install Synapse <https://element-hq.github.io/synapse/latest/setup/installation.html>`_. We recommend using
|
There are three editions of ESS:
|
||||||
`Docker images <https://element-hq.github.io/synapse/latest/setup/installation.html#docker-images-and-ansible-playbooks>`_ or `Debian packages from Matrix.org
|
|
||||||
<https://element-hq.github.io/synapse/latest/setup/installation.html#matrixorg-packages>`_.
|
|
||||||
|
|
||||||
.. _federation:
|
- `ESS Community <https://github.com/element-hq/ess-helm>`_ - the free Matrix
|
||||||
|
distribution from Element tailored to small-/mid-scale, non-commercial
|
||||||
Synapse has a variety of `config options
|
community use cases
|
||||||
<https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html>`_
|
- `ESS Pro <https://element.io/server-suite>`_ - the commercial Matrix
|
||||||
which can be used to customise its behaviour after installation.
|
distribution from Element for professional use
|
||||||
There are additional details on how to `configure Synapse for federation here
|
- `ESS TI-M <https://element.io/server-suite/ti-messenger>`_ - a special version
|
||||||
<https://element-hq.github.io/synapse/latest/federate.html>`_.
|
of ESS Pro focused on the requirements of TI-Messenger Pro and ePA as
|
||||||
|
specified by the German National Digital Health Agency Gematik
|
||||||
.. _reverse-proxy:
|
|
||||||
|
|
||||||
Using a reverse proxy with Synapse
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
It is recommended to put a reverse proxy such as
|
|
||||||
`nginx <https://nginx.org/en/docs/http/ngx_http_proxy_module.html>`_,
|
|
||||||
`Apache <https://httpd.apache.org/docs/current/mod/mod_proxy_http.html>`_,
|
|
||||||
`Caddy <https://caddyserver.com/docs/quick-starts/reverse-proxy>`_,
|
|
||||||
`HAProxy <https://www.haproxy.org/>`_ or
|
|
||||||
`relayd <https://man.openbsd.org/relayd.8>`_ in front of Synapse. One advantage of
|
|
||||||
doing so is that it means that you can expose the default https port (443) to
|
|
||||||
Matrix clients without needing to run Synapse with root privileges.
|
|
||||||
For information on configuring one, see `the reverse proxy docs
|
|
||||||
<https://element-hq.github.io/synapse/latest/reverse_proxy.html>`_.
|
|
||||||
|
|
||||||
Upgrading an existing Synapse
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
The instructions for upgrading Synapse are in `the upgrade notes`_.
|
|
||||||
Please check these instructions as upgrading may require extra steps for some
|
|
||||||
versions of Synapse.
|
|
||||||
|
|
||||||
.. _the upgrade notes: https://element-hq.github.io/synapse/develop/upgrade.html
|
|
||||||
|
|
||||||
|
|
||||||
Platform dependencies
|
🛠️ Standalone installation and configuration
|
||||||
---------------------
|
============================================
|
||||||
|
|
||||||
Synapse uses a number of platform dependencies such as Python and PostgreSQL,
|
The Synapse documentation describes `options for installing Synapse standalone
|
||||||
and aims to follow supported upstream versions. See the
|
<https://element-hq.github.io/synapse/latest/setup/installation.html>`_. See
|
||||||
`deprecation policy <https://element-hq.github.io/synapse/latest/deprecation_policy.html>`_
|
below for more useful documentation links.
|
||||||
for more details.
|
|
||||||
|
|
||||||
|
- `Synapse configuration options <https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html>`_
|
||||||
|
- `Synapse configuration for federation <https://element-hq.github.io/synapse/latest/federate.html>`_
|
||||||
|
- `Using a reverse proxy with Synapse <https://element-hq.github.io/synapse/latest/reverse_proxy.html>`_
|
||||||
|
- `Upgrading Synapse <https://element-hq.github.io/synapse/develop/upgrade.html>`_
|
||||||
|
|
||||||
Security note
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Matrix serves raw, user-supplied data in some APIs -- specifically the `content
|
|
||||||
repository endpoints`_.
|
|
||||||
|
|
||||||
.. _content repository endpoints: https://matrix.org/docs/spec/client_server/latest.html#get-matrix-media-r0-download-servername-mediaid
|
|
||||||
|
|
||||||
Whilst we make a reasonable effort to mitigate against XSS attacks (for
|
|
||||||
instance, by using `CSP`_), a Matrix homeserver should not be hosted on a
|
|
||||||
domain hosting other web applications. This especially applies to sharing
|
|
||||||
the domain with Matrix web clients and other sensitive applications like
|
|
||||||
webmail. See
|
|
||||||
https://developer.github.com/changes/2014-04-25-user-content-security for more
|
|
||||||
information.
|
|
||||||
|
|
||||||
.. _CSP: https://github.com/matrix-org/synapse/pull/1021
|
|
||||||
|
|
||||||
Ideally, the homeserver should not simply be on a different subdomain, but on
|
|
||||||
a completely different `registered domain`_ (also known as top-level site or
|
|
||||||
eTLD+1). This is because `some attacks`_ are still possible as long as the two
|
|
||||||
applications share the same registered domain.
|
|
||||||
|
|
||||||
.. _registered domain: https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-2.3
|
|
||||||
|
|
||||||
.. _some attacks: https://en.wikipedia.org/wiki/Session_fixation#Attacks_using_cross-subdomain_cookie
|
|
||||||
|
|
||||||
To illustrate this with an example, if your Element Web or other sensitive web
|
|
||||||
application is hosted on ``A.example1.com``, you should ideally host Synapse on
|
|
||||||
``example2.com``. Some amount of protection is offered by hosting on
|
|
||||||
``B.example1.com`` instead, so this is also acceptable in some scenarios.
|
|
||||||
However, you should *not* host your Synapse on ``A.example1.com``.
|
|
||||||
|
|
||||||
Note that all of the above refers exclusively to the domain used in Synapse's
|
|
||||||
``public_baseurl`` setting. In particular, it has no bearing on the domain
|
|
||||||
mentioned in MXIDs hosted on that server.
|
|
||||||
|
|
||||||
Following this advice ensures that even if an XSS is found in Synapse, the
|
|
||||||
impact to other applications will be minimal.
|
|
||||||
|
|
||||||
|
|
||||||
🧪 Testing a new installation
|
|
||||||
=============================
|
|
||||||
|
|
||||||
The easiest way to try out your new Synapse installation is by connecting to it
|
|
||||||
from a web client.
|
|
||||||
|
|
||||||
Unless you are running a test instance of Synapse on your local machine, in
|
|
||||||
general, you will need to enable TLS support before you can successfully
|
|
||||||
connect from a client: see
|
|
||||||
`TLS certificates <https://element-hq.github.io/synapse/latest/setup/installation.html#tls-certificates>`_.
|
|
||||||
|
|
||||||
An easy way to get started is to login or register via Element at
|
|
||||||
https://app.element.io/#/login or https://app.element.io/#/register respectively.
|
|
||||||
You will need to change the server you are logging into from ``matrix.org``
|
|
||||||
and instead specify a homeserver URL of ``https://<server_name>:8448``
|
|
||||||
(or just ``https://<server_name>`` if you are using a reverse proxy).
|
|
||||||
If you prefer to use another client, refer to our
|
|
||||||
`client breakdown <https://matrix.org/ecosystem/clients/>`_.
|
|
||||||
|
|
||||||
If all goes well you should at least be able to log in, create a room, and
|
|
||||||
start sending messages.
|
|
||||||
|
|
||||||
.. _`client-user-reg`:
|
|
||||||
|
|
||||||
Registering a new user from a client
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
By default, registration of new users via Matrix clients is disabled. To enable
|
|
||||||
it:
|
|
||||||
|
|
||||||
1. In the
|
|
||||||
`registration config section <https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#registration>`_
|
|
||||||
set ``enable_registration: true`` in ``homeserver.yaml``.
|
|
||||||
2. Then **either**:
|
|
||||||
|
|
||||||
a. set up a `CAPTCHA <https://element-hq.github.io/synapse/latest/CAPTCHA_SETUP.html>`_, or
|
|
||||||
b. set ``enable_registration_without_verification: true`` in ``homeserver.yaml``.
|
|
||||||
|
|
||||||
We **strongly** recommend using a CAPTCHA, particularly if your homeserver is exposed to
|
|
||||||
the public internet. Without it, anyone can freely register accounts on your homeserver.
|
|
||||||
This can be exploited by attackers to create spambots targeting the rest of the Matrix
|
|
||||||
federation.
|
|
||||||
|
|
||||||
Your new Matrix ID will be formed partly from the ``server_name``, and partly
|
|
||||||
from a localpart you specify when you create the account in the form of::
|
|
||||||
|
|
||||||
@localpart:my.domain.name
|
|
||||||
|
|
||||||
(pronounced "at localpart on my dot domain dot name").
|
|
||||||
|
|
||||||
As when logging in, you will need to specify a "Custom server". Specify your
|
|
||||||
desired ``localpart`` in the 'Username' box.
|
|
||||||
|
|
||||||
🎯 Troubleshooting and support
|
🎯 Troubleshooting and support
|
||||||
==============================
|
==============================
|
||||||
@@ -182,7 +60,7 @@ Enterprise quality support for Synapse including SLAs is available as part of an
|
|||||||
`Element Server Suite (ESS) <https://element.io/pricing>`_ subscription.
|
`Element Server Suite (ESS) <https://element.io/pricing>`_ subscription.
|
||||||
|
|
||||||
If you are an existing ESS subscriber then you can raise a `support request <https://ems.element.io/support>`_
|
If you are an existing ESS subscriber then you can raise a `support request <https://ems.element.io/support>`_
|
||||||
and access the `knowledge base <https://ems-docs.element.io>`_.
|
and access the `Element product documentation <https://docs.element.io>`_.
|
||||||
|
|
||||||
🤝 Community support
|
🤝 Community support
|
||||||
--------------------
|
--------------------
|
||||||
@@ -201,35 +79,6 @@ issues for support requests, only for bug reports and feature requests.
|
|||||||
.. |docs| replace:: ``docs``
|
.. |docs| replace:: ``docs``
|
||||||
.. _docs: docs
|
.. _docs: docs
|
||||||
|
|
||||||
🪪 Identity Servers
|
|
||||||
===================
|
|
||||||
|
|
||||||
Identity servers have the job of mapping email addresses and other 3rd Party
|
|
||||||
IDs (3PIDs) to Matrix user IDs, as well as verifying the ownership of 3PIDs
|
|
||||||
before creating that mapping.
|
|
||||||
|
|
||||||
**Identity servers do not store accounts or credentials - these are stored and managed on homeservers.
|
|
||||||
Identity Servers are just for mapping 3rd Party IDs to Matrix IDs.**
|
|
||||||
|
|
||||||
This process is highly security-sensitive, as there is an obvious risk of spam if it
|
|
||||||
is too easy to sign up for Matrix accounts or harvest 3PID data. In the longer
|
|
||||||
term, we hope to create a decentralised system to manage it (`matrix-doc #712
|
|
||||||
<https://github.com/matrix-org/matrix-doc/issues/712>`_), but in the meantime,
|
|
||||||
the role of managing trusted identity in the Matrix ecosystem is farmed out to
|
|
||||||
a cluster of known trusted ecosystem partners, who run 'Matrix Identity
|
|
||||||
Servers' such as `Sydent <https://github.com/matrix-org/sydent>`_, whose role
|
|
||||||
is purely to authenticate and track 3PID logins and publish end-user public
|
|
||||||
keys.
|
|
||||||
|
|
||||||
You can host your own copy of Sydent, but this will prevent you reaching other
|
|
||||||
users in the Matrix ecosystem via their email address, and prevent them finding
|
|
||||||
you. We therefore recommend that you use one of the centralised identity servers
|
|
||||||
at ``https://matrix.org`` or ``https://vector.im`` for now.
|
|
||||||
|
|
||||||
To reiterate: the Identity server will only be used if you choose to associate
|
|
||||||
an email address with your account, or send an invite to another user via their
|
|
||||||
email address.
|
|
||||||
|
|
||||||
|
|
||||||
🛠️ Development
|
🛠️ Development
|
||||||
==============
|
==============
|
||||||
@@ -252,20 +101,29 @@ Alongside all that, join our developer community on Matrix:
|
|||||||
Copyright and Licensing
|
Copyright and Licensing
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
| Copyright 2014-2017 OpenMarket Ltd
|
| Copyright 2014–2017 OpenMarket Ltd
|
||||||
| Copyright 2017 Vector Creations Ltd
|
| Copyright 2017 Vector Creations Ltd
|
||||||
| Copyright 2017-2025 New Vector Ltd
|
| Copyright 2017–2025 New Vector Ltd
|
||||||
|
|
| Copyright 2025 Element Creations Ltd
|
||||||
|
|
||||||
This software is dual-licensed by New Vector Ltd (Element). It can be used either:
|
This software is dual-licensed by Element Creations Ltd (Element). It can be
|
||||||
|
used either:
|
||||||
|
|
||||||
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
|
(1) for free under the terms of the GNU Affero General Public License (as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version); OR
|
||||||
|
|
||||||
(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
|
(2) under the terms of a paid-for Element Commercial License agreement between
|
||||||
|
you and Element (the terms of which may vary depending on what you and
|
||||||
|
Element have agreed to).
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the
|
||||||
|
specific language governing permissions and limitations under the Licenses.
|
||||||
|
|
||||||
Please contact `licensing@element.io <mailto:licensing@element.io>`_ to purchase an Element commercial license for this software.
|
Please contact `licensing@element.io <mailto:licensing@element.io>`_ to purchase
|
||||||
|
an Element commercial license for this software.
|
||||||
|
|
||||||
|
|
||||||
.. |support| image:: https://img.shields.io/badge/matrix-community%20support-success
|
.. |support| image:: https://img.shields.io/badge/matrix-community%20support-success
|
||||||
|
|||||||
1
changelog.d/18402.misc
Normal file
1
changelog.d/18402.misc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Group together dependabot update PRs to reduce the review load.
|
||||||
1
changelog.d/19187.misc
Normal file
1
changelog.d/19187.misc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Fix `HomeServer.shutdown()` failing if the homeserver hasn't been setup yet.
|
||||||
1
changelog.d/19212.misc
Normal file
1
changelog.d/19212.misc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Respond with useful error codes with `Content-Length` header/s are invalid.
|
||||||
1
changelog.d/19232.misc
Normal file
1
changelog.d/19232.misc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Fix `HomeServer.shutdown()` failing if the homeserver failed to `start`.
|
||||||
1
changelog.d/19253.misc
Normal file
1
changelog.d/19253.misc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Raise the limit for concurrently-open non-security @dependabot PRs from 5 to 10.
|
||||||
1
changelog.d/19254.removal
Normal file
1
changelog.d/19254.removal
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Remove the "Updates to locked dependencies" section from the changelog due to lack of use and the maintenance burden.
|
||||||
1
changelog.d/19258.misc
Normal file
1
changelog.d/19258.misc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Require 14 days to pass before pulling in general dependency updates to help mitigate upstream supply chain attacks.
|
||||||
1
changelog.d/19262.misc
Normal file
1
changelog.d/19262.misc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Drop the broken netlify documentation workflow until a new one is implemented.
|
||||||
1
changelog.d/19267.bugfix
Normal file
1
changelog.d/19267.bugfix
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Fix bug where `Duration` was logged incorrectly.
|
||||||
1
changelog.d/19278.misc
Normal file
1
changelog.d/19278.misc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Don't include debug logs in `Clock` unless explicitly enabled.
|
||||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@@ -1,3 +1,9 @@
|
|||||||
|
matrix-synapse-py3 (1.144.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.144.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 02 Dec 2025 09:11:19 -0700
|
||||||
|
|
||||||
matrix-synapse-py3 (1.143.0) stable; urgency=medium
|
matrix-synapse-py3 (1.143.0) stable; urgency=medium
|
||||||
|
|
||||||
* New Synapse release 1.143.0.
|
* New Synapse release 1.143.0.
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ WORKERS_CONFIG: dict[str, dict[str, Any]] = {
|
|||||||
"^/_matrix/client/(api/v1|r0|v3|unstable)/keys/upload",
|
"^/_matrix/client/(api/v1|r0|v3|unstable)/keys/upload",
|
||||||
"^/_matrix/client/(api/v1|r0|v3|unstable)/keys/device_signing/upload$",
|
"^/_matrix/client/(api/v1|r0|v3|unstable)/keys/device_signing/upload$",
|
||||||
"^/_matrix/client/(api/v1|r0|v3|unstable)/keys/signatures/upload$",
|
"^/_matrix/client/(api/v1|r0|v3|unstable)/keys/signatures/upload$",
|
||||||
|
"^/_matrix/client/unstable/org.matrix.msc4140/delayed_events(/.*/restart)?$",
|
||||||
],
|
],
|
||||||
"shared_extra_conf": {},
|
"shared_extra_conf": {},
|
||||||
"worker_extra_conf": "",
|
"worker_extra_conf": "",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
- [Installation](setup/installation.md)
|
- [Installation](setup/installation.md)
|
||||||
|
- [Security](setup/security.md)
|
||||||
- [Using Postgres](postgres.md)
|
- [Using Postgres](postgres.md)
|
||||||
- [Configuring a Reverse Proxy](reverse_proxy.md)
|
- [Configuring a Reverse Proxy](reverse_proxy.md)
|
||||||
- [Configuring a Forward/Outbound Proxy](setup/forward_proxy.md)
|
- [Configuring a Forward/Outbound Proxy](setup/forward_proxy.md)
|
||||||
|
|||||||
@@ -16,8 +16,15 @@ that your email address is probably `user@example.com` rather than
|
|||||||
`user@email.example.com`) - but doing so may require more advanced setup: see
|
`user@email.example.com`) - but doing so may require more advanced setup: see
|
||||||
[Setting up Federation](../federate.md).
|
[Setting up Federation](../federate.md).
|
||||||
|
|
||||||
|
⚠️ Before setting up Synapse please consult the [security page](security.md) for
|
||||||
|
best practices. ⚠️
|
||||||
|
|
||||||
## Installing Synapse
|
## Installing Synapse
|
||||||
|
|
||||||
|
Note: Synapse uses a number of platform dependencies such as Python and PostgreSQL,
|
||||||
|
and aims to follow supported upstream versions. See the [deprecation
|
||||||
|
policy](../deprecation_policy.md) for more details.
|
||||||
|
|
||||||
### Prebuilt packages
|
### Prebuilt packages
|
||||||
|
|
||||||
Prebuilt packages are available for a number of platforms. These are recommended
|
Prebuilt packages are available for a number of platforms. These are recommended
|
||||||
|
|||||||
41
docs/setup/security.md
Normal file
41
docs/setup/security.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Security
|
||||||
|
|
||||||
|
This page lays out security best-practices when running Synapse.
|
||||||
|
|
||||||
|
If you believe you have encountered a security issue, see our [Security
|
||||||
|
Disclosure Policy](https://element.io/en/security/security-disclosure-policy).
|
||||||
|
|
||||||
|
## Content repository
|
||||||
|
|
||||||
|
Matrix serves raw, user-supplied data in some APIs — specifically the [content
|
||||||
|
repository endpoints](https://matrix.org/docs/spec/client_server/latest.html#get-matrix-media-r0-download-servername-mediaid).
|
||||||
|
|
||||||
|
Whilst we make a reasonable effort to mitigate against XSS attacks (for
|
||||||
|
instance, by using [CSP](https://github.com/matrix-org/synapse/pull/1021)), a
|
||||||
|
Matrix homeserver should not be hosted on a domain hosting other web
|
||||||
|
applications. This especially applies to sharing the domain with Matrix web
|
||||||
|
clients and other sensitive applications like webmail. See
|
||||||
|
https://developer.github.com/changes/2014-04-25-user-content-security for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
Ideally, the homeserver should not simply be on a different subdomain, but on a
|
||||||
|
completely different [registered
|
||||||
|
domain](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-2.3)
|
||||||
|
(also known as top-level site or eTLD+1). This is because [some
|
||||||
|
attacks](https://en.wikipedia.org/wiki/Session_fixation#Attacks_using_cross-subdomain_cookie)
|
||||||
|
are still possible as long as the two applications share the same registered
|
||||||
|
domain.
|
||||||
|
|
||||||
|
|
||||||
|
To illustrate this with an example, if your Element Web or other sensitive web
|
||||||
|
application is hosted on `A.example1.com`, you should ideally host Synapse on
|
||||||
|
`example2.com`. Some amount of protection is offered by hosting on
|
||||||
|
`B.example1.com` instead, so this is also acceptable in some scenarios.
|
||||||
|
However, you should *not* host your Synapse on `A.example1.com`.
|
||||||
|
|
||||||
|
Note that all of the above refers exclusively to the domain used in Synapse's
|
||||||
|
`public_baseurl` setting. In particular, it has no bearing on the domain
|
||||||
|
mentioned in MXIDs hosted on that server.
|
||||||
|
|
||||||
|
Following this advice ensures that even if an XSS is found in Synapse, the
|
||||||
|
impact to other applications will be minimal.
|
||||||
@@ -117,6 +117,25 @@ each upgrade are complete before moving on to the next upgrade, to avoid
|
|||||||
stacking them up. You can monitor the currently running background updates with
|
stacking them up. You can monitor the currently running background updates with
|
||||||
[the Admin API](usage/administration/admin_api/background_updates.html#status).
|
[the Admin API](usage/administration/admin_api/background_updates.html#status).
|
||||||
|
|
||||||
|
# Upgrading to v1.144.0
|
||||||
|
|
||||||
|
## Worker support for unstable MSC4140 `/restart` endpoint
|
||||||
|
|
||||||
|
The following unstable endpoint pattern may now be routed to worker processes:
|
||||||
|
|
||||||
|
```
|
||||||
|
^/_matrix/client/unstable/org.matrix.msc4140/delayed_events/.*/restart$
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unstable mutual rooms endpoint is now behind an experimental feature flag
|
||||||
|
|
||||||
|
The unstable mutual rooms endpoint from
|
||||||
|
[MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
|
||||||
|
(`/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`) is now
|
||||||
|
disabled by default. If you rely on this unstable endpoint, you must now set
|
||||||
|
`experimental_features.msc2666_enabled: true` in your configuration to keep
|
||||||
|
using it.
|
||||||
|
|
||||||
# Upgrading to v1.143.0
|
# Upgrading to v1.143.0
|
||||||
|
|
||||||
## Dropping support for PostgreSQL 13
|
## Dropping support for PostgreSQL 13
|
||||||
|
|||||||
@@ -285,10 +285,13 @@ information.
|
|||||||
# User directory search requests
|
# User directory search requests
|
||||||
^/_matrix/client/(r0|v3|unstable)/user_directory/search$
|
^/_matrix/client/(r0|v3|unstable)/user_directory/search$
|
||||||
|
|
||||||
|
# Unstable MSC4140 support
|
||||||
|
^/_matrix/client/unstable/org.matrix.msc4140/delayed_events(/.*/restart)?$
|
||||||
|
|
||||||
Additionally, the following REST endpoints can be handled for GET requests:
|
Additionally, the following REST endpoints can be handled for GET requests:
|
||||||
|
|
||||||
|
# Push rules requests
|
||||||
^/_matrix/client/(api/v1|r0|v3|unstable)/pushrules/
|
^/_matrix/client/(api/v1|r0|v3|unstable)/pushrules/
|
||||||
^/_matrix/client/unstable/org.matrix.msc4140/delayed_events
|
|
||||||
|
|
||||||
# Account data requests
|
# Account data requests
|
||||||
^/_matrix/client/(r0|v3|unstable)/.*/tags
|
^/_matrix/client/(r0|v3|unstable)/.*/tags
|
||||||
|
|||||||
766
poetry.lock
generated
766
poetry.lock
generated
@@ -14,24 +14,16 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "attrs"
|
name = "attrs"
|
||||||
version = "25.3.0"
|
version = "25.4.0"
|
||||||
description = "Classes Without Boilerplate"
|
description = "Classes Without Boilerplate"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.9"
|
||||||
groups = ["main", "dev"]
|
groups = ["main", "dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"},
|
{file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"},
|
||||||
{file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"},
|
{file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
|
|
||||||
cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
|
|
||||||
dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
|
|
||||||
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"]
|
|
||||||
tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
|
|
||||||
tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "authlib"
|
name = "authlib"
|
||||||
version = "1.6.5"
|
version = "1.6.5"
|
||||||
@@ -142,14 +134,14 @@ typecheck = ["mypy"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bleach"
|
name = "bleach"
|
||||||
version = "6.2.0"
|
version = "6.3.0"
|
||||||
description = "An easy safelist-based HTML-sanitizing tool."
|
description = "An easy safelist-based HTML-sanitizing tool."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.10"
|
||||||
groups = ["main", "dev"]
|
groups = ["main", "dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e"},
|
{file = "bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6"},
|
||||||
{file = "bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f"},
|
{file = "bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -184,83 +176,100 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cffi"
|
name = "cffi"
|
||||||
version = "1.17.1"
|
version = "2.0.0"
|
||||||
description = "Foreign Function Interface for Python calling C code."
|
description = "Foreign Function Interface for Python calling C code."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.9"
|
||||||
groups = ["main", "dev"]
|
groups = ["main", "dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
|
{file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
|
{file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"},
|
{file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"},
|
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"},
|
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"},
|
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"},
|
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"},
|
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"},
|
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"},
|
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"},
|
{file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"},
|
||||||
{file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"},
|
{file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"},
|
{file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"},
|
{file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"},
|
{file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"},
|
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"},
|
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"},
|
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"},
|
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"},
|
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"},
|
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"},
|
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"},
|
{file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"},
|
||||||
{file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"},
|
{file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"},
|
{file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"},
|
{file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"},
|
{file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"},
|
{file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"},
|
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"},
|
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"},
|
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"},
|
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"},
|
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"},
|
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"},
|
||||||
{file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"},
|
{file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"},
|
{file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"},
|
{file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"},
|
{file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"},
|
{file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"},
|
{file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"},
|
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"},
|
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"},
|
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"},
|
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"},
|
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"},
|
||||||
{file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"},
|
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"},
|
||||||
{file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"},
|
{file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"},
|
||||||
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"},
|
{file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"},
|
||||||
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"},
|
{file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"},
|
||||||
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"},
|
{file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"},
|
||||||
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"},
|
{file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"},
|
||||||
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"},
|
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"},
|
||||||
{file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"},
|
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"},
|
||||||
{file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"},
|
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"},
|
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"},
|
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"},
|
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"},
|
{file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"},
|
{file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"},
|
{file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"},
|
{file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"},
|
{file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"},
|
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"},
|
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"},
|
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"},
|
||||||
{file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
|
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"},
|
||||||
{file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
|
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"},
|
||||||
|
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"},
|
||||||
|
{file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"},
|
||||||
|
{file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"},
|
||||||
|
{file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"},
|
||||||
|
{file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"},
|
||||||
|
{file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pycparser = "*"
|
pycparser = {version = "*", markers = "implementation_name != \"PyPy\""}
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "charset-normalizer"
|
name = "charset-normalizer"
|
||||||
@@ -389,62 +398,80 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cryptography"
|
name = "cryptography"
|
||||||
version = "45.0.7"
|
version = "46.0.3"
|
||||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.9.0,!=3.9.1,>=3.7"
|
python-versions = "!=3.9.0,!=3.9.1,>=3.8"
|
||||||
groups = ["main", "dev"]
|
groups = ["main", "dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee"},
|
{file = "cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6"},
|
{file = "cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339"},
|
{file = "cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8"},
|
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf"},
|
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513"},
|
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3"},
|
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3"},
|
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6"},
|
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd"},
|
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-win32.whl", hash = "sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8"},
|
{file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71"},
|
||||||
{file = "cryptography-45.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443"},
|
{file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2"},
|
{file = "cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691"},
|
{file = "cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59"},
|
{file = "cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4"},
|
{file = "cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3"},
|
{file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1"},
|
{file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27"},
|
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17"},
|
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b"},
|
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c"},
|
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-win32.whl", hash = "sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5"},
|
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044"},
|
||||||
{file = "cryptography-45.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90"},
|
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665"},
|
||||||
{file = "cryptography-45.0.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:de58755d723e86175756f463f2f0bddd45cc36fbd62601228a3f8761c9f58252"},
|
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3"},
|
||||||
{file = "cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a20e442e917889d1a6b3c570c9e3fa2fdc398c20868abcea268ea33c024c4083"},
|
{file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20"},
|
||||||
{file = "cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:258e0dff86d1d891169b5af222d362468a9570e2532923088658aa866eb11130"},
|
{file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de"},
|
||||||
{file = "cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d97cf502abe2ab9eff8bd5e4aca274da8d06dd3ef08b759a8d6143f4ad65d4b4"},
|
{file = "cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914"},
|
||||||
{file = "cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:c987dad82e8c65ebc985f5dae5e74a3beda9d0a2a4daf8a1115f3772b59e5141"},
|
{file = "cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db"},
|
||||||
{file = "cryptography-45.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c13b1e3afd29a5b3b2656257f14669ca8fa8d7956d509926f0b130b600b50ab7"},
|
{file = "cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21"},
|
||||||
{file = "cryptography-45.0.7-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde"},
|
{file = "cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936"},
|
||||||
{file = "cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34"},
|
{file = "cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683"},
|
||||||
{file = "cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9"},
|
{file = "cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d"},
|
||||||
{file = "cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae"},
|
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0"},
|
||||||
{file = "cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b"},
|
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc"},
|
||||||
{file = "cryptography-45.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63"},
|
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3"},
|
||||||
{file = "cryptography-45.0.7.tar.gz", hash = "sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971"},
|
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971"},
|
||||||
|
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac"},
|
||||||
|
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04"},
|
||||||
|
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506"},
|
||||||
|
{file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963"},
|
||||||
|
{file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4"},
|
||||||
|
{file = "cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df"},
|
||||||
|
{file = "cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f"},
|
||||||
|
{file = "cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372"},
|
||||||
|
{file = "cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32"},
|
||||||
|
{file = "cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c"},
|
||||||
|
{file = "cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea"},
|
||||||
|
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b"},
|
||||||
|
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb"},
|
||||||
|
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717"},
|
||||||
|
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9"},
|
||||||
|
{file = "cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c"},
|
||||||
|
{file = "cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
cffi = {version = ">=1.14", markers = "platform_python_implementation != \"PyPy\""}
|
cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""}
|
||||||
|
typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""}
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs ; python_full_version >= \"3.8.0\"", "sphinx-rtd-theme (>=3.0.0) ; python_full_version >= \"3.8.0\""]
|
docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"]
|
||||||
docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"]
|
docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"]
|
||||||
nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_full_version >= \"3.8.0\""]
|
nox = ["nox[uv] (>=2024.4.15)"]
|
||||||
pep8test = ["check-sdist ; python_full_version >= \"3.8.0\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"]
|
pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"]
|
||||||
sdist = ["build (>=1.0.0)"]
|
sdist = ["build (>=1.0.0)"]
|
||||||
ssh = ["bcrypt (>=3.1.5)"]
|
ssh = ["bcrypt (>=3.1.5)"]
|
||||||
test = ["certifi (>=2024)", "cryptography-vectors (==45.0.7)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
|
test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
|
||||||
test-randomorder = ["pytest-randomly"]
|
test-randomorder = ["pytest-randomly"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -999,6 +1026,92 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pyasn1 = ">=0.4.6"
|
pyasn1 = ">=0.4.6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "librt"
|
||||||
|
version = "0.6.3"
|
||||||
|
description = "Mypyc runtime library"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45660d26569cc22ed30adf583389d8a0d1b468f8b5e518fcf9bfe2cd298f9dd1"},
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:54f3b2177fb892d47f8016f1087d21654b44f7fc4cf6571c1c6b3ea531ab0fcf"},
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c5b31bed2c2f2fa1fcb4815b75f931121ae210dc89a3d607fb1725f5907f1437"},
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f8ed5053ef9fb08d34f1fd80ff093ccbd1f67f147633a84cf4a7d9b09c0f089"},
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3f0e4bd9bcb0ee34fa3dbedb05570da50b285f49e52c07a241da967840432513"},
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8f89c8d20dfa648a3f0a56861946eb00e5b00d6b00eea14bc5532b2fcfa8ef1"},
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecc2c526547eacd20cb9fbba19a5268611dbc70c346499656d6cf30fae328977"},
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fbedeb9b48614d662822ee514567d2d49a8012037fc7b4cd63f282642c2f4b7d"},
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-win32.whl", hash = "sha256:0765b0fe0927d189ee14b087cd595ae636bef04992e03fe6dfdaa383866c8a46"},
|
||||||
|
{file = "librt-0.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:8c659f9fb8a2f16dc4131b803fa0144c1dadcb3ab24bb7914d01a6da58ae2457"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:61348cc488b18d1b1ff9f3e5fcd5ac43ed22d3e13e862489d2267c2337285c08"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64645b757d617ad5f98c08e07620bc488d4bced9ced91c6279cec418f16056fa"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:26b8026393920320bb9a811b691d73c5981385d537ffc5b6e22e53f7b65d4122"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d998b432ed9ffccc49b820e913c8f327a82026349e9c34fa3690116f6b70770f"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e18875e17ef69ba7dfa9623f2f95f3eda6f70b536079ee6d5763ecdfe6cc9040"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a218f85081fc3f70cddaed694323a1ad7db5ca028c379c214e3a7c11c0850523"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1ef42ff4edd369e84433ce9b188a64df0837f4f69e3d34d3b34d4955c599d03f"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e0f2b79993fec23a685b3e8107ba5f8675eeae286675a216da0b09574fa1e47"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-win32.whl", hash = "sha256:fd98cacf4e0fabcd4005c452cb8a31750258a85cab9a59fb3559e8078da408d7"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:e17b5b42c8045867ca9d1f54af00cc2275198d38de18545edaa7833d7e9e4ac8"},
|
||||||
|
{file = "librt-0.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:87597e3d57ec0120a3e1d857a708f80c02c42ea6b00227c728efbc860f067c45"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74418f718083009108dc9a42c21bf2e4802d49638a1249e13677585fcc9ca176"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:514f3f363d1ebc423357d36222c37e5c8e6674b6eae8d7195ac9a64903722057"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cf1115207a5049d1f4b7b4b72de0e52f228d6c696803d94843907111cbf80610"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad8ba80cdcea04bea7b78fcd4925bfbf408961e9d8397d2ee5d3ec121e20c08c"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4018904c83eab49c814e2494b4e22501a93cdb6c9f9425533fe693c3117126f9"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8983c5c06ac9c990eac5eb97a9f03fe41dc7e9d7993df74d9e8682a1056f596c"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7769c579663a6f8dbf34878969ac71befa42067ce6bf78e6370bf0d1194997c"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d3c9a07eafdc70556f8c220da4a538e715668c0c63cabcc436a026e4e89950bf"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-win32.whl", hash = "sha256:38320386a48a15033da295df276aea93a92dfa94a862e06893f75ea1d8bbe89d"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:c0ecf4786ad0404b072196b5df774b1bb23c8aacdcacb6c10b4128bc7b00bd01"},
|
||||||
|
{file = "librt-0.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:9f2a6623057989ebc469cd9cc8fe436c40117a0147627568d03f84aef7854c55"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9e716f9012148a81f02f46a04fc4c663420c6fbfeacfac0b5e128cf43b4413d3"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:669ff2495728009a96339c5ad2612569c6d8be4474e68f3f3ac85d7c3261f5f5"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:349b6873ebccfc24c9efd244e49da9f8a5c10f60f07575e248921aae2123fc42"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c74c26736008481c9f6d0adf1aedb5a52aff7361fea98276d1f965c0256ee70"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:408a36ddc75e91918cb15b03460bdc8a015885025d67e68c6f78f08c3a88f522"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e61ab234624c9ffca0248a707feffe6fac2343758a36725d8eb8a6efef0f8c30"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:324462fe7e3896d592b967196512491ec60ca6e49c446fe59f40743d08c97917"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36b2ec8c15030002c7f688b4863e7be42820d7c62d9c6eece3db54a2400f0530"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-win32.whl", hash = "sha256:25b1b60cb059471c0c0c803e07d0dfdc79e41a0a122f288b819219ed162672a3"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:10a95ad074e2a98c9e4abc7f5b7d40e5ecbfa84c04c6ab8a70fabf59bd429b88"},
|
||||||
|
{file = "librt-0.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:17000df14f552e86877d67e4ab7966912224efc9368e998c96a6974a8d609bf9"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8e695f25d1a425ad7a272902af8ab8c8d66c1998b177e4b5f5e7b4e215d0c88a"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3e84a4121a7ae360ca4da436548a9c1ca8ca134a5ced76c893cc5944426164bd"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:05f385a414de3f950886ea0aad8f109650d4b712cf9cc14cc17f5f62a9ab240b"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36a8e337461150b05ca2c7bdedb9e591dfc262c5230422cea398e89d0c746cdc"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcbe48f6a03979384f27086484dc2a14959be1613cb173458bd58f714f2c48f3"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4bca9e4c260233fba37b15c4ec2f78aa99c1a79fbf902d19dd4a763c5c3fb751"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:760c25ed6ac968e24803eb5f7deb17ce026902d39865e83036bacbf5cf242aa8"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4aa4a93a353ccff20df6e34fa855ae8fd788832c88f40a9070e3ddd3356a9f0e"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-win32.whl", hash = "sha256:cb92741c2b4ea63c09609b064b26f7f5d9032b61ae222558c55832ec3ad0bcaf"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:fdcd095b1b812d756fa5452aca93b962cf620694c0cadb192cec2bb77dcca9a2"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:822ca79e28720a76a935c228d37da6579edef048a17cd98d406a2484d10eda78"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:078cd77064d1640cb7b0650871a772956066174d92c8aeda188a489b58495179"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5cc22f7f5c0cc50ed69f4b15b9c51d602aabc4500b433aaa2ddd29e578f452f7"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:14b345eb7afb61b9fdcdfda6738946bd11b8e0f6be258666b0646af3b9bb5916"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d46aa46aa29b067f0b8b84f448fd9719aaf5f4c621cc279164d76a9dc9ab3e8"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1b51ba7d9d5d9001494769eca8c0988adce25d0a970c3ba3f2eb9df9d08036fc"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ced0925a18fddcff289ef54386b2fc230c5af3c83b11558571124bfc485b8c07"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6bac97e51f66da2ca012adddbe9fd656b17f7368d439de30898f24b39512f40f"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b2922a0e8fa97395553c304edc3bd36168d8eeec26b92478e292e5d4445c1ef0"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-win32.whl", hash = "sha256:f33462b19503ba68d80dac8a1354402675849259fb3ebf53b67de86421735a3a"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:04f8ce401d4f6380cfc42af0f4e67342bf34c820dae01343f58f472dbac75dcf"},
|
||||||
|
{file = "librt-0.6.3-cp314-cp314t-win_arm64.whl", hash = "sha256:afb39550205cc5e5c935762c6bf6a2bb34f7d21a68eadb25e2db7bf3593fecc0"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09262cb2445b6f15d09141af20b95bb7030c6f13b00e876ad8fdd1a9045d6aa5"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57705e8eec76c5b77130d729c0f70190a9773366c555c5457c51eace80afd873"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3ac2a7835434b31def8ed5355dd9b895bbf41642d61967522646d1d8b9681106"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71f0a5918aebbea1e7db2179a8fe87e8a8732340d9e8b8107401fb407eda446e"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa346e202e6e1ebc01fe1c69509cffe486425884b96cb9ce155c99da1ecbe0e9"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:92267f865c7bbd12327a0d394666948b9bf4b51308b52947c0cc453bfa812f5d"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:86605d5bac340beb030cbc35859325982a79047ebdfba1e553719c7126a2389d"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:98e4bbecbef8d2a60ecf731d735602feee5ac0b32117dbbc765e28b054bac912"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-win32.whl", hash = "sha256:3caa0634c02d5ff0b2ae4a28052e0d8c5f20d497623dc13f629bd4a9e2a6efad"},
|
||||||
|
{file = "librt-0.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:b47395091e7e0ece1e6ebac9b98bf0c9084d1e3d3b2739aa566be7e56e3f7bf2"},
|
||||||
|
{file = "librt-0.6.3.tar.gz", hash = "sha256:c724a884e642aa2bbad52bb0203ea40406ad742368a5f90da1b220e970384aae"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lxml"
|
name = "lxml"
|
||||||
version = "6.0.2"
|
version = "6.0.2"
|
||||||
@@ -1421,53 +1534,54 @@ docs = ["sphinx (>=8,<9)", "sphinx-autobuild"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy"
|
name = "mypy"
|
||||||
version = "1.17.1"
|
version = "1.19.0"
|
||||||
description = "Optional static typing for Python"
|
description = "Optional static typing for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972"},
|
{file = "mypy-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6148ede033982a8c5ca1143de34c71836a09f105068aaa8b7d5edab2b053e6c8"},
|
||||||
{file = "mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7"},
|
{file = "mypy-1.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a9ac09e52bb0f7fb912f5d2a783345c72441a08ef56ce3e17c1752af36340a39"},
|
||||||
{file = "mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df"},
|
{file = "mypy-1.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f7254c15ab3f8ed68f8e8f5cbe88757848df793e31c36aaa4d4f9783fd08ab"},
|
||||||
{file = "mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390"},
|
{file = "mypy-1.19.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318ba74f75899b0e78b847d8c50821e4c9637c79d9a59680fc1259f29338cb3e"},
|
||||||
{file = "mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94"},
|
{file = "mypy-1.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf7d84f497f78b682edd407f14a7b6e1a2212b433eedb054e2081380b7395aa3"},
|
||||||
{file = "mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b"},
|
{file = "mypy-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:c3385246593ac2b97f155a0e9639be906e73534630f663747c71908dfbf26134"},
|
||||||
{file = "mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58"},
|
{file = "mypy-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a31e4c28e8ddb042c84c5e977e28a21195d086aaffaf08b016b78e19c9ef8106"},
|
||||||
{file = "mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5"},
|
{file = "mypy-1.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34ec1ac66d31644f194b7c163d7f8b8434f1b49719d403a5d26c87fff7e913f7"},
|
||||||
{file = "mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd"},
|
{file = "mypy-1.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb64b0ba5980466a0f3f9990d1c582bcab8db12e29815ecb57f1408d99b4bff7"},
|
||||||
{file = "mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b"},
|
{file = "mypy-1.19.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:120cffe120cca5c23c03c77f84abc0c14c5d2e03736f6c312480020082f1994b"},
|
||||||
{file = "mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5"},
|
{file = "mypy-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a500ab5c444268a70565e374fc803972bfd1f09545b13418a5174e29883dab7"},
|
||||||
{file = "mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b"},
|
{file = "mypy-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:c14a98bc63fd867530e8ec82f217dae29d0550c86e70debc9667fff1ec83284e"},
|
||||||
{file = "mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb"},
|
{file = "mypy-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fb3115cb8fa7c5f887c8a8d81ccdcb94cff334684980d847e5a62e926910e1d"},
|
||||||
{file = "mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403"},
|
{file = "mypy-1.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3e19e3b897562276bb331074d64c076dbdd3e79213f36eed4e592272dabd760"},
|
||||||
{file = "mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056"},
|
{file = "mypy-1.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9d491295825182fba01b6ffe2c6fe4e5a49dbf4e2bb4d1217b6ced3b4797bc6"},
|
||||||
{file = "mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341"},
|
{file = "mypy-1.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2"},
|
||||||
{file = "mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb"},
|
{file = "mypy-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431"},
|
||||||
{file = "mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19"},
|
{file = "mypy-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:0e3c3d1e1d62e678c339e7ade72746a9e0325de42cd2cccc51616c7b2ed1a018"},
|
||||||
{file = "mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7"},
|
{file = "mypy-1.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7686ed65dbabd24d20066f3115018d2dce030d8fa9db01aa9f0a59b6813e9f9e"},
|
||||||
{file = "mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81"},
|
{file = "mypy-1.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd4a985b2e32f23bead72e2fb4bbe5d6aceee176be471243bd831d5b2644672d"},
|
||||||
{file = "mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6"},
|
{file = "mypy-1.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc51a5b864f73a3a182584b1ac75c404396a17eced54341629d8bdcb644a5bba"},
|
||||||
{file = "mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849"},
|
{file = "mypy-1.19.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37af5166f9475872034b56c5efdcf65ee25394e9e1d172907b84577120714364"},
|
||||||
{file = "mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14"},
|
{file = "mypy-1.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:510c014b722308c9bd377993bcbf9a07d7e0692e5fa8fc70e639c1eb19fc6bee"},
|
||||||
{file = "mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a"},
|
{file = "mypy-1.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:cabbee74f29aa9cd3b444ec2f1e4fa5a9d0d746ce7567a6a609e224429781f53"},
|
||||||
{file = "mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733"},
|
{file = "mypy-1.19.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f2e36bed3c6d9b5f35d28b63ca4b727cb0228e480826ffc8953d1892ddc8999d"},
|
||||||
{file = "mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd"},
|
{file = "mypy-1.19.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a18d8abdda14035c5718acb748faec09571432811af129bf0d9e7b2d6699bf18"},
|
||||||
{file = "mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0"},
|
{file = "mypy-1.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75e60aca3723a23511948539b0d7ed514dda194bc3755eae0bfc7a6b4887aa7"},
|
||||||
{file = "mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a"},
|
{file = "mypy-1.19.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f44f2ae3c58421ee05fe609160343c25f70e3967f6e32792b5a78006a9d850f"},
|
||||||
{file = "mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91"},
|
{file = "mypy-1.19.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63ea6a00e4bd6822adbfc75b02ab3653a17c02c4347f5bb0cf1d5b9df3a05835"},
|
||||||
{file = "mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed"},
|
{file = "mypy-1.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ad925b14a0bb99821ff6f734553294aa6a3440a8cb082fe1f5b84dfb662afb1"},
|
||||||
{file = "mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9"},
|
{file = "mypy-1.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0dde5cb375cb94deff0d4b548b993bec52859d1651e073d63a1386d392a95495"},
|
||||||
{file = "mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99"},
|
{file = "mypy-1.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1cf9c59398db1c68a134b0b5354a09a1e124523f00bacd68e553b8bd16ff3299"},
|
||||||
{file = "mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8"},
|
{file = "mypy-1.19.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3210d87b30e6af9c8faed61be2642fcbe60ef77cec64fa1ef810a630a4cf671c"},
|
||||||
{file = "mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8"},
|
{file = "mypy-1.19.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2c1101ab41d01303103ab6ef82cbbfedb81c1a060c868fa7cc013d573d37ab5"},
|
||||||
{file = "mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259"},
|
{file = "mypy-1.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ea4fd21bb48f0da49e6d3b37ef6bd7e8228b9fe41bbf4d80d9364d11adbd43c"},
|
||||||
{file = "mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d"},
|
{file = "mypy-1.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:16f76ff3f3fd8137aadf593cb4607d82634fca675e8211ad75c43d86033ee6c6"},
|
||||||
{file = "mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9"},
|
{file = "mypy-1.19.0-py3-none-any.whl", hash = "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9"},
|
||||||
{file = "mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01"},
|
{file = "mypy-1.19.0.tar.gz", hash = "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
librt = ">=0.6.2"
|
||||||
mypy_extensions = ">=1.0.0"
|
mypy_extensions = ">=1.0.0"
|
||||||
pathspec = ">=0.9.0"
|
pathspec = ">=0.9.0"
|
||||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||||
@@ -1494,18 +1608,18 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy-zope"
|
name = "mypy-zope"
|
||||||
version = "1.0.13"
|
version = "1.0.14"
|
||||||
description = "Plugin for mypy to support zope interfaces"
|
description = "Plugin for mypy to support zope interfaces"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "mypy_zope-1.0.13-py3-none-any.whl", hash = "sha256:13740c4cbc910cca2c143c6709e1c483c991abeeeb7b629ad6f73d8ac1edad15"},
|
{file = "mypy_zope-1.0.14-py3-none-any.whl", hash = "sha256:8842ade93630421dbec0c9906d6515f6e65c6407ef8b9b2eb7f4f73ae1e8a42a"},
|
||||||
{file = "mypy_zope-1.0.13.tar.gz", hash = "sha256:63fb4d035ea874baf280dc69e714dcde4bd2a4a4837a0fd8d90ce91bea510f99"},
|
{file = "mypy_zope-1.0.14.tar.gz", hash = "sha256:42555ad4703f2e50c912de3ebe0c7197619c3f71864817fabc5385ecea0f8449"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
mypy = ">=1.0.0,<1.18.0"
|
mypy = ">=1.0.0,<1.20.0"
|
||||||
"zope.interface" = "*"
|
"zope.interface" = "*"
|
||||||
"zope.schema" = "*"
|
"zope.schema" = "*"
|
||||||
|
|
||||||
@@ -1583,14 +1697,14 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phonenumbers"
|
name = "phonenumbers"
|
||||||
version = "9.0.18"
|
version = "9.0.19"
|
||||||
description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
|
description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "phonenumbers-9.0.18-py2.py3-none-any.whl", hash = "sha256:d3354454ac31c97f8a08121df97a7145b8dca641f734c6f1518a41c2f60c5764"},
|
{file = "phonenumbers-9.0.19-py2.py3-none-any.whl", hash = "sha256:004abdfe2010518c2383f148515664a742e8a5d5540e07c049735c139d7e8b09"},
|
||||||
{file = "phonenumbers-9.0.18.tar.gz", hash = "sha256:5537c61ba95b11b992c95e804da6e49193cc06b1224f632ade64631518a48ed1"},
|
{file = "phonenumbers-9.0.19.tar.gz", hash = "sha256:e0674e31554362f4d95383558f7aefde738ef2e7bf96d28a10afd3e87d63a65c"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1800,6 +1914,7 @@ description = "C parser in Python"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
groups = ["main", "dev"]
|
groups = ["main", "dev"]
|
||||||
|
markers = "implementation_name != \"PyPy\""
|
||||||
files = [
|
files = [
|
||||||
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
|
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
|
||||||
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
|
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
|
||||||
@@ -1807,14 +1922,14 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic"
|
name = "pydantic"
|
||||||
version = "2.12.4"
|
version = "2.12.5"
|
||||||
description = "Data validation using Python type hints"
|
description = "Data validation using Python type hints"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e"},
|
{file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"},
|
||||||
{file = "pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac"},
|
{file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -2074,18 +2189,18 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyopenssl"
|
name = "pyopenssl"
|
||||||
version = "25.1.0"
|
version = "25.3.0"
|
||||||
description = "Python wrapper module around the OpenSSL library"
|
description = "Python wrapper module around the OpenSSL library"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab"},
|
{file = "pyopenssl-25.3.0-py3-none-any.whl", hash = "sha256:1fda6fc034d5e3d179d39e59c1895c9faeaf40a79de5fc4cbbfbe0d36f4a77b6"},
|
||||||
{file = "pyopenssl-25.1.0.tar.gz", hash = "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b"},
|
{file = "pyopenssl-25.3.0.tar.gz", hash = "sha256:c981cb0a3fd84e8602d7afc209522773b94c1c2446a3c710a75b06fe1beae329"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
cryptography = ">=41.0.5,<46"
|
cryptography = ">=45.0.7,<47"
|
||||||
typing-extensions = {version = ">=4.9", markers = "python_version < \"3.13\" and python_version >= \"3.8\""}
|
typing-extensions = {version = ">=4.9", markers = "python_version < \"3.13\" and python_version >= \"3.8\""}
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
@@ -2147,15 +2262,15 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytz"
|
name = "pytz"
|
||||||
version = "2022.7.1"
|
version = "2025.2"
|
||||||
description = "World timezone definitions, modern and historical"
|
description = "World timezone definitions, modern and historical"
|
||||||
optional = true
|
optional = true
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
markers = "extra == \"all\" or extra == \"saml2\""
|
markers = "extra == \"all\" or extra == \"saml2\""
|
||||||
files = [
|
files = [
|
||||||
{file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"},
|
{file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"},
|
||||||
{file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"},
|
{file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2364,127 +2479,127 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rpds-py"
|
name = "rpds-py"
|
||||||
version = "0.28.0"
|
version = "0.30.0"
|
||||||
description = "Python bindings to Rust's persistent data structures (rpds)"
|
description = "Python bindings to Rust's persistent data structures (rpds)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
groups = ["main", "dev"]
|
groups = ["main", "dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7b6013db815417eeb56b2d9d7324e64fcd4fa289caeee6e7a78b2e11fc9b438a"},
|
{file = "rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a4c6b05c685c0c03f80dabaeb73e74218c49deea965ca63f76a752807397207"},
|
{file = "rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4794c6c3fbe8f9ac87699b131a1f26e7b4abcf6d828da46a3a52648c7930eba"},
|
{file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2e8456b6ee5527112ff2354dd9087b030e3429e43a74f480d4a5ca79d269fd85"},
|
{file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:beb880a9ca0a117415f241f66d56025c02037f7c4efc6fe59b5b8454f1eaa50d"},
|
{file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6897bebb118c44b38c9cb62a178e09f1593c949391b9a1a6fe777ccab5934ee7"},
|
{file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b553dd06e875249fd43efd727785efb57a53180e0fde321468222eabbeaafa"},
|
{file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:f0b2044fdddeea5b05df832e50d2a06fe61023acb44d76978e1b060206a8a476"},
|
{file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05cf1e74900e8da73fa08cc76c74a03345e5a3e37691d07cfe2092d7d8e27b04"},
|
{file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:efd489fec7c311dae25e94fe7eeda4b3d06be71c68f2cf2e8ef990ffcd2cd7e8"},
|
{file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada7754a10faacd4f26067e62de52d6af93b6d9542f0df73c57b9771eb3ba9c4"},
|
{file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c2a34fd26588949e1e7977cfcbb17a9a42c948c100cab890c6d8d823f0586457"},
|
{file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-win32.whl", hash = "sha256:f9174471d6920cbc5e82a7822de8dfd4dcea86eb828b04fc8c6519a77b0ee51e"},
|
{file = "rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464"},
|
||||||
{file = "rpds_py-0.28.0-cp310-cp310-win_amd64.whl", hash = "sha256:6e32dd207e2c4f8475257a3540ab8a93eff997abfa0a3fdb287cae0d6cd874b8"},
|
{file = "rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:03065002fd2e287725d95fbc69688e0c6daf6c6314ba38bdbaa3895418e09296"},
|
{file = "rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28ea02215f262b6d078daec0b45344c89e161eab9526b0d898221d96fdda5f27"},
|
{file = "rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25dbade8fbf30bcc551cb352376c0ad64b067e4fc56f90e22ba70c3ce205988c"},
|
{file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c03002f54cc855860bfdc3442928ffdca9081e73b5b382ed0b9e8efe6e5e205"},
|
{file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9699fa7990368b22032baf2b2dce1f634388e4ffc03dfefaaac79f4695edc95"},
|
{file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9b06fe1a75e05e0713f06ea0c89ecb6452210fd60e2f1b6ddc1067b990e08d9"},
|
{file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9f83e7b326a3f9ec3ef84cda98fb0a74c7159f33e692032233046e7fd15da2"},
|
{file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:0d3259ea9ad8743a75a43eb7819324cdab393263c91be86e2d1901ee65c314e0"},
|
{file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a7548b345f66f6695943b4ef6afe33ccd3f1b638bd9afd0f730dd255c249c9e"},
|
{file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9a40040aa388b037eb39416710fbcce9443498d2eaab0b9b45ae988b53f5c67"},
|
{file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f60c7ea34e78c199acd0d3cda37a99be2c861dd2b8cf67399784f70c9f8e57d"},
|
{file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1571ae4292649100d743b26d5f9c63503bb1fedf538a8f29a98dce2d5ba6b4e6"},
|
{file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-win32.whl", hash = "sha256:5cfa9af45e7c1140af7321fa0bef25b386ee9faa8928c80dc3a5360971a29e8c"},
|
{file = "rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd8d86b5d29d1b74100982424ba53e56033dc47720a6de9ba0259cf81d7cecaa"},
|
{file = "rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825"},
|
||||||
{file = "rpds_py-0.28.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e27d3a5709cc2b3e013bf93679a849213c79ae0573f9b894b284b55e729e120"},
|
{file = "rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6b4f28583a4f247ff60cd7bdda83db8c3f5b05a7a82ff20dd4b078571747708f"},
|
{file = "rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d678e91b610c29c4b3d52a2c148b641df2b4676ffe47c59f6388d58b99cdc424"},
|
{file = "rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e819e0e37a44a78e1383bf1970076e2ccc4dc8c2bbaa2f9bd1dc987e9afff628"},
|
{file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5ee514e0f0523db5d3fb171f397c54875dbbd69760a414dccf9d4d7ad628b5bd"},
|
{file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3fa06d27fdcee47f07a39e02862da0100cb4982508f5ead53ec533cd5fe55e"},
|
{file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46959ef2e64f9e4a41fc89aa20dbca2b85531f9a72c21099a3360f35d10b0d5a"},
|
{file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8455933b4bcd6e83fde3fefc987a023389c4b13f9a58c8d23e4b3f6d13f78c84"},
|
{file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:ad50614a02c8c2962feebe6012b52f9802deec4263946cddea37aaf28dd25a66"},
|
{file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5deca01b271492553fdb6c7fd974659dce736a15bae5dad7ab8b93555bceb28"},
|
{file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:735f8495a13159ce6a0d533f01e8674cec0c57038c920495f87dcb20b3ddb48a"},
|
{file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:961ca621ff10d198bbe6ba4957decca61aa2a0c56695384c1d6b79bf61436df5"},
|
{file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2374e16cc9131022e7d9a8f8d65d261d9ba55048c78f3b6e017971a4f5e6353c"},
|
{file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-win32.whl", hash = "sha256:d15431e334fba488b081d47f30f091e5d03c18527c325386091f31718952fe08"},
|
{file = "rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-win_amd64.whl", hash = "sha256:a410542d61fc54710f750d3764380b53bf09e8c4edbf2f9141a82aa774a04f7c"},
|
{file = "rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b"},
|
||||||
{file = "rpds_py-0.28.0-cp312-cp312-win_arm64.whl", hash = "sha256:1f0cfd1c69e2d14f8c892b893997fa9a60d890a0c8a603e88dca4955f26d1edd"},
|
{file = "rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e9e184408a0297086f880556b6168fa927d677716f83d3472ea333b42171ee3b"},
|
{file = "rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:edd267266a9b0448f33dc465a97cfc5d467594b600fe28e7fa2f36450e03053a"},
|
{file = "rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85beb8b3f45e4e32f6802fb6cd6b17f615ef6c6a52f265371fb916fae02814aa"},
|
{file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d2412be8d00a1b895f8ad827cc2116455196e20ed994bb704bf138fe91a42724"},
|
{file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf128350d384b777da0e68796afdcebc2e9f63f0e9f242217754e647f6d32491"},
|
{file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2036d09b363aa36695d1cc1a97b36865597f4478470b0697b5ee9403f4fe399"},
|
{file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8e1e9be4fa6305a16be628959188e4fd5cd6f1b0e724d63c6d8b2a8adf74ea6"},
|
{file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0a403460c9dd91a7f23fc3188de6d8977f1d9603a351d5db6cf20aaea95b538d"},
|
{file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d7366b6553cdc805abcc512b849a519167db8f5e5c3472010cd1228b224265cb"},
|
{file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b43c6a3726efd50f18d8120ec0551241c38785b68952d240c45ea553912ac41"},
|
{file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0cb7203c7bc69d7c1585ebb33a2e6074492d2fc21ad28a7b9d40457ac2a51ab7"},
|
{file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a52a5169c664dfb495882adc75c304ae1d50df552fbd68e100fdc719dee4ff9"},
|
{file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-win32.whl", hash = "sha256:2e42456917b6687215b3e606ab46aa6bca040c77af7df9a08a6dcfe8a4d10ca5"},
|
{file = "rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-win_amd64.whl", hash = "sha256:e0a0311caedc8069d68fc2bf4c9019b58a2d5ce3cd7cb656c845f1615b577e1e"},
|
{file = "rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313-win_arm64.whl", hash = "sha256:04c1b207ab8b581108801528d59ad80aa83bb170b35b0ddffb29c20e411acdc1"},
|
{file = "rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f296ea3054e11fc58ad42e850e8b75c62d9a93a9f981ad04b2e5ae7d2186ff9c"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5a7306c19b19005ad98468fcefeb7100b19c79fc23a5f24a12e06d91181193fa"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5d9b86aa501fed9862a443c5c3116f6ead8bc9296185f369277c42542bd646b"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e5bbc701eff140ba0e872691d573b3d5d30059ea26e5785acba9132d10c8c31d"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5690671cd672a45aa8616d7374fdf334a1b9c04a0cac3c854b1136e92374fe"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f1d92ecea4fa12f978a367c32a5375a1982834649cdb96539dcdc12e609ab1a"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d252db6b1a78d0a3928b6190156042d54c93660ce4d98290d7b16b5296fb7cc"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d61b355c3275acb825f8777d6c4505f42b5007e357af500939d4a35b19177259"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:acbe5e8b1026c0c580d0321c8aae4b0a1e1676861d48d6e8c6586625055b606a"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8aa23b6f0fc59b85b4c7d89ba2965af274346f738e8d9fc2455763602e62fd5f"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7b14b0c680286958817c22d76fcbca4800ddacef6f678f3a7c79a1fe7067fe37"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bcf1d210dfee61a6c86551d67ee1031899c0fdbae88b2d44a569995d43797712"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-win32.whl", hash = "sha256:3aa4dc0fdab4a7029ac63959a3ccf4ed605fee048ba67ce89ca3168da34a1342"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6"},
|
||||||
{file = "rpds_py-0.28.0-cp313-cp313t-win_amd64.whl", hash = "sha256:7b7d9d83c942855e4fdcfa75d4f96f6b9e272d42fffcb72cd4bb2577db2e2907"},
|
{file = "rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:dcdcb890b3ada98a03f9f2bb108489cdc7580176cb73b4f2d789e9a1dac1d472"},
|
{file = "rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f274f56a926ba2dc02976ca5b11c32855cbd5925534e57cfe1fda64e04d1add2"},
|
{file = "rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fe0438ac4a29a520ea94c8c7f1754cdd8feb1bc490dfda1bfd990072363d527"},
|
{file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a358a32dd3ae50e933347889b6af9a1bdf207ba5d1a3f34e1a38cd3540e6733"},
|
{file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e80848a71c78aa328fefaba9c244d588a342c8e03bda518447b624ea64d1ff56"},
|
{file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f586db2e209d54fe177e58e0bc4946bea5fb0102f150b1b2f13de03e1f0976f8"},
|
{file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ae8ee156d6b586e4292491e885d41483136ab994e719a13458055bec14cf370"},
|
{file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:a805e9b3973f7e27f7cab63a6b4f61d90f2e5557cff73b6e97cd5b8540276d3d"},
|
{file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5d3fd16b6dc89c73a4da0b4ac8b12a7ecc75b2864b95c9e5afed8003cb50a728"},
|
{file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6796079e5d24fdaba6d49bda28e2c47347e89834678f2bc2c1b4fc1489c0fb01"},
|
{file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:76500820c2af232435cbe215e3324c75b950a027134e044423f59f5b9a1ba515"},
|
{file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bbdc5640900a7dbf9dd707fe6388972f5bbd883633eb68b76591044cfe346f7e"},
|
{file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-win32.whl", hash = "sha256:adc8aa88486857d2b35d75f0640b949759f79dc105f50aa2c27816b2e0dd749f"},
|
{file = "rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-win_amd64.whl", hash = "sha256:66e6fa8e075b58946e76a78e69e1a124a21d9a48a5b4766d15ba5b06869d1fa1"},
|
{file = "rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314-win_arm64.whl", hash = "sha256:a6fe887c2c5c59413353b7c0caff25d0e566623501ccfff88957fa438a69377d"},
|
{file = "rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7a69df082db13c7070f7b8b1f155fa9e687f1d6aefb7b0e3f7231653b79a067b"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b1cde22f2c30ebb049a9e74c5374994157b9b70a16147d332f89c99c5960737a"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5338742f6ba7a51012ea470bd4dc600a8c713c0c72adaa0977a1b1f4327d6592"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1460ebde1bcf6d496d80b191d854adedcc619f84ff17dc1c6d550f58c9efbba"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e3eb248f2feba84c692579257a043a7699e28a77d86c77b032c1d9fbb3f0219c"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3bbba5def70b16cd1c1d7255666aad3b290fbf8d0fe7f9f91abafb73611a91"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3114f4db69ac5a1f32e7e4d1cbbe7c8f9cf8217f78e6e002cedf2d54c2a548ed"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4b0cb8a906b1a0196b863d460c0222fb8ad0f34041568da5620f9799b83ccf0b"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf681ac76a60b667106141e11a92a3330890257e6f559ca995fbb5265160b56e"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1e8ee6413cfc677ce8898d9cde18cc3a60fc2ba756b0dec5b71eb6eb21c49fa1"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b3072b16904d0b5572a15eb9d31c1954e0d3227a585fc1351aa9878729099d6c"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b670c30fd87a6aec281c3c9896d3bae4b205fd75d79d06dc87c2503717e46092"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-win32.whl", hash = "sha256:8014045a15b4d2b3476f0a287fcc93d4f823472d7d1308d47884ecac9e612be3"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0"},
|
||||||
{file = "rpds_py-0.28.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7a4e59c90d9c27c561eb3160323634a9ff50b04e4f7820600a2beb0ac90db578"},
|
{file = "rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f5e7101145427087e493b9c9b959da68d357c28c562792300dd21a095118ed16"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:31eb671150b9c62409a888850aaa8e6533635704fe2b78335f9aaf7ff81eec4d"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48b55c1f64482f7d8bd39942f376bfdf2f6aec637ee8c805b5041e14eeb771db"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24743a7b372e9a76171f6b69c01aedf927e8ac3e16c474d9fe20d552a8cb45c7"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:389c29045ee8bbb1627ea190b4976a310a295559eaf9f1464a1a6f2bf84dde78"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23690b5827e643150cf7b49569679ec13fe9a610a15949ed48b85eb7f98f34ec"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f0c9266c26580e7243ad0d72fc3e01d6b33866cfab5084a6da7576bcf1c4f72"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4c6c4db5d73d179746951486df97fd25e92396be07fc29ee8ff9a8f5afbdfb27"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3b695a8fa799dd2cfdb4804b37096c5f6dba1ac7f48a7fbf6d0485bcd060316"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:6aa1bfce3f83baf00d9c5fcdbba93a3ab79958b4c7d7d1f55e7fe68c20e63912"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:7b0f9dceb221792b3ee6acb5438eb1f02b0cb2c247796a72b016dcc92c6de829"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4"},
|
||||||
{file = "rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5d0145edba8abd3db0ab22b5300c99dc152f5c9021fab861be0f0544dc3cbc5f"},
|
{file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e"},
|
||||||
{file = "rpds_py-0.28.0.tar.gz", hash = "sha256:abd4df20485a0983e2ca334a216249b6186d6e3c1627e106651943dbdb791aea"},
|
{file = "rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2551,15 +2666,15 @@ doc = ["Sphinx", "sphinx-rtd-theme"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-sdk"
|
name = "sentry-sdk"
|
||||||
version = "2.44.0"
|
version = "2.46.0"
|
||||||
description = "Python client for Sentry (https://sentry.io)"
|
description = "Python client for Sentry (https://sentry.io)"
|
||||||
optional = true
|
optional = true
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
markers = "extra == \"all\" or extra == \"sentry\""
|
markers = "extra == \"all\" or extra == \"sentry\""
|
||||||
files = [
|
files = [
|
||||||
{file = "sentry_sdk-2.44.0-py2.py3-none-any.whl", hash = "sha256:9e36a0372b881e8f92fdbff4564764ce6cec4b7f25424d0a3a8d609c9e4651a7"},
|
{file = "sentry_sdk-2.46.0-py2.py3-none-any.whl", hash = "sha256:4eeeb60198074dff8d066ea153fa6f241fef1668c10900ea53a4200abc8da9b1"},
|
||||||
{file = "sentry_sdk-2.44.0.tar.gz", hash = "sha256:5b1fe54dfafa332e900b07dd8f4dfe35753b64e78e7d9b1655a28fd3065e2493"},
|
{file = "sentry_sdk-2.46.0.tar.gz", hash = "sha256:91821a23460725734b7741523021601593f35731808afc0bb2ba46c27b8acd91"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -2598,6 +2713,7 @@ openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
|
|||||||
openfeature = ["openfeature-sdk (>=0.7.1)"]
|
openfeature = ["openfeature-sdk (>=0.7.1)"]
|
||||||
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
|
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
|
||||||
opentelemetry-experimental = ["opentelemetry-distro"]
|
opentelemetry-experimental = ["opentelemetry-distro"]
|
||||||
|
opentelemetry-otlp = ["opentelemetry-distro[otlp] (>=0.35b0)"]
|
||||||
pure-eval = ["asttokens", "executing", "pure_eval"]
|
pure-eval = ["asttokens", "executing", "pure_eval"]
|
||||||
pydantic-ai = ["pydantic-ai (>=1.0.0)"]
|
pydantic-ai = ["pydantic-ai (>=1.0.0)"]
|
||||||
pymongo = ["pymongo (>=3.1)"]
|
pymongo = ["pymongo (>=3.1)"]
|
||||||
@@ -2730,6 +2846,22 @@ files = [
|
|||||||
{file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
|
{file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlglot"
|
||||||
|
version = "28.0.0"
|
||||||
|
description = "An easily customizable SQL parser and transpiler"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "sqlglot-28.0.0-py3-none-any.whl", hash = "sha256:ac1778e7fa4812f4f7e5881b260632fc167b00ca4c1226868891fb15467122e4"},
|
||||||
|
{file = "sqlglot-28.0.0.tar.gz", hash = "sha256:cc9a651ef4182e61dac58aa955e5fb21845a5865c6a4d7d7b5a7857450285ad4"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["duckdb (>=0.6)", "maturin (>=1.4,<2.0)", "mypy", "pandas", "pandas-stubs", "pdoc", "pre-commit", "pyperf", "python-dateutil", "pytz", "ruff (==0.7.2)", "types-python-dateutil", "types-pytz", "typing_extensions"]
|
||||||
|
rs = ["sqlglotrs (==0.7.3)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "systemd-python"
|
name = "systemd-python"
|
||||||
version = "235"
|
version = "235"
|
||||||
@@ -2984,14 +3116,14 @@ twisted = "*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-bleach"
|
name = "types-bleach"
|
||||||
version = "6.2.0.20250809"
|
version = "6.3.0.20251115"
|
||||||
description = "Typing stubs for bleach"
|
description = "Typing stubs for bleach"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "types_bleach-6.2.0.20250809-py3-none-any.whl", hash = "sha256:0b372a75117947d9ac8a31ae733fd0f8d92ec75c4772e7b37093ba3fa5b48fb9"},
|
{file = "types_bleach-6.3.0.20251115-py3-none-any.whl", hash = "sha256:f81e7cf4ebac3f3d60b66b3fd5236c324e65037d1b28d22c94d5b457f0b98f42"},
|
||||||
{file = "types_bleach-6.2.0.20250809.tar.gz", hash = "sha256:188d7a1119f6c953140b513ed57ba4213755695815472c19d0c22ac09c79b90b"},
|
{file = "types_bleach-6.3.0.20251115.tar.gz", hash = "sha256:96911b20f169a18524d03b61fa7e98a08c411292f7cdb5dc191057f55dad9ae3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -3026,14 +3158,14 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-jsonschema"
|
name = "types-jsonschema"
|
||||||
version = "4.25.1.20250822"
|
version = "4.25.1.20251009"
|
||||||
description = "Typing stubs for jsonschema"
|
description = "Typing stubs for jsonschema"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "types_jsonschema-4.25.1.20250822-py3-none-any.whl", hash = "sha256:f82c2d7fa1ce1c0b84ba1de4ed6798469768188884db04e66421913a4e181294"},
|
{file = "types_jsonschema-4.25.1.20251009-py3-none-any.whl", hash = "sha256:f30b329037b78e7a60146b1146feb0b6fb0b71628637584409bada83968dad3e"},
|
||||||
{file = "types_jsonschema-4.25.1.20250822.tar.gz", hash = "sha256:aac69ed4b23f49aaceb7fcb834141d61b9e4e6a7f6008cb2f0d3b831dfa8464a"},
|
{file = "types_jsonschema-4.25.1.20251009.tar.gz", hash = "sha256:75d0f5c5dd18dc23b664437a0c1a625743e8d2e665ceaf3aecb29841f3a5f97f"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -3183,21 +3315,21 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main", "dev"]
|
groups = ["main", "dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"},
|
{file = "urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f"},
|
||||||
{file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"},
|
{file = "urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
|
brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""]
|
||||||
h2 = ["h2 (>=4,<5)"]
|
h2 = ["h2 (>=4,<5)"]
|
||||||
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
zstd = ["zstandard (>=0.18.0)"]
|
zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webencodings"
|
name = "webencodings"
|
||||||
@@ -3353,4 +3485,4 @@ url-preview = ["lxml"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.1"
|
lock-version = "2.1"
|
||||||
python-versions = ">=3.10.0,<4.0.0"
|
python-versions = ">=3.10.0,<4.0.0"
|
||||||
content-hash = "4f8d98723236eaf3d13f440dce95ec6cc3c4dc49ba3a0e45bf9cfbb51aca899c"
|
content-hash = "98b9062f48205a3bcc99b43ae665083d360a15d4a208927fa978df9c36fd5315"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "matrix-synapse"
|
name = "matrix-synapse"
|
||||||
version = "1.143.0"
|
version = "1.144.0rc1"
|
||||||
description = "Homeserver for the Matrix decentralised comms protocol"
|
description = "Homeserver for the Matrix decentralised comms protocol"
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
authors = [
|
authors = [
|
||||||
@@ -269,6 +269,8 @@ extend-safe-fixes = [
|
|||||||
"UP007",
|
"UP007",
|
||||||
# pyupgrade rules compatible with Python >= 3.10
|
# pyupgrade rules compatible with Python >= 3.10
|
||||||
"UP045",
|
"UP045",
|
||||||
|
# Allow ruff to automatically fix trailing spaces within a multi-line string/comment.
|
||||||
|
"W293"
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff.lint.isort]
|
[tool.ruff.lint.isort]
|
||||||
@@ -368,6 +370,9 @@ towncrier = ">=18.6.0rc1"
|
|||||||
# Used for checking the Poetry lockfile
|
# Used for checking the Poetry lockfile
|
||||||
tomli = ">=1.2.3"
|
tomli = ">=1.2.3"
|
||||||
|
|
||||||
|
# Used for checking the schema delta files
|
||||||
|
sqlglot = ">=28.0.0"
|
||||||
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
# The upper bounds here are defensive, intended to prevent situations like
|
# The upper bounds here are defensive, intended to prevent situations like
|
||||||
@@ -393,7 +398,8 @@ build-backend = "poetry.core.masonry.api"
|
|||||||
# We skip:
|
# We skip:
|
||||||
# - free-threaded cpython builds: these are not currently supported.
|
# - free-threaded cpython builds: these are not currently supported.
|
||||||
# - i686: We don't support 32-bit platforms.
|
# - i686: We don't support 32-bit platforms.
|
||||||
skip = "cp3??t-* *i686*"
|
# - *macosx*: we don't support building wheels for MacOS.
|
||||||
|
skip = "cp3??t-* *i686* *macosx*"
|
||||||
# Enable non-default builds. See the list of available options:
|
# Enable non-default builds. See the list of available options:
|
||||||
# https://cibuildwheel.pypa.io/en/stable/options#enable
|
# https://cibuildwheel.pypa.io/en/stable/options#enable
|
||||||
#
|
#
|
||||||
@@ -418,7 +424,3 @@ test-command = "python -c 'from synapse.synapse_rust import sum_as_string; print
|
|||||||
[tool.cibuildwheel.linux]
|
[tool.cibuildwheel.linux]
|
||||||
# Wrap the repair command to correctly rename the built cpython wheels as ABI3.
|
# Wrap the repair command to correctly rename the built cpython wheels as ABI3.
|
||||||
repair-wheel-command = "./.ci/scripts/auditwheel_wrapper.py -w {dest_dir} {wheel}"
|
repair-wheel-command = "./.ci/scripts/auditwheel_wrapper.py -w {dest_dir} {wheel}"
|
||||||
|
|
||||||
[tool.cibuildwheel.macos]
|
|
||||||
# Wrap the repair command to correctly rename the built cpython wheels as ABI3.
|
|
||||||
repair-wheel-command = "./.ci/scripts/auditwheel_wrapper.py --require-archs {delocate_archs} -w {dest_dir} {wheel}"
|
|
||||||
|
|||||||
56
rust/src/duration.rs
Normal file
56
rust/src/duration.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Element Creations, Ltd
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* See the GNU Affero General Public License for more details:
|
||||||
|
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use pyo3::{
|
||||||
|
types::{IntoPyDict, PyAnyMethods},
|
||||||
|
Bound, BoundObject, IntoPyObject, Py, PyAny, PyErr, PyResult, Python,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A reference to the `synapse.util.duration` module.
|
||||||
|
static DURATION: OnceCell<Py<PyAny>> = OnceCell::new();
|
||||||
|
|
||||||
|
/// Access to the `synapse.util.duration` module.
|
||||||
|
fn duration_module(py: Python<'_>) -> PyResult<&Bound<'_, PyAny>> {
|
||||||
|
Ok(DURATION
|
||||||
|
.get_or_try_init(|| py.import("synapse.util.duration").map(Into::into))?
|
||||||
|
.bind(py))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mirrors the `synapse.util.duration.Duration` Python class.
|
||||||
|
pub struct SynapseDuration {
|
||||||
|
microseconds: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SynapseDuration {
|
||||||
|
/// For now we only need to create durations from milliseconds.
|
||||||
|
pub fn from_milliseconds(milliseconds: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
microseconds: milliseconds * 1_000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'py> IntoPyObject<'py> for &SynapseDuration {
|
||||||
|
type Target = PyAny;
|
||||||
|
type Output = Bound<'py, Self::Target>;
|
||||||
|
type Error = PyErr;
|
||||||
|
|
||||||
|
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||||
|
let duration_module = duration_module(py)?;
|
||||||
|
let kwargs = [("microseconds", self.microseconds)].into_py_dict(py)?;
|
||||||
|
let duration_instance = duration_module.call_method("Duration", (), Some(&kwargs))?;
|
||||||
|
Ok(duration_instance.into_bound())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ use pyo3::prelude::*;
|
|||||||
use pyo3_log::ResetHandle;
|
use pyo3_log::ResetHandle;
|
||||||
|
|
||||||
pub mod acl;
|
pub mod acl;
|
||||||
|
pub mod duration;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ use ulid::Ulid;
|
|||||||
|
|
||||||
use self::session::Session;
|
use self::session::Session;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
duration::SynapseDuration,
|
||||||
errors::{NotFoundError, SynapseError},
|
errors::{NotFoundError, SynapseError},
|
||||||
http::{http_request_from_twisted, http_response_to_twisted, HeaderMapPyExt},
|
http::{http_request_from_twisted, http_response_to_twisted, HeaderMapPyExt},
|
||||||
UnwrapInfallible,
|
UnwrapInfallible,
|
||||||
@@ -132,6 +133,8 @@ impl RendezvousHandler {
|
|||||||
.unwrap_infallible()
|
.unwrap_infallible()
|
||||||
.unbind();
|
.unbind();
|
||||||
|
|
||||||
|
let eviction_duration = SynapseDuration::from_milliseconds(eviction_interval);
|
||||||
|
|
||||||
// Construct a Python object so that we can get a reference to the
|
// Construct a Python object so that we can get a reference to the
|
||||||
// evict method and schedule it to run.
|
// evict method and schedule it to run.
|
||||||
let self_ = Py::new(
|
let self_ = Py::new(
|
||||||
@@ -149,7 +152,7 @@ impl RendezvousHandler {
|
|||||||
let evict = self_.getattr(py, "_evict")?;
|
let evict = self_.getattr(py, "_evict")?;
|
||||||
homeserver.call_method0("get_clock")?.call_method(
|
homeserver.call_method0("get_clock")?.call_method(
|
||||||
"looping_call",
|
"looping_call",
|
||||||
(evict, eviction_interval),
|
(evict, &eviction_duration),
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
$schema: https://element-hq.github.io/synapse/latest/schema/v1/meta.schema.json
|
$schema: https://element-hq.github.io/synapse/latest/schema/v1/meta.schema.json
|
||||||
$id: https://element-hq.github.io/synapse/schema/synapse/v1.143/synapse-config.schema.json
|
$id: https://element-hq.github.io/synapse/schema/synapse/v1.144/synapse-config.schema.json
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
modules:
|
modules:
|
||||||
|
|||||||
@@ -9,15 +9,11 @@ from typing import Any
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
import git
|
import git
|
||||||
|
import sqlglot
|
||||||
|
import sqlglot.expressions
|
||||||
|
|
||||||
SCHEMA_FILE_REGEX = re.compile(r"^synapse/storage/schema/(.*)/delta/(.*)/(.*)$")
|
SCHEMA_FILE_REGEX = re.compile(r"^synapse/storage/schema/(.*)/delta/(.*)/(.*)$")
|
||||||
INDEX_CREATION_REGEX = re.compile(
|
|
||||||
r"CREATE .*INDEX .*ON ([a-z_0-9]+)", flags=re.IGNORECASE
|
|
||||||
)
|
|
||||||
INDEX_DELETION_REGEX = re.compile(r"DROP .*INDEX ([a-z_0-9]+)", flags=re.IGNORECASE)
|
|
||||||
TABLE_CREATION_REGEX = re.compile(
|
|
||||||
r"CREATE .*TABLE.* ([a-z_0-9]+)\s*\(", flags=re.IGNORECASE
|
|
||||||
)
|
|
||||||
|
|
||||||
# The base branch we want to check against. We use the main development branch
|
# The base branch we want to check against. We use the main development branch
|
||||||
# on the assumption that is what we are developing against.
|
# on the assumption that is what we are developing against.
|
||||||
@@ -141,6 +137,9 @@ def main(force_colors: bool) -> None:
|
|||||||
color=force_colors,
|
color=force_colors,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Mark this run as not successful, but continue so that we report *all*
|
||||||
|
# errors.
|
||||||
|
return_code = 1
|
||||||
else:
|
else:
|
||||||
click.secho(
|
click.secho(
|
||||||
f"All deltas are in the correct folder: {current_schema_version}!",
|
f"All deltas are in the correct folder: {current_schema_version}!",
|
||||||
@@ -153,60 +152,90 @@ def main(force_colors: bool) -> None:
|
|||||||
# and delta files are also numbered in order.
|
# and delta files are also numbered in order.
|
||||||
changed_delta_files.sort()
|
changed_delta_files.sort()
|
||||||
|
|
||||||
# Now check that we're not trying to create or drop indices. If we want to
|
success = check_schema_delta(changed_delta_files, force_colors)
|
||||||
# do that they should be in background updates. The exception is when we
|
if not success:
|
||||||
# create indices on tables we've just created.
|
return_code = 1
|
||||||
created_tables = set()
|
|
||||||
for delta_file in changed_delta_files:
|
|
||||||
with open(delta_file) as fd:
|
|
||||||
delta_lines = fd.readlines()
|
|
||||||
|
|
||||||
for line in delta_lines:
|
|
||||||
# Strip SQL comments
|
|
||||||
line = line.split("--", maxsplit=1)[0]
|
|
||||||
|
|
||||||
# Check and track any tables we create
|
|
||||||
match = TABLE_CREATION_REGEX.search(line)
|
|
||||||
if match:
|
|
||||||
table_name = match.group(1)
|
|
||||||
created_tables.add(table_name)
|
|
||||||
|
|
||||||
# Check for dropping indices, these are always banned
|
|
||||||
match = INDEX_DELETION_REGEX.search(line)
|
|
||||||
if match:
|
|
||||||
clause = match.group()
|
|
||||||
|
|
||||||
click.secho(
|
|
||||||
f"Found delta with index deletion: '{clause}' in {delta_file}",
|
|
||||||
fg="red",
|
|
||||||
bold=True,
|
|
||||||
color=force_colors,
|
|
||||||
)
|
|
||||||
click.secho(
|
|
||||||
" ↪ These should be in background updates.",
|
|
||||||
)
|
|
||||||
return_code = 1
|
|
||||||
|
|
||||||
# Check for index creation, which is only allowed for tables we've
|
|
||||||
# created.
|
|
||||||
match = INDEX_CREATION_REGEX.search(line)
|
|
||||||
if match:
|
|
||||||
clause = match.group()
|
|
||||||
table_name = match.group(1)
|
|
||||||
if table_name not in created_tables:
|
|
||||||
click.secho(
|
|
||||||
f"Found delta with index creation for existing table: '{clause}' in {delta_file}",
|
|
||||||
fg="red",
|
|
||||||
bold=True,
|
|
||||||
color=force_colors,
|
|
||||||
)
|
|
||||||
click.secho(
|
|
||||||
" ↪ These should be in background updates (or the table should be created in the same delta).",
|
|
||||||
)
|
|
||||||
return_code = 1
|
|
||||||
|
|
||||||
click.get_current_context().exit(return_code)
|
click.get_current_context().exit(return_code)
|
||||||
|
|
||||||
|
|
||||||
|
def check_schema_delta(delta_files: list[str], force_colors: bool) -> bool:
|
||||||
|
"""Check that the given schema delta files do not create or drop indices
|
||||||
|
inappropriately.
|
||||||
|
|
||||||
|
Index creation is only allowed on tables created in the same set of deltas.
|
||||||
|
|
||||||
|
Index deletion is never allowed and should be done in background updates.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if all checks succeeded, False if at least one failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The tables created in this delta
|
||||||
|
created_tables = set[str]()
|
||||||
|
|
||||||
|
# The indices created/dropped in this delta, each a tuple of (table_name, sql)
|
||||||
|
created_indices = list[tuple[str, str]]()
|
||||||
|
|
||||||
|
# The indices dropped in this delta, just the sql
|
||||||
|
dropped_indices = list[str]()
|
||||||
|
|
||||||
|
for delta_file in delta_files:
|
||||||
|
with open(delta_file) as fd:
|
||||||
|
delta_contents = fd.read()
|
||||||
|
|
||||||
|
# Assume the SQL dialect from the file extension, defaulting to Postgres.
|
||||||
|
sql_lang = "postgres"
|
||||||
|
if delta_file.endswith(".sqlite"):
|
||||||
|
sql_lang = "sqlite"
|
||||||
|
|
||||||
|
statements = sqlglot.parse(delta_contents, read=sql_lang)
|
||||||
|
|
||||||
|
for statement in statements:
|
||||||
|
if isinstance(statement, sqlglot.expressions.Create):
|
||||||
|
if statement.kind == "TABLE":
|
||||||
|
assert isinstance(statement.this, sqlglot.expressions.Schema)
|
||||||
|
assert isinstance(statement.this.this, sqlglot.expressions.Table)
|
||||||
|
|
||||||
|
table_name = statement.this.this.name
|
||||||
|
created_tables.add(table_name)
|
||||||
|
elif statement.kind == "INDEX":
|
||||||
|
assert isinstance(statement.this, sqlglot.expressions.Index)
|
||||||
|
|
||||||
|
table_name = statement.this.args["table"].name
|
||||||
|
created_indices.append((table_name, statement.sql()))
|
||||||
|
elif isinstance(statement, sqlglot.expressions.Drop):
|
||||||
|
if statement.kind == "INDEX":
|
||||||
|
dropped_indices.append(statement.sql())
|
||||||
|
|
||||||
|
success = True
|
||||||
|
for table_name, clause in created_indices:
|
||||||
|
if table_name not in created_tables:
|
||||||
|
click.secho(
|
||||||
|
f"Found delta with index creation for existing table: '{clause}'",
|
||||||
|
fg="red",
|
||||||
|
bold=True,
|
||||||
|
color=force_colors,
|
||||||
|
)
|
||||||
|
click.secho(
|
||||||
|
" ↪ These should be in background updates (or the table should be created in the same delta).",
|
||||||
|
)
|
||||||
|
success = False
|
||||||
|
|
||||||
|
for clause in dropped_indices:
|
||||||
|
click.secho(
|
||||||
|
f"Found delta with index deletion: '{clause}'",
|
||||||
|
fg="red",
|
||||||
|
bold=True,
|
||||||
|
color=force_colors,
|
||||||
|
)
|
||||||
|
click.secho(
|
||||||
|
" ↪ These should be in background updates.",
|
||||||
|
)
|
||||||
|
success = False
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -72,153 +72,151 @@ For help on arguments to 'go test', run 'go help testflag'.
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
# parse our arguments
|
# We use a function to wrap the script logic so that we can use `return` to exit early
|
||||||
skip_docker_build=""
|
# if needed. This is particularly useful so that this script can be sourced by other
|
||||||
skip_complement_run=""
|
# scripts without exiting the calling subshell (composable). This allows us to share
|
||||||
while [ $# -ge 1 ]; do
|
# variables like `SYNAPSE_SUPPORTED_COMPLEMENT_TEST_PACKAGES` with other scripts.
|
||||||
|
#
|
||||||
|
# Returns an exit code of 0 on success, or 1 on failure.
|
||||||
|
main() {
|
||||||
|
# parse our arguments
|
||||||
|
skip_docker_build=""
|
||||||
|
skip_complement_run=""
|
||||||
|
while [ $# -ge 1 ]; do
|
||||||
arg=$1
|
arg=$1
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
"-h")
|
"-h")
|
||||||
usage
|
usage
|
||||||
exit 1
|
return 1
|
||||||
;;
|
;;
|
||||||
"-f"|"--fast")
|
"-f"|"--fast")
|
||||||
skip_docker_build=1
|
skip_docker_build=1
|
||||||
;;
|
;;
|
||||||
"--build-only")
|
"--build-only")
|
||||||
skip_complement_run=1
|
skip_complement_run=1
|
||||||
;;
|
;;
|
||||||
"-e"|"--editable")
|
"-e"|"--editable")
|
||||||
use_editable_synapse=1
|
use_editable_synapse=1
|
||||||
;;
|
;;
|
||||||
"--rebuild-editable")
|
"--rebuild-editable")
|
||||||
rebuild_editable_synapse=1
|
rebuild_editable_synapse=1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
# unknown arg: presumably an argument to gotest. break the loop.
|
# unknown arg: presumably an argument to gotest. break the loop.
|
||||||
break
|
break
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
# enable buildkit for the docker builds
|
# enable buildkit for the docker builds
|
||||||
export DOCKER_BUILDKIT=1
|
export DOCKER_BUILDKIT=1
|
||||||
|
|
||||||
# Determine whether to use the docker or podman container runtime.
|
# Determine whether to use the docker or podman container runtime.
|
||||||
if [ -n "$PODMAN" ]; then
|
if [ -n "$PODMAN" ]; then
|
||||||
export CONTAINER_RUNTIME=podman
|
export CONTAINER_RUNTIME=podman
|
||||||
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
|
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
|
||||||
export BUILDAH_FORMAT=docker
|
export BUILDAH_FORMAT=docker
|
||||||
export COMPLEMENT_HOSTNAME_RUNNING_COMPLEMENT=host.containers.internal
|
export COMPLEMENT_HOSTNAME_RUNNING_COMPLEMENT=host.containers.internal
|
||||||
else
|
else
|
||||||
export CONTAINER_RUNTIME=docker
|
export CONTAINER_RUNTIME=docker
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Change to the repository root
|
# Change to the repository root
|
||||||
cd "$(dirname $0)/.."
|
cd "$(dirname $0)/.."
|
||||||
|
|
||||||
# Check for a user-specified Complement checkout
|
# Check for a user-specified Complement checkout
|
||||||
if [[ -z "$COMPLEMENT_DIR" ]]; then
|
if [[ -z "$COMPLEMENT_DIR" ]]; then
|
||||||
COMPLEMENT_REF=${COMPLEMENT_REF:-main}
|
COMPLEMENT_REF=${COMPLEMENT_REF:-main}
|
||||||
echo "COMPLEMENT_DIR not set. Fetching Complement checkout from ${COMPLEMENT_REF}..."
|
echo "COMPLEMENT_DIR not set. Fetching Complement checkout from ${COMPLEMENT_REF}..."
|
||||||
wget -Nq https://github.com/matrix-org/complement/archive/${COMPLEMENT_REF}.tar.gz
|
wget -Nq https://github.com/matrix-org/complement/archive/${COMPLEMENT_REF}.tar.gz
|
||||||
tar -xzf ${COMPLEMENT_REF}.tar.gz
|
tar -xzf ${COMPLEMENT_REF}.tar.gz
|
||||||
COMPLEMENT_DIR=complement-${COMPLEMENT_REF}
|
COMPLEMENT_DIR=complement-${COMPLEMENT_REF}
|
||||||
echo "Checkout available at 'complement-${COMPLEMENT_REF}'"
|
echo "Checkout available at 'complement-${COMPLEMENT_REF}'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$use_editable_synapse" ]; then
|
if [ -n "$use_editable_synapse" ]; then
|
||||||
if [[ -e synapse/synapse_rust.abi3.so ]]; then
|
if [[ -e synapse/synapse_rust.abi3.so ]]; then
|
||||||
# In an editable install, back up the host's compiled Rust module to prevent
|
# In an editable install, back up the host's compiled Rust module to prevent
|
||||||
# inconvenience; the container will overwrite the module with its own copy.
|
# inconvenience; the container will overwrite the module with its own copy.
|
||||||
mv -n synapse/synapse_rust.abi3.so synapse/synapse_rust.abi3.so~host
|
mv -n synapse/synapse_rust.abi3.so synapse/synapse_rust.abi3.so~host
|
||||||
# And restore it on exit:
|
# And restore it on exit:
|
||||||
synapse_pkg=`realpath synapse`
|
synapse_pkg=`realpath synapse`
|
||||||
trap "mv -f '$synapse_pkg/synapse_rust.abi3.so~host' '$synapse_pkg/synapse_rust.abi3.so'" EXIT
|
trap "mv -f '$synapse_pkg/synapse_rust.abi3.so~host' '$synapse_pkg/synapse_rust.abi3.so'" EXIT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
editable_mount="$(realpath .):/editable-src:z"
|
editable_mount="$(realpath .):/editable-src:z"
|
||||||
if [ -n "$rebuild_editable_synapse" ]; then
|
if [ -n "$rebuild_editable_synapse" ]; then
|
||||||
unset skip_docker_build
|
unset skip_docker_build
|
||||||
elif $CONTAINER_RUNTIME inspect complement-synapse-editable &>/dev/null; then
|
elif $CONTAINER_RUNTIME inspect complement-synapse-editable &>/dev/null; then
|
||||||
# complement-synapse-editable already exists: see if we can still use it:
|
# complement-synapse-editable already exists: see if we can still use it:
|
||||||
# - The Rust module must still be importable; it will fail to import if the Rust source has changed.
|
# - The Rust module must still be importable; it will fail to import if the Rust source has changed.
|
||||||
# - The Poetry lock file must be the same (otherwise we assume dependencies have changed)
|
# - The Poetry lock file must be the same (otherwise we assume dependencies have changed)
|
||||||
|
|
||||||
# First set up the module in the right place for an editable installation.
|
# First set up the module in the right place for an editable installation.
|
||||||
$CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'cp' complement-synapse-editable -- /synapse_rust.abi3.so.bak /editable-src/synapse/synapse_rust.abi3.so
|
$CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'cp' complement-synapse-editable -- /synapse_rust.abi3.so.bak /editable-src/synapse/synapse_rust.abi3.so
|
||||||
|
|
||||||
if ($CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'python' complement-synapse-editable -c 'import synapse.synapse_rust' \
|
if ($CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'python' complement-synapse-editable -c 'import synapse.synapse_rust' \
|
||||||
&& $CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'diff' complement-synapse-editable --brief /editable-src/poetry.lock /poetry.lock.bak); then
|
&& $CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'diff' complement-synapse-editable --brief /editable-src/poetry.lock /poetry.lock.bak); then
|
||||||
skip_docker_build=1
|
skip_docker_build=1
|
||||||
else
|
else
|
||||||
echo "Editable Synapse image is stale. Will rebuild."
|
echo "Editable Synapse image is stale. Will rebuild."
|
||||||
unset skip_docker_build
|
unset skip_docker_build
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$skip_docker_build" ]; then
|
if [ -z "$skip_docker_build" ]; then
|
||||||
if [ -n "$use_editable_synapse" ]; then
|
if [ -n "$use_editable_synapse" ]; then
|
||||||
|
|
||||||
# Build a special image designed for use in development with editable
|
# Build a special image designed for use in development with editable
|
||||||
# installs.
|
# installs.
|
||||||
$CONTAINER_RUNTIME build -t synapse-editable \
|
$CONTAINER_RUNTIME build -t synapse-editable \
|
||||||
-f "docker/editable.Dockerfile" .
|
-f "docker/editable.Dockerfile" .
|
||||||
|
|
||||||
$CONTAINER_RUNTIME build -t synapse-workers-editable \
|
$CONTAINER_RUNTIME build -t synapse-workers-editable \
|
||||||
--build-arg FROM=synapse-editable \
|
--build-arg FROM=synapse-editable \
|
||||||
-f "docker/Dockerfile-workers" .
|
-f "docker/Dockerfile-workers" .
|
||||||
|
|
||||||
$CONTAINER_RUNTIME build -t complement-synapse-editable \
|
$CONTAINER_RUNTIME build -t complement-synapse-editable \
|
||||||
--build-arg FROM=synapse-workers-editable \
|
--build-arg FROM=synapse-workers-editable \
|
||||||
-f "docker/complement/Dockerfile" "docker/complement"
|
-f "docker/complement/Dockerfile" "docker/complement"
|
||||||
|
|
||||||
# Prepare the Rust module
|
# Prepare the Rust module
|
||||||
$CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'cp' complement-synapse-editable -- /synapse_rust.abi3.so.bak /editable-src/synapse/synapse_rust.abi3.so
|
$CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'cp' complement-synapse-editable -- /synapse_rust.abi3.so.bak /editable-src/synapse/synapse_rust.abi3.so
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
# Build the base Synapse image from the local checkout
|
# Build the base Synapse image from the local checkout
|
||||||
echo_if_github "::group::Build Docker image: matrixdotorg/synapse"
|
echo_if_github "::group::Build Docker image: matrixdotorg/synapse"
|
||||||
$CONTAINER_RUNTIME build -t matrixdotorg/synapse \
|
$CONTAINER_RUNTIME build -t matrixdotorg/synapse \
|
||||||
--build-arg TEST_ONLY_SKIP_DEP_HASH_VERIFICATION \
|
--build-arg TEST_ONLY_SKIP_DEP_HASH_VERIFICATION \
|
||||||
--build-arg TEST_ONLY_IGNORE_POETRY_LOCKFILE \
|
--build-arg TEST_ONLY_IGNORE_POETRY_LOCKFILE \
|
||||||
-f "docker/Dockerfile" .
|
-f "docker/Dockerfile" .
|
||||||
echo_if_github "::endgroup::"
|
echo_if_github "::endgroup::"
|
||||||
|
|
||||||
# Build the workers docker image (from the base Synapse image we just built).
|
# Build the workers docker image (from the base Synapse image we just built).
|
||||||
echo_if_github "::group::Build Docker image: matrixdotorg/synapse-workers"
|
echo_if_github "::group::Build Docker image: matrixdotorg/synapse-workers"
|
||||||
$CONTAINER_RUNTIME build -t matrixdotorg/synapse-workers -f "docker/Dockerfile-workers" .
|
$CONTAINER_RUNTIME build -t matrixdotorg/synapse-workers -f "docker/Dockerfile-workers" .
|
||||||
echo_if_github "::endgroup::"
|
echo_if_github "::endgroup::"
|
||||||
|
|
||||||
# Build the unified Complement image (from the worker Synapse image we just built).
|
# Build the unified Complement image (from the worker Synapse image we just built).
|
||||||
echo_if_github "::group::Build Docker image: complement/Dockerfile"
|
echo_if_github "::group::Build Docker image: complement/Dockerfile"
|
||||||
$CONTAINER_RUNTIME build -t complement-synapse \
|
$CONTAINER_RUNTIME build -t complement-synapse \
|
||||||
`# This is the tag we end up pushing to the registry (see` \
|
`# This is the tag we end up pushing to the registry (see` \
|
||||||
`# .github/workflows/push_complement_image.yml) so let's just label it now` \
|
`# .github/workflows/push_complement_image.yml) so let's just label it now` \
|
||||||
`# so people can reference it by the same name locally.` \
|
`# so people can reference it by the same name locally.` \
|
||||||
-t ghcr.io/element-hq/synapse/complement-synapse \
|
-t ghcr.io/element-hq/synapse/complement-synapse \
|
||||||
-f "docker/complement/Dockerfile" "docker/complement"
|
-f "docker/complement/Dockerfile" "docker/complement"
|
||||||
echo_if_github "::endgroup::"
|
echo_if_github "::endgroup::"
|
||||||
|
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
echo "Docker images built."
|
||||||
|
else
|
||||||
|
echo "Skipping Docker image build as requested."
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -n "$skip_complement_run" ]; then
|
test_packages=(
|
||||||
echo "Skipping Complement run as requested."
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
export COMPLEMENT_BASE_IMAGE=complement-synapse
|
|
||||||
if [ -n "$use_editable_synapse" ]; then
|
|
||||||
export COMPLEMENT_BASE_IMAGE=complement-synapse-editable
|
|
||||||
export COMPLEMENT_HOST_MOUNTS="$editable_mount"
|
|
||||||
fi
|
|
||||||
|
|
||||||
extra_test_args=()
|
|
||||||
|
|
||||||
test_packages=(
|
|
||||||
./tests/csapi
|
./tests/csapi
|
||||||
./tests
|
./tests
|
||||||
./tests/msc3874
|
./tests/msc3874
|
||||||
@@ -231,71 +229,104 @@ test_packages=(
|
|||||||
./tests/msc4140
|
./tests/msc4140
|
||||||
./tests/msc4155
|
./tests/msc4155
|
||||||
./tests/msc4306
|
./tests/msc4306
|
||||||
)
|
)
|
||||||
|
|
||||||
# Enable dirty runs, so tests will reuse the same container where possible.
|
# Export the list of test packages as a space-separated environment variable, so other
|
||||||
# This significantly speeds up tests, but increases the possibility of test pollution.
|
# scripts can use it.
|
||||||
export COMPLEMENT_ENABLE_DIRTY_RUNS=1
|
export SYNAPSE_SUPPORTED_COMPLEMENT_TEST_PACKAGES="${test_packages[@]}"
|
||||||
|
|
||||||
# All environment variables starting with PASS_ will be shared.
|
export COMPLEMENT_BASE_IMAGE=complement-synapse
|
||||||
# (The prefix is stripped off before reaching the container.)
|
if [ -n "$use_editable_synapse" ]; then
|
||||||
export COMPLEMENT_SHARE_ENV_PREFIX=PASS_
|
export COMPLEMENT_BASE_IMAGE=complement-synapse-editable
|
||||||
|
export COMPLEMENT_HOST_MOUNTS="$editable_mount"
|
||||||
# It takes longer than 10m to run the whole suite.
|
|
||||||
extra_test_args+=("-timeout=60m")
|
|
||||||
|
|
||||||
if [[ -n "$WORKERS" ]]; then
|
|
||||||
# Use workers.
|
|
||||||
export PASS_SYNAPSE_COMPLEMENT_USE_WORKERS=true
|
|
||||||
|
|
||||||
# Pass through the workers defined. If none, it will be an empty string
|
|
||||||
export PASS_SYNAPSE_WORKER_TYPES="$WORKER_TYPES"
|
|
||||||
|
|
||||||
# Workers can only use Postgres as a database.
|
|
||||||
export PASS_SYNAPSE_COMPLEMENT_DATABASE=postgres
|
|
||||||
|
|
||||||
# And provide some more configuration to complement.
|
|
||||||
|
|
||||||
# It can take quite a while to spin up a worker-mode Synapse for the first
|
|
||||||
# time (the main problem is that we start 14 python processes for each test,
|
|
||||||
# and complement likes to do two of them in parallel).
|
|
||||||
export COMPLEMENT_SPAWN_HS_TIMEOUT_SECS=120
|
|
||||||
else
|
|
||||||
export PASS_SYNAPSE_COMPLEMENT_USE_WORKERS=
|
|
||||||
if [[ -n "$POSTGRES" ]]; then
|
|
||||||
export PASS_SYNAPSE_COMPLEMENT_DATABASE=postgres
|
|
||||||
else
|
|
||||||
export PASS_SYNAPSE_COMPLEMENT_DATABASE=sqlite
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Enable dirty runs, so tests will reuse the same container where possible.
|
||||||
|
# This significantly speeds up tests, but increases the possibility of test pollution.
|
||||||
|
export COMPLEMENT_ENABLE_DIRTY_RUNS=1
|
||||||
|
|
||||||
|
# All environment variables starting with PASS_ will be shared.
|
||||||
|
# (The prefix is stripped off before reaching the container.)
|
||||||
|
export COMPLEMENT_SHARE_ENV_PREFIX=PASS_
|
||||||
|
|
||||||
|
# * -count=1: Only run tests once, and disable caching for tests.
|
||||||
|
# * -v: Output test logs, even if those tests pass.
|
||||||
|
# * -tags=synapse_blacklist: Enable the `synapse_blacklist` build tag, which is
|
||||||
|
# necessary for `runtime.Synapse` checks/skips to work in the tests
|
||||||
|
test_args=(
|
||||||
|
-v
|
||||||
|
-tags="synapse_blacklist"
|
||||||
|
-count=1
|
||||||
|
)
|
||||||
|
|
||||||
|
# It takes longer than 10m to run the whole suite.
|
||||||
|
test_args+=("-timeout=60m")
|
||||||
|
|
||||||
|
if [[ -n "$WORKERS" ]]; then
|
||||||
|
# Use workers.
|
||||||
|
export PASS_SYNAPSE_COMPLEMENT_USE_WORKERS=true
|
||||||
|
|
||||||
|
# Pass through the workers defined. If none, it will be an empty string
|
||||||
|
export PASS_SYNAPSE_WORKER_TYPES="$WORKER_TYPES"
|
||||||
|
|
||||||
|
# Workers can only use Postgres as a database.
|
||||||
|
export PASS_SYNAPSE_COMPLEMENT_DATABASE=postgres
|
||||||
|
|
||||||
|
# And provide some more configuration to complement.
|
||||||
|
|
||||||
|
# It can take quite a while to spin up a worker-mode Synapse for the first
|
||||||
|
# time (the main problem is that we start 14 python processes for each test,
|
||||||
|
# and complement likes to do two of them in parallel).
|
||||||
|
export COMPLEMENT_SPAWN_HS_TIMEOUT_SECS=120
|
||||||
|
else
|
||||||
|
export PASS_SYNAPSE_COMPLEMENT_USE_WORKERS=
|
||||||
|
if [[ -n "$POSTGRES" ]]; then
|
||||||
|
export PASS_SYNAPSE_COMPLEMENT_DATABASE=postgres
|
||||||
|
else
|
||||||
|
export PASS_SYNAPSE_COMPLEMENT_DATABASE=sqlite
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$ASYNCIO_REACTOR" ]]; then
|
||||||
|
# Enable the Twisted asyncio reactor
|
||||||
|
export PASS_SYNAPSE_COMPLEMENT_USE_ASYNCIO_REACTOR=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$UNIX_SOCKETS" ]]; then
|
||||||
|
# Enable full on Unix socket mode for Synapse, Redis and Postgresql
|
||||||
|
export PASS_SYNAPSE_USE_UNIX_SOCKET=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$SYNAPSE_TEST_LOG_LEVEL" ]]; then
|
||||||
|
# Set the log level to what is desired
|
||||||
|
export PASS_SYNAPSE_LOG_LEVEL="$SYNAPSE_TEST_LOG_LEVEL"
|
||||||
|
|
||||||
|
# Allow logging sensitive things (currently SQL queries & parameters).
|
||||||
|
# (This won't have any effect if we're not logging at DEBUG level overall.)
|
||||||
|
# Since this is just a test suite, this is fine and won't reveal anyone's
|
||||||
|
# personal information
|
||||||
|
export PASS_SYNAPSE_LOG_SENSITIVE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log a few more useful things for a developer attempting to debug something
|
||||||
|
# particularly tricky.
|
||||||
|
export PASS_SYNAPSE_LOG_TESTING=1
|
||||||
|
|
||||||
|
if [ -n "$skip_complement_run" ]; then
|
||||||
|
echo "Skipping Complement run as requested."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the tests!
|
||||||
|
echo "Running Complement with ${test_args[@]} $@ ${test_packages[@]}"
|
||||||
|
cd "$COMPLEMENT_DIR"
|
||||||
|
go test "${test_args[@]}" "$@" "${test_packages[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
|
# For any non-zero exit code (indicating some sort of error happened), we want to exit
|
||||||
|
# with that code.
|
||||||
|
exit_code=$?
|
||||||
|
if [ $exit_code -ne 0 ]; then
|
||||||
|
exit $exit_code
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$ASYNCIO_REACTOR" ]]; then
|
|
||||||
# Enable the Twisted asyncio reactor
|
|
||||||
export PASS_SYNAPSE_COMPLEMENT_USE_ASYNCIO_REACTOR=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "$UNIX_SOCKETS" ]]; then
|
|
||||||
# Enable full on Unix socket mode for Synapse, Redis and Postgresql
|
|
||||||
export PASS_SYNAPSE_USE_UNIX_SOCKET=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "$SYNAPSE_TEST_LOG_LEVEL" ]]; then
|
|
||||||
# Set the log level to what is desired
|
|
||||||
export PASS_SYNAPSE_LOG_LEVEL="$SYNAPSE_TEST_LOG_LEVEL"
|
|
||||||
|
|
||||||
# Allow logging sensitive things (currently SQL queries & parameters).
|
|
||||||
# (This won't have any effect if we're not logging at DEBUG level overall.)
|
|
||||||
# Since this is just a test suite, this is fine and won't reveal anyone's
|
|
||||||
# personal information
|
|
||||||
export PASS_SYNAPSE_LOG_SENSITIVE=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Log a few more useful things for a developer attempting to debug something
|
|
||||||
# particularly tricky.
|
|
||||||
export PASS_SYNAPSE_LOG_TESTING=1
|
|
||||||
|
|
||||||
# Run the tests!
|
|
||||||
echo "Images built; running complement with ${extra_test_args[@]} $@ ${test_packages[@]}"
|
|
||||||
cd "$COMPLEMENT_DIR"
|
|
||||||
|
|
||||||
go test -v -tags "synapse_blacklist" -count=1 "${extra_test_args[@]}" "$@" "${test_packages[@]}"
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import time
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
from os import path
|
from os import path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import Any, Match
|
from typing import Any
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
import click
|
import click
|
||||||
@@ -291,6 +291,12 @@ def _prepare() -> None:
|
|||||||
synapse_repo.git.add("-u")
|
synapse_repo.git.add("-u")
|
||||||
subprocess.run("git diff --cached", shell=True)
|
subprocess.run("git diff --cached", shell=True)
|
||||||
|
|
||||||
|
print(
|
||||||
|
"Consider any upcoming platform deprecations that should be mentioned in the changelog. (e.g. upcoming Python, PostgreSQL or SQLite deprecations)"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"Platform deprecations should be mentioned at least 1 release prior to being unsupported."
|
||||||
|
)
|
||||||
if click.confirm("Edit changelog?", default=False):
|
if click.confirm("Edit changelog?", default=False):
|
||||||
click.edit(filename="CHANGES.md")
|
click.edit(filename="CHANGES.md")
|
||||||
|
|
||||||
@@ -962,10 +968,6 @@ def generate_and_write_changelog(
|
|||||||
new_changes = new_changes.replace(
|
new_changes = new_changes.replace(
|
||||||
"No significant changes.", f"No significant changes since {current_version}."
|
"No significant changes.", f"No significant changes since {current_version}."
|
||||||
)
|
)
|
||||||
new_changes += build_dependabot_changelog(
|
|
||||||
repo,
|
|
||||||
current_version,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Prepend changes to changelog
|
# Prepend changes to changelog
|
||||||
with open("CHANGES.md", "r+") as f:
|
with open("CHANGES.md", "r+") as f:
|
||||||
@@ -980,49 +982,5 @@ def generate_and_write_changelog(
|
|||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
|
|
||||||
|
|
||||||
def build_dependabot_changelog(repo: Repo, current_version: version.Version) -> str:
|
|
||||||
"""Summarise dependabot commits between `current_version` and `release_branch`.
|
|
||||||
|
|
||||||
Returns an empty string if there have been no such commits; otherwise outputs a
|
|
||||||
third-level markdown header followed by an unordered list."""
|
|
||||||
last_release_commit = repo.tag("v" + str(current_version)).commit
|
|
||||||
rev_spec = f"{last_release_commit.hexsha}.."
|
|
||||||
commits = list(git.objects.Commit.iter_items(repo, rev_spec))
|
|
||||||
messages = []
|
|
||||||
for commit in reversed(commits):
|
|
||||||
if commit.author.name == "dependabot[bot]":
|
|
||||||
message: str | bytes = commit.message
|
|
||||||
if isinstance(message, bytes):
|
|
||||||
message = message.decode("utf-8")
|
|
||||||
messages.append(message.split("\n", maxsplit=1)[0])
|
|
||||||
|
|
||||||
if not messages:
|
|
||||||
print(f"No dependabot commits in range {rev_spec}", file=sys.stderr)
|
|
||||||
return ""
|
|
||||||
|
|
||||||
messages.sort()
|
|
||||||
|
|
||||||
def replacer(match: Match[str]) -> str:
|
|
||||||
desc = match.group(1)
|
|
||||||
number = match.group(2)
|
|
||||||
return f"* {desc}. ([\\#{number}](https://github.com/element-hq/synapse/issues/{number}))"
|
|
||||||
|
|
||||||
for i, message in enumerate(messages):
|
|
||||||
messages[i] = re.sub(r"(.*) \(#(\d+)\)$", replacer, message)
|
|
||||||
messages.insert(0, "### Updates to locked dependencies\n")
|
|
||||||
# Add an extra blank line to the bottom of the section
|
|
||||||
messages.append("")
|
|
||||||
return "\n".join(messages)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.argument("since")
|
|
||||||
def test_dependabot_changelog(since: str) -> None:
|
|
||||||
"""Test building the dependabot changelog.
|
|
||||||
|
|
||||||
Summarises all dependabot commits between the SINCE tag and the current git HEAD."""
|
|
||||||
print(build_dependabot_changelog(git.Repo("."), version.Version(since)))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
cli()
|
cli()
|
||||||
|
|||||||
@@ -29,6 +29,19 @@ from typing import Final
|
|||||||
# the max size of a (canonical-json-encoded) event
|
# the max size of a (canonical-json-encoded) event
|
||||||
MAX_PDU_SIZE = 65536
|
MAX_PDU_SIZE = 65536
|
||||||
|
|
||||||
|
# The maximum allowed size of an HTTP request.
|
||||||
|
# Other than media uploads, the biggest request we expect to see is a fully-loaded
|
||||||
|
# /federation/v1/send request.
|
||||||
|
#
|
||||||
|
# The main thing in such a request is up to 50 PDUs, and up to 100 EDUs. PDUs are
|
||||||
|
# limited to 65536 bytes (possibly slightly more if the sender didn't use canonical
|
||||||
|
# json encoding); there is no specced limit to EDUs (see
|
||||||
|
# https://github.com/matrix-org/matrix-doc/issues/3121).
|
||||||
|
#
|
||||||
|
# in short, we somewhat arbitrarily limit requests to 200 * 64K (about 12.5M)
|
||||||
|
#
|
||||||
|
MAX_REQUEST_SIZE = 200 * MAX_PDU_SIZE
|
||||||
|
|
||||||
# Max/min size of ints in canonical JSON
|
# Max/min size of ints in canonical JSON
|
||||||
CANONICALJSON_MAX_INT = (2**53) - 1
|
CANONICALJSON_MAX_INT = (2**53) - 1
|
||||||
CANONICALJSON_MIN_INT = -CANONICALJSON_MAX_INT
|
CANONICALJSON_MIN_INT = -CANONICALJSON_MAX_INT
|
||||||
@@ -307,6 +320,10 @@ class AccountDataTypes:
|
|||||||
MSC4155_INVITE_PERMISSION_CONFIG: Final = (
|
MSC4155_INVITE_PERMISSION_CONFIG: Final = (
|
||||||
"org.matrix.msc4155.invite_permission_config"
|
"org.matrix.msc4155.invite_permission_config"
|
||||||
)
|
)
|
||||||
|
# MSC4380: Invite blocking
|
||||||
|
MSC4380_INVITE_PERMISSION_CONFIG: Final = (
|
||||||
|
"org.matrix.msc4380.invite_permission_config"
|
||||||
|
)
|
||||||
# Synapse-specific behaviour. See "Client-Server API Extensions" documentation
|
# Synapse-specific behaviour. See "Client-Server API Extensions" documentation
|
||||||
# in Admin API for more information.
|
# in Admin API for more information.
|
||||||
SYNAPSE_ADMIN_CLIENT_CONFIG: Final = "io.element.synapse.admin_client_config"
|
SYNAPSE_ADMIN_CLIENT_CONFIG: Final = "io.element.synapse.admin_client_config"
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class Codes(str, Enum):
|
|||||||
PROFILE_TOO_LARGE = "M_PROFILE_TOO_LARGE"
|
PROFILE_TOO_LARGE = "M_PROFILE_TOO_LARGE"
|
||||||
KEY_TOO_LARGE = "M_KEY_TOO_LARGE"
|
KEY_TOO_LARGE = "M_KEY_TOO_LARGE"
|
||||||
|
|
||||||
# Part of MSC4155
|
# Part of MSC4155/MSC4380
|
||||||
INVITE_BLOCKED = "ORG.MATRIX.MSC4155.M_INVITE_BLOCKED"
|
INVITE_BLOCKED = "ORG.MATRIX.MSC4155.M_INVITE_BLOCKED"
|
||||||
|
|
||||||
# Part of MSC4190
|
# Part of MSC4190
|
||||||
@@ -856,6 +856,12 @@ class HttpResponseException(CodeMessageException):
|
|||||||
return ProxiedRequestError(self.code, errmsg, errcode, j)
|
return ProxiedRequestError(self.code, errmsg, errcode, j)
|
||||||
|
|
||||||
|
|
||||||
|
class HomeServerNotSetupException(Exception):
|
||||||
|
"""
|
||||||
|
Raised when an operation is attempted on the HomeServer before setup() has been called.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ShadowBanError(Exception):
|
class ShadowBanError(Exception):
|
||||||
"""
|
"""
|
||||||
Raised when a shadow-banned user attempts to perform an action.
|
Raised when a shadow-banned user attempts to perform an action.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from synapse.config.ratelimiting import RatelimitSettings
|
|||||||
from synapse.storage.databases.main import DataStore
|
from synapse.storage.databases.main import DataStore
|
||||||
from synapse.types import Requester
|
from synapse.types import Requester
|
||||||
from synapse.util.clock import Clock
|
from synapse.util.clock import Clock
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.wheel_timer import WheelTimer
|
from synapse.util.wheel_timer import WheelTimer
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -100,7 +101,7 @@ class Ratelimiter:
|
|||||||
# and doesn't affect correctness.
|
# and doesn't affect correctness.
|
||||||
self._timer: WheelTimer[Hashable] = WheelTimer()
|
self._timer: WheelTimer[Hashable] = WheelTimer()
|
||||||
|
|
||||||
self.clock.looping_call(self._prune_message_counts, 15 * 1000)
|
self.clock.looping_call(self._prune_message_counts, Duration(seconds=15))
|
||||||
|
|
||||||
def _get_key(self, requester: Requester | None, key: Hashable | None) -> Hashable:
|
def _get_key(self, requester: Requester | None, key: Hashable | None) -> Hashable:
|
||||||
"""Use the requester's MXID as a fallback key if no key is provided."""
|
"""Use the requester's MXID as a fallback key if no key is provided."""
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ from typing import (
|
|||||||
from wsgiref.simple_server import WSGIServer
|
from wsgiref.simple_server import WSGIServer
|
||||||
|
|
||||||
from cryptography.utils import CryptographyDeprecationWarning
|
from cryptography.utils import CryptographyDeprecationWarning
|
||||||
from typing_extensions import ParamSpec
|
from typing_extensions import ParamSpec, assert_never
|
||||||
|
|
||||||
import twisted
|
import twisted
|
||||||
from twisted.internet import defer, error, reactor as _reactor
|
from twisted.internet import defer, error, reactor as _reactor
|
||||||
@@ -59,12 +59,17 @@ from twisted.python.threadpool import ThreadPool
|
|||||||
from twisted.web.resource import Resource
|
from twisted.web.resource import Resource
|
||||||
|
|
||||||
import synapse.util.caches
|
import synapse.util.caches
|
||||||
from synapse.api.constants import MAX_PDU_SIZE
|
from synapse.api.constants import MAX_REQUEST_SIZE
|
||||||
from synapse.app import check_bind_error
|
from synapse.app import check_bind_error
|
||||||
from synapse.config import ConfigError
|
from synapse.config import ConfigError
|
||||||
from synapse.config._base import format_config_error
|
from synapse.config._base import format_config_error
|
||||||
from synapse.config.homeserver import HomeServerConfig
|
from synapse.config.homeserver import HomeServerConfig
|
||||||
from synapse.config.server import ListenerConfig, ManholeConfig, TCPListenerConfig
|
from synapse.config.server import (
|
||||||
|
ListenerConfig,
|
||||||
|
ManholeConfig,
|
||||||
|
TCPListenerConfig,
|
||||||
|
UnixListenerConfig,
|
||||||
|
)
|
||||||
from synapse.crypto import context_factory
|
from synapse.crypto import context_factory
|
||||||
from synapse.events.auto_accept_invites import InviteAutoAccepter
|
from synapse.events.auto_accept_invites import InviteAutoAccepter
|
||||||
from synapse.events.presence_router import load_legacy_presence_router
|
from synapse.events.presence_router import load_legacy_presence_router
|
||||||
@@ -413,6 +418,37 @@ def listen_unix(
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ListenerException(RuntimeError):
|
||||||
|
"""
|
||||||
|
An exception raised when we fail to listen with the given `ListenerConfig`.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
listener_config: The listener config that caused the exception.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
listener_config: ListenerConfig,
|
||||||
|
):
|
||||||
|
listener_human_name = ""
|
||||||
|
port = ""
|
||||||
|
if isinstance(listener_config, TCPListenerConfig):
|
||||||
|
listener_human_name = "TCP port"
|
||||||
|
port = str(listener_config.port)
|
||||||
|
elif isinstance(listener_config, UnixListenerConfig):
|
||||||
|
listener_human_name = "unix socket"
|
||||||
|
port = listener_config.path
|
||||||
|
else:
|
||||||
|
assert_never(listener_config)
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
"Failed to listen on %s (%s) with the given listener config: %s"
|
||||||
|
% (listener_human_name, port, listener_config)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.listener_config = listener_config
|
||||||
|
|
||||||
|
|
||||||
def listen_http(
|
def listen_http(
|
||||||
hs: "HomeServer",
|
hs: "HomeServer",
|
||||||
listener_config: ListenerConfig,
|
listener_config: ListenerConfig,
|
||||||
@@ -447,39 +483,55 @@ def listen_http(
|
|||||||
hs=hs,
|
hs=hs,
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(listener_config, TCPListenerConfig):
|
try:
|
||||||
if listener_config.is_tls():
|
if isinstance(listener_config, TCPListenerConfig):
|
||||||
# refresh_certificate should have been called before this.
|
if listener_config.is_tls():
|
||||||
assert context_factory is not None
|
# refresh_certificate should have been called before this.
|
||||||
ports = listen_ssl(
|
assert context_factory is not None
|
||||||
listener_config.bind_addresses,
|
ports = listen_ssl(
|
||||||
listener_config.port,
|
listener_config.bind_addresses,
|
||||||
site,
|
listener_config.port,
|
||||||
context_factory,
|
site,
|
||||||
reactor=reactor,
|
context_factory,
|
||||||
|
reactor=reactor,
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Synapse now listening on TCP port %d (TLS)", listener_config.port
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ports = listen_tcp(
|
||||||
|
listener_config.bind_addresses,
|
||||||
|
listener_config.port,
|
||||||
|
site,
|
||||||
|
reactor=reactor,
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Synapse now listening on TCP port %d", listener_config.port
|
||||||
|
)
|
||||||
|
|
||||||
|
elif isinstance(listener_config, UnixListenerConfig):
|
||||||
|
ports = listen_unix(
|
||||||
|
listener_config.path, listener_config.mode, site, reactor=reactor
|
||||||
)
|
)
|
||||||
|
# getHost() returns a UNIXAddress which contains an instance variable of 'name'
|
||||||
|
# encoded as a byte string. Decode as utf-8 so pretty.
|
||||||
logger.info(
|
logger.info(
|
||||||
"Synapse now listening on TCP port %d (TLS)", listener_config.port
|
"Synapse now listening on Unix Socket at: %s",
|
||||||
|
ports[0].getHost().name.decode("utf-8"),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
ports = listen_tcp(
|
assert_never(listener_config)
|
||||||
listener_config.bind_addresses,
|
except Exception as exc:
|
||||||
listener_config.port,
|
# The Twisted interface says that "Users should not call this function
|
||||||
site,
|
# themselves!" but this appears to be the correct/only way handle proper cleanup
|
||||||
reactor=reactor,
|
# of the site when things go wrong. In the normal case, a `Port` is created
|
||||||
)
|
# which we can call `Port.stopListening()` on to do the same thing (but no
|
||||||
logger.info("Synapse now listening on TCP port %d", listener_config.port)
|
# `Port` is created when an error occurs).
|
||||||
|
#
|
||||||
else:
|
# We use `site.stopFactory()` instead of `site.doStop()` as the latter assumes
|
||||||
ports = listen_unix(
|
# that `site.doStart()` was called (which won't be the case if an error occurs).
|
||||||
listener_config.path, listener_config.mode, site, reactor=reactor
|
site.stopFactory()
|
||||||
)
|
raise ListenerException(listener_config) from exc
|
||||||
# getHost() returns a UNIXAddress which contains an instance variable of 'name'
|
|
||||||
# encoded as a byte string. Decode as utf-8 so pretty.
|
|
||||||
logger.info(
|
|
||||||
"Synapse now listening on Unix Socket at: %s",
|
|
||||||
ports[0].getHost().name.decode("utf-8"),
|
|
||||||
)
|
|
||||||
|
|
||||||
return ports
|
return ports
|
||||||
|
|
||||||
@@ -843,17 +895,8 @@ def sdnotify(state: bytes) -> None:
|
|||||||
def max_request_body_size(config: HomeServerConfig) -> int:
|
def max_request_body_size(config: HomeServerConfig) -> int:
|
||||||
"""Get a suitable maximum size for incoming HTTP requests"""
|
"""Get a suitable maximum size for incoming HTTP requests"""
|
||||||
|
|
||||||
# Other than media uploads, the biggest request we expect to see is a fully-loaded
|
# Baseline default for any request that isn't configured in the homeserver config
|
||||||
# /federation/v1/send request.
|
max_request_size = MAX_REQUEST_SIZE
|
||||||
#
|
|
||||||
# The main thing in such a request is up to 50 PDUs, and up to 100 EDUs. PDUs are
|
|
||||||
# limited to 65536 bytes (possibly slightly more if the sender didn't use canonical
|
|
||||||
# json encoding); there is no specced limit to EDUs (see
|
|
||||||
# https://github.com/matrix-org/matrix-doc/issues/3121).
|
|
||||||
#
|
|
||||||
# in short, we somewhat arbitrarily limit requests to 200 * 64K (about 12.5M)
|
|
||||||
#
|
|
||||||
max_request_size = 200 * MAX_PDU_SIZE
|
|
||||||
|
|
||||||
# if we have a media repo enabled, we may need to allow larger uploads than that
|
# if we have a media repo enabled, we may need to allow larger uploads than that
|
||||||
if config.media.can_load_media_repo:
|
if config.media.can_load_media_repo:
|
||||||
|
|||||||
@@ -30,24 +30,20 @@ from twisted.internet import defer
|
|||||||
|
|
||||||
from synapse.metrics import SERVER_NAME_LABEL
|
from synapse.metrics import SERVER_NAME_LABEL
|
||||||
from synapse.types import JsonDict
|
from synapse.types import JsonDict
|
||||||
from synapse.util.constants import (
|
from synapse.util.duration import Duration
|
||||||
MILLISECONDS_PER_SECOND,
|
|
||||||
ONE_HOUR_SECONDS,
|
|
||||||
ONE_MINUTE_SECONDS,
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
|
||||||
logger = logging.getLogger("synapse.app.homeserver")
|
logger = logging.getLogger("synapse.app.homeserver")
|
||||||
|
|
||||||
INITIAL_DELAY_BEFORE_FIRST_PHONE_HOME_SECONDS = 5 * ONE_MINUTE_SECONDS
|
INITIAL_DELAY_BEFORE_FIRST_PHONE_HOME = Duration(minutes=5)
|
||||||
"""
|
"""
|
||||||
We wait 5 minutes to send the first set of stats as the server can be quite busy the
|
We wait 5 minutes to send the first set of stats as the server can be quite busy the
|
||||||
first few minutes
|
first few minutes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
PHONE_HOME_INTERVAL_SECONDS = 3 * ONE_HOUR_SECONDS
|
PHONE_HOME_INTERVAL = Duration(hours=3)
|
||||||
"""
|
"""
|
||||||
Phone home stats are sent every 3 hours
|
Phone home stats are sent every 3 hours
|
||||||
"""
|
"""
|
||||||
@@ -222,13 +218,13 @@ def start_phone_stats_home(hs: "HomeServer") -> None:
|
|||||||
# table will decrease
|
# table will decrease
|
||||||
clock.looping_call(
|
clock.looping_call(
|
||||||
hs.get_datastores().main.generate_user_daily_visits,
|
hs.get_datastores().main.generate_user_daily_visits,
|
||||||
5 * ONE_MINUTE_SECONDS * MILLISECONDS_PER_SECOND,
|
Duration(minutes=5),
|
||||||
)
|
)
|
||||||
|
|
||||||
# monthly active user limiting functionality
|
# monthly active user limiting functionality
|
||||||
clock.looping_call(
|
clock.looping_call(
|
||||||
hs.get_datastores().main.reap_monthly_active_users,
|
hs.get_datastores().main.reap_monthly_active_users,
|
||||||
ONE_HOUR_SECONDS * MILLISECONDS_PER_SECOND,
|
Duration(hours=1),
|
||||||
)
|
)
|
||||||
hs.get_datastores().main.reap_monthly_active_users()
|
hs.get_datastores().main.reap_monthly_active_users()
|
||||||
|
|
||||||
@@ -267,14 +263,14 @@ def start_phone_stats_home(hs: "HomeServer") -> None:
|
|||||||
|
|
||||||
if hs.config.server.limit_usage_by_mau or hs.config.server.mau_stats_only:
|
if hs.config.server.limit_usage_by_mau or hs.config.server.mau_stats_only:
|
||||||
generate_monthly_active_users()
|
generate_monthly_active_users()
|
||||||
clock.looping_call(generate_monthly_active_users, 5 * 60 * 1000)
|
clock.looping_call(generate_monthly_active_users, Duration(minutes=5))
|
||||||
# End of monthly active user settings
|
# End of monthly active user settings
|
||||||
|
|
||||||
if hs.config.metrics.report_stats:
|
if hs.config.metrics.report_stats:
|
||||||
logger.info("Scheduling stats reporting for 3 hour intervals")
|
logger.info("Scheduling stats reporting for 3 hour intervals")
|
||||||
clock.looping_call(
|
clock.looping_call(
|
||||||
phone_stats_home,
|
phone_stats_home,
|
||||||
PHONE_HOME_INTERVAL_SECONDS * MILLISECONDS_PER_SECOND,
|
PHONE_HOME_INTERVAL,
|
||||||
hs,
|
hs,
|
||||||
stats,
|
stats,
|
||||||
)
|
)
|
||||||
@@ -282,14 +278,14 @@ def start_phone_stats_home(hs: "HomeServer") -> None:
|
|||||||
# We need to defer this init for the cases that we daemonize
|
# We need to defer this init for the cases that we daemonize
|
||||||
# otherwise the process ID we get is that of the non-daemon process
|
# otherwise the process ID we get is that of the non-daemon process
|
||||||
clock.call_later(
|
clock.call_later(
|
||||||
0,
|
Duration(seconds=0),
|
||||||
performance_stats_init,
|
performance_stats_init,
|
||||||
)
|
)
|
||||||
|
|
||||||
# We wait 5 minutes to send the first set of stats as the server can
|
# We wait 5 minutes to send the first set of stats as the server can
|
||||||
# be quite busy the first few minutes
|
# be quite busy the first few minutes
|
||||||
clock.call_later(
|
clock.call_later(
|
||||||
INITIAL_DELAY_BEFORE_FIRST_PHONE_HOME_SECONDS,
|
INITIAL_DELAY_BEFORE_FIRST_PHONE_HOME,
|
||||||
phone_stats_home,
|
phone_stats_home,
|
||||||
hs,
|
hs,
|
||||||
stats,
|
stats,
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ from synapse.logging.context import run_in_background
|
|||||||
from synapse.storage.databases.main import DataStore
|
from synapse.storage.databases.main import DataStore
|
||||||
from synapse.types import DeviceListUpdates, JsonMapping
|
from synapse.types import DeviceListUpdates, JsonMapping
|
||||||
from synapse.util.clock import Clock, DelayedCallWrapper
|
from synapse.util.clock import Clock, DelayedCallWrapper
|
||||||
|
from synapse.util.duration import Duration
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
@@ -504,8 +505,8 @@ class _Recoverer:
|
|||||||
self.scheduled_recovery: DelayedCallWrapper | None = None
|
self.scheduled_recovery: DelayedCallWrapper | None = None
|
||||||
|
|
||||||
def recover(self) -> None:
|
def recover(self) -> None:
|
||||||
delay = 2**self.backoff_counter
|
delay = Duration(seconds=2**self.backoff_counter)
|
||||||
logger.info("Scheduling retries on %s in %fs", self.service.id, delay)
|
logger.info("Scheduling retries on %s in %fs", self.service.id, delay.as_secs())
|
||||||
self.scheduled_recovery = self.clock.call_later(
|
self.scheduled_recovery = self.clock.call_later(
|
||||||
delay,
|
delay,
|
||||||
self.hs.run_as_background_process,
|
self.hs.run_as_background_process,
|
||||||
|
|||||||
@@ -672,7 +672,8 @@ class RootConfig:
|
|||||||
action="append",
|
action="append",
|
||||||
metavar="CONFIG_FILE",
|
metavar="CONFIG_FILE",
|
||||||
help="Specify config file. Can be given multiple times and"
|
help="Specify config file. Can be given multiple times and"
|
||||||
" may specify directories containing *.yaml files.",
|
" may specify directories containing *.yaml files."
|
||||||
|
" Top-level keys in later files overwrite ones in earlier files.",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--no-secrets-in-config",
|
"--no-secrets-in-config",
|
||||||
|
|||||||
@@ -438,6 +438,9 @@ class ExperimentalConfig(Config):
|
|||||||
# previously calculated push actions.
|
# previously calculated push actions.
|
||||||
self.msc2654_enabled: bool = experimental.get("msc2654_enabled", False)
|
self.msc2654_enabled: bool = experimental.get("msc2654_enabled", False)
|
||||||
|
|
||||||
|
# MSC2666: Query mutual rooms between two users.
|
||||||
|
self.msc2666_enabled: bool = experimental.get("msc2666_enabled", False)
|
||||||
|
|
||||||
# MSC2815 (allow room moderators to view redacted event content)
|
# MSC2815 (allow room moderators to view redacted event content)
|
||||||
self.msc2815_enabled: bool = experimental.get("msc2815_enabled", False)
|
self.msc2815_enabled: bool = experimental.get("msc2815_enabled", False)
|
||||||
|
|
||||||
@@ -593,3 +596,6 @@ class ExperimentalConfig(Config):
|
|||||||
# MSC4306: Thread Subscriptions
|
# MSC4306: Thread Subscriptions
|
||||||
# (and MSC4308: Thread Subscriptions extension to Sliding Sync)
|
# (and MSC4308: Thread Subscriptions extension to Sliding Sync)
|
||||||
self.msc4306_enabled: bool = experimental.get("msc4306_enabled", False)
|
self.msc4306_enabled: bool = experimental.get("msc4306_enabled", False)
|
||||||
|
|
||||||
|
# MSC4380: Invite blocking
|
||||||
|
self.msc4380_enabled: bool = experimental.get("msc4380_enabled", False)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
import abc
|
import abc
|
||||||
import logging
|
import logging
|
||||||
|
from contextlib import ExitStack
|
||||||
from typing import TYPE_CHECKING, Callable, Iterable
|
from typing import TYPE_CHECKING, Callable, Iterable
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
@@ -150,57 +151,81 @@ class Keyring:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, hs: "HomeServer", key_fetchers: "Iterable[KeyFetcher] | None" = None
|
self,
|
||||||
|
hs: "HomeServer",
|
||||||
|
test_only_key_fetchers: "list[KeyFetcher] | None" = None,
|
||||||
):
|
):
|
||||||
self.server_name = hs.hostname
|
"""
|
||||||
|
Args:
|
||||||
|
hs: The HomeServer instance
|
||||||
|
test_only_key_fetchers: Dependency injection for tests only. If provided,
|
||||||
|
these key fetchers will be used instead of the default ones.
|
||||||
|
"""
|
||||||
|
# Clean-up to avoid partial initialization leaving behind references.
|
||||||
|
with ExitStack() as exit:
|
||||||
|
self.server_name = hs.hostname
|
||||||
|
|
||||||
if key_fetchers is None:
|
self._key_fetchers: list[KeyFetcher] = []
|
||||||
# Always fetch keys from the database.
|
if test_only_key_fetchers is None:
|
||||||
mutable_key_fetchers: list[KeyFetcher] = [StoreKeyFetcher(hs)]
|
# Always fetch keys from the database.
|
||||||
# Fetch keys from configured trusted key servers, if any exist.
|
store_key_fetcher = StoreKeyFetcher(hs)
|
||||||
key_servers = hs.config.key.key_servers
|
exit.callback(store_key_fetcher.shutdown)
|
||||||
if key_servers:
|
self._key_fetchers.append(store_key_fetcher)
|
||||||
mutable_key_fetchers.append(PerspectivesKeyFetcher(hs))
|
|
||||||
# Finally, fetch keys from the origin server directly.
|
|
||||||
mutable_key_fetchers.append(ServerKeyFetcher(hs))
|
|
||||||
|
|
||||||
self._key_fetchers: Iterable[KeyFetcher] = tuple(mutable_key_fetchers)
|
# Fetch keys from configured trusted key servers, if any exist.
|
||||||
else:
|
key_servers = hs.config.key.key_servers
|
||||||
self._key_fetchers = key_fetchers
|
if key_servers:
|
||||||
|
perspectives_key_fetcher = PerspectivesKeyFetcher(hs)
|
||||||
|
exit.callback(perspectives_key_fetcher.shutdown)
|
||||||
|
self._key_fetchers.append(perspectives_key_fetcher)
|
||||||
|
|
||||||
self._fetch_keys_queue: BatchingQueue[
|
# Finally, fetch keys from the origin server directly.
|
||||||
_FetchKeyRequest, dict[str, dict[str, FetchKeyResult]]
|
server_key_fetcher = ServerKeyFetcher(hs)
|
||||||
] = BatchingQueue(
|
exit.callback(server_key_fetcher.shutdown)
|
||||||
name="keyring_server",
|
self._key_fetchers.append(server_key_fetcher)
|
||||||
hs=hs,
|
else:
|
||||||
clock=hs.get_clock(),
|
self._key_fetchers = test_only_key_fetchers
|
||||||
# The method called to fetch each key
|
|
||||||
process_batch_callback=self._inner_fetch_key_requests,
|
|
||||||
)
|
|
||||||
|
|
||||||
self._is_mine_server_name = hs.is_mine_server_name
|
self._fetch_keys_queue: BatchingQueue[
|
||||||
|
_FetchKeyRequest, dict[str, dict[str, FetchKeyResult]]
|
||||||
|
] = BatchingQueue(
|
||||||
|
name="keyring_server",
|
||||||
|
hs=hs,
|
||||||
|
clock=hs.get_clock(),
|
||||||
|
# The method called to fetch each key
|
||||||
|
process_batch_callback=self._inner_fetch_key_requests,
|
||||||
|
)
|
||||||
|
exit.callback(self._fetch_keys_queue.shutdown)
|
||||||
|
|
||||||
# build a FetchKeyResult for each of our own keys, to shortcircuit the
|
self._is_mine_server_name = hs.is_mine_server_name
|
||||||
# fetcher.
|
|
||||||
self._local_verify_keys: dict[str, FetchKeyResult] = {}
|
# build a FetchKeyResult for each of our own keys, to shortcircuit the
|
||||||
for key_id, key in hs.config.key.old_signing_keys.items():
|
# fetcher.
|
||||||
self._local_verify_keys[key_id] = FetchKeyResult(
|
self._local_verify_keys: dict[str, FetchKeyResult] = {}
|
||||||
verify_key=key, valid_until_ts=key.expired
|
for key_id, key in hs.config.key.old_signing_keys.items():
|
||||||
|
self._local_verify_keys[key_id] = FetchKeyResult(
|
||||||
|
verify_key=key, valid_until_ts=key.expired
|
||||||
|
)
|
||||||
|
|
||||||
|
vk = get_verify_key(hs.signing_key)
|
||||||
|
self._local_verify_keys[f"{vk.alg}:{vk.version}"] = FetchKeyResult(
|
||||||
|
verify_key=vk,
|
||||||
|
valid_until_ts=2**63, # fake future timestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
vk = get_verify_key(hs.signing_key)
|
# We reached the end of the block which means everything was successful, so
|
||||||
self._local_verify_keys[f"{vk.alg}:{vk.version}"] = FetchKeyResult(
|
# no exit handlers are needed (remove them all).
|
||||||
verify_key=vk,
|
exit.pop_all()
|
||||||
valid_until_ts=2**63, # fake future timestamp
|
|
||||||
)
|
|
||||||
|
|
||||||
def shutdown(self) -> None:
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Prepares the KeyRing for garbage collection by shutting down it's queues.
|
Prepares the KeyRing for garbage collection by shutting down it's queues.
|
||||||
"""
|
"""
|
||||||
self._fetch_keys_queue.shutdown()
|
self._fetch_keys_queue.shutdown()
|
||||||
|
|
||||||
for key_fetcher in self._key_fetchers:
|
for key_fetcher in self._key_fetchers:
|
||||||
key_fetcher.shutdown()
|
key_fetcher.shutdown()
|
||||||
|
self._key_fetchers.clear()
|
||||||
|
|
||||||
async def verify_json_for_server(
|
async def verify_json_for_server(
|
||||||
self,
|
self,
|
||||||
@@ -521,9 +546,21 @@ class StoreKeyFetcher(KeyFetcher):
|
|||||||
"""KeyFetcher impl which fetches keys from our data store"""
|
"""KeyFetcher impl which fetches keys from our data store"""
|
||||||
|
|
||||||
def __init__(self, hs: "HomeServer"):
|
def __init__(self, hs: "HomeServer"):
|
||||||
super().__init__(hs)
|
# Clean-up to avoid partial initialization leaving behind references.
|
||||||
|
with ExitStack() as exit:
|
||||||
|
super().__init__(hs)
|
||||||
|
# `KeyFetcher` keeps a reference to `hs` which we need to clean up if
|
||||||
|
# something goes wrong so we can cleanly shutdown the homeserver.
|
||||||
|
exit.callback(super().shutdown)
|
||||||
|
|
||||||
self.store = hs.get_datastores().main
|
# An error can be raised here if someone tried to create a `StoreKeyFetcher`
|
||||||
|
# before the homeserver is fully set up (`HomeServerNotSetupException:
|
||||||
|
# HomeServer.setup must be called before getting datastores`).
|
||||||
|
self.store = hs.get_datastores().main
|
||||||
|
|
||||||
|
# We reached the end of the block which means everything was successful, so
|
||||||
|
# no exit handlers are needed (remove them all).
|
||||||
|
exit.pop_all()
|
||||||
|
|
||||||
async def _fetch_keys(
|
async def _fetch_keys(
|
||||||
self, keys_to_fetch: list[_FetchKeyRequest]
|
self, keys_to_fetch: list[_FetchKeyRequest]
|
||||||
@@ -543,9 +580,21 @@ class StoreKeyFetcher(KeyFetcher):
|
|||||||
|
|
||||||
class BaseV2KeyFetcher(KeyFetcher):
|
class BaseV2KeyFetcher(KeyFetcher):
|
||||||
def __init__(self, hs: "HomeServer"):
|
def __init__(self, hs: "HomeServer"):
|
||||||
super().__init__(hs)
|
# Clean-up to avoid partial initialization leaving behind references.
|
||||||
|
with ExitStack() as exit:
|
||||||
|
super().__init__(hs)
|
||||||
|
# `KeyFetcher` keeps a reference to `hs` which we need to clean up if
|
||||||
|
# something goes wrong so we can cleanly shutdown the homeserver.
|
||||||
|
exit.callback(super().shutdown)
|
||||||
|
|
||||||
self.store = hs.get_datastores().main
|
# An error can be raised here if someone tried to create a `StoreKeyFetcher`
|
||||||
|
# before the homeserver is fully set up (`HomeServerNotSetupException:
|
||||||
|
# HomeServer.setup must be called before getting datastores`).
|
||||||
|
self.store = hs.get_datastores().main
|
||||||
|
|
||||||
|
# We reached the end of the block which means everything was successful, so
|
||||||
|
# no exit handlers are needed (remove them all).
|
||||||
|
exit.pop_all()
|
||||||
|
|
||||||
async def process_v2_response(
|
async def process_v2_response(
|
||||||
self, from_server: str, response_json: JsonDict, time_added_ms: int
|
self, from_server: str, response_json: JsonDict, time_added_ms: int
|
||||||
|
|||||||
@@ -548,7 +548,7 @@ class FrozenEventV4(FrozenEventV3):
|
|||||||
assert create_event_id not in self._dict["auth_events"]
|
assert create_event_id not in self._dict["auth_events"]
|
||||||
if self.type == EventTypes.Create and self.get_state_key() == "":
|
if self.type == EventTypes.Create and self.get_state_key() == "":
|
||||||
return self._dict["auth_events"] # should be []
|
return self._dict["auth_events"] # should be []
|
||||||
return self._dict["auth_events"] + [create_event_id]
|
return [*self._dict["auth_events"], create_event_id]
|
||||||
|
|
||||||
|
|
||||||
def _event_type_from_format_version(
|
def _event_type_from_format_version(
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ from synapse.types import JsonDict, StrCollection, UserID, get_domain_from_id
|
|||||||
from synapse.types.handlers.policy_server import RECOMMENDATION_OK, RECOMMENDATION_SPAM
|
from synapse.types.handlers.policy_server import RECOMMENDATION_OK, RECOMMENDATION_SPAM
|
||||||
from synapse.util.async_helpers import concurrently_execute
|
from synapse.util.async_helpers import concurrently_execute
|
||||||
from synapse.util.caches.expiringcache import ExpiringCache
|
from synapse.util.caches.expiringcache import ExpiringCache
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.retryutils import NotRetryingDestination
|
from synapse.util.retryutils import NotRetryingDestination
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -132,7 +133,7 @@ class FederationClient(FederationBase):
|
|||||||
super().__init__(hs)
|
super().__init__(hs)
|
||||||
|
|
||||||
self.pdu_destination_tried: dict[str, dict[str, int]] = {}
|
self.pdu_destination_tried: dict[str, dict[str, int]] = {}
|
||||||
self._clock.looping_call(self._clear_tried_cache, 60 * 1000)
|
self._clock.looping_call(self._clear_tried_cache, Duration(minutes=1))
|
||||||
self.state = hs.get_state_handler()
|
self.state = hs.get_state_handler()
|
||||||
self.transport_layer = hs.get_federation_transport_client()
|
self.transport_layer = hs.get_federation_transport_client()
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ from synapse.types import JsonDict, StateMap, UserID, get_domain_from_id
|
|||||||
from synapse.util import unwrapFirstError
|
from synapse.util import unwrapFirstError
|
||||||
from synapse.util.async_helpers import Linearizer, concurrently_execute, gather_results
|
from synapse.util.async_helpers import Linearizer, concurrently_execute, gather_results
|
||||||
from synapse.util.caches.response_cache import ResponseCache
|
from synapse.util.caches.response_cache import ResponseCache
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.stringutils import parse_server_name
|
from synapse.util.stringutils import parse_server_name
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -226,7 +227,7 @@ class FederationServer(FederationBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# We pause a bit so that we don't start handling all rooms at once.
|
# We pause a bit so that we don't start handling all rooms at once.
|
||||||
await self._clock.sleep(random.uniform(0, 0.1))
|
await self._clock.sleep(Duration(seconds=random.uniform(0, 0.1)))
|
||||||
|
|
||||||
async def on_backfill_request(
|
async def on_backfill_request(
|
||||||
self, origin: str, room_id: str, versions: list[str], limit: int
|
self, origin: str, room_id: str, versions: list[str], limit: int
|
||||||
@@ -301,7 +302,9 @@ class FederationServer(FederationBase):
|
|||||||
# Start a periodic check for old staged events. This is to handle
|
# Start a periodic check for old staged events. This is to handle
|
||||||
# the case where locks time out, e.g. if another process gets killed
|
# the case where locks time out, e.g. if another process gets killed
|
||||||
# without dropping its locks.
|
# without dropping its locks.
|
||||||
self._clock.looping_call(self._handle_old_staged_events, 60 * 1000)
|
self._clock.looping_call(
|
||||||
|
self._handle_old_staged_events, Duration(minutes=1)
|
||||||
|
)
|
||||||
|
|
||||||
# keep this as early as possible to make the calculated origin ts as
|
# keep this as early as possible to make the calculated origin ts as
|
||||||
# accurate as possible.
|
# accurate as possible.
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ from synapse.federation.sender import AbstractFederationSender, FederationSender
|
|||||||
from synapse.metrics import SERVER_NAME_LABEL, LaterGauge
|
from synapse.metrics import SERVER_NAME_LABEL, LaterGauge
|
||||||
from synapse.replication.tcp.streams.federation import FederationStream
|
from synapse.replication.tcp.streams.federation import FederationStream
|
||||||
from synapse.types import JsonDict, ReadReceipt, RoomStreamToken, StrCollection
|
from synapse.types import JsonDict, ReadReceipt, RoomStreamToken, StrCollection
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
|
|
||||||
from .units import Edu
|
from .units import Edu
|
||||||
@@ -137,7 +138,7 @@ class FederationRemoteSendQueue(AbstractFederationSender):
|
|||||||
assert isinstance(queue, Sized)
|
assert isinstance(queue, Sized)
|
||||||
register(queue_name, queue=queue)
|
register(queue_name, queue=queue)
|
||||||
|
|
||||||
self.clock.looping_call(self._clear_queue, 30 * 1000)
|
self.clock.looping_call(self._clear_queue, Duration(seconds=30))
|
||||||
|
|
||||||
def shutdown(self) -> None:
|
def shutdown(self) -> None:
|
||||||
"""Stops this federation sender instance from sending further transactions."""
|
"""Stops this federation sender instance from sending further transactions."""
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ from synapse.types import (
|
|||||||
get_domain_from_id,
|
get_domain_from_id,
|
||||||
)
|
)
|
||||||
from synapse.util.clock import Clock
|
from synapse.util.clock import Clock
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
from synapse.util.retryutils import filter_destinations_by_retry_limiter
|
from synapse.util.retryutils import filter_destinations_by_retry_limiter
|
||||||
|
|
||||||
@@ -218,12 +219,12 @@ transaction_queue_pending_edus_gauge = LaterGauge(
|
|||||||
# Please note that rate limiting still applies, so while the loop is
|
# Please note that rate limiting still applies, so while the loop is
|
||||||
# executed every X seconds the destinations may not be woken up because
|
# executed every X seconds the destinations may not be woken up because
|
||||||
# they are being rate limited following previous attempt failures.
|
# they are being rate limited following previous attempt failures.
|
||||||
WAKEUP_RETRY_PERIOD_SEC = 60
|
WAKEUP_RETRY_PERIOD = Duration(minutes=1)
|
||||||
|
|
||||||
# Time (in s) to wait in between waking up each destination, i.e. one destination
|
# Time to wait in between waking up each destination, i.e. one destination
|
||||||
# will be woken up every <x> seconds until we have woken every destination
|
# will be woken up every <x> seconds until we have woken every destination
|
||||||
# has outstanding catch-up.
|
# has outstanding catch-up.
|
||||||
WAKEUP_INTERVAL_BETWEEN_DESTINATIONS_SEC = 5
|
WAKEUP_INTERVAL_BETWEEN_DESTINATIONS = Duration(seconds=5)
|
||||||
|
|
||||||
|
|
||||||
class AbstractFederationSender(metaclass=abc.ABCMeta):
|
class AbstractFederationSender(metaclass=abc.ABCMeta):
|
||||||
@@ -379,7 +380,7 @@ class _DestinationWakeupQueue:
|
|||||||
|
|
||||||
queue.attempt_new_transaction()
|
queue.attempt_new_transaction()
|
||||||
|
|
||||||
await self.clock.sleep(current_sleep_seconds)
|
await self.clock.sleep(Duration(seconds=current_sleep_seconds))
|
||||||
|
|
||||||
if not self.queue:
|
if not self.queue:
|
||||||
break
|
break
|
||||||
@@ -468,7 +469,7 @@ class FederationSender(AbstractFederationSender):
|
|||||||
# Regularly wake up destinations that have outstanding PDUs to be caught up
|
# Regularly wake up destinations that have outstanding PDUs to be caught up
|
||||||
self.clock.looping_call_now(
|
self.clock.looping_call_now(
|
||||||
self.hs.run_as_background_process,
|
self.hs.run_as_background_process,
|
||||||
WAKEUP_RETRY_PERIOD_SEC * 1000.0,
|
WAKEUP_RETRY_PERIOD,
|
||||||
"wake_destinations_needing_catchup",
|
"wake_destinations_needing_catchup",
|
||||||
self._wake_destinations_needing_catchup,
|
self._wake_destinations_needing_catchup,
|
||||||
)
|
)
|
||||||
@@ -1161,4 +1162,4 @@ class FederationSender(AbstractFederationSender):
|
|||||||
last_processed,
|
last_processed,
|
||||||
)
|
)
|
||||||
self.wake_destination(destination)
|
self.wake_destination(destination)
|
||||||
await self.clock.sleep(WAKEUP_INTERVAL_BETWEEN_DESTINATIONS_SEC)
|
await self.clock.sleep(WAKEUP_INTERVAL_BETWEEN_DESTINATIONS)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ from synapse.metrics.background_process_metrics import wrap_as_background_proces
|
|||||||
from synapse.types import UserID
|
from synapse.types import UserID
|
||||||
from synapse.util import stringutils
|
from synapse.util import stringutils
|
||||||
from synapse.util.async_helpers import delay_cancellation
|
from synapse.util.async_helpers import delay_cancellation
|
||||||
|
from synapse.util.duration import Duration
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
@@ -73,7 +74,7 @@ class AccountValidityHandler:
|
|||||||
|
|
||||||
# Check the renewal emails to send and send them every 30min.
|
# Check the renewal emails to send and send them every 30min.
|
||||||
if hs.config.worker.run_background_tasks:
|
if hs.config.worker.run_background_tasks:
|
||||||
self.clock.looping_call(self._send_renewal_emails, 30 * 60 * 1000)
|
self.clock.looping_call(self._send_renewal_emails, Duration(minutes=30))
|
||||||
|
|
||||||
async def is_user_expired(self, user_id: str) -> bool:
|
async def is_user_expired(self, user_id: str) -> bool:
|
||||||
"""Checks if a user has expired against third-party modules.
|
"""Checks if a user has expired against third-party modules.
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ from synapse.storage.databases.main.registration import (
|
|||||||
from synapse.types import JsonDict, Requester, StrCollection, UserID
|
from synapse.types import JsonDict, Requester, StrCollection, UserID
|
||||||
from synapse.util import stringutils as stringutils
|
from synapse.util import stringutils as stringutils
|
||||||
from synapse.util.async_helpers import delay_cancellation, maybe_awaitable
|
from synapse.util.async_helpers import delay_cancellation, maybe_awaitable
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.msisdn import phone_number_to_msisdn
|
from synapse.util.msisdn import phone_number_to_msisdn
|
||||||
from synapse.util.stringutils import base62_encode
|
from synapse.util.stringutils import base62_encode
|
||||||
from synapse.util.threepids import canonicalise_email
|
from synapse.util.threepids import canonicalise_email
|
||||||
@@ -242,7 +243,7 @@ class AuthHandler:
|
|||||||
if hs.config.worker.run_background_tasks:
|
if hs.config.worker.run_background_tasks:
|
||||||
self._clock.looping_call(
|
self._clock.looping_call(
|
||||||
run_as_background_process,
|
run_as_background_process,
|
||||||
5 * 60 * 1000,
|
Duration(minutes=5),
|
||||||
"expire_old_sessions",
|
"expire_old_sessions",
|
||||||
self.server_name,
|
self.server_name,
|
||||||
self._expire_old_sessions,
|
self._expire_old_sessions,
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ from synapse.types import (
|
|||||||
UserID,
|
UserID,
|
||||||
create_requester,
|
create_requester,
|
||||||
)
|
)
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.events import generate_fake_event_id
|
from synapse.util.events import generate_fake_event_id
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
from synapse.util.sentinel import Sentinel
|
from synapse.util.sentinel import Sentinel
|
||||||
@@ -92,20 +93,22 @@ class DelayedEventsHandler:
|
|||||||
# Kick off again (without blocking) to catch any missed notifications
|
# Kick off again (without blocking) to catch any missed notifications
|
||||||
# that may have fired before the callback was added.
|
# that may have fired before the callback was added.
|
||||||
self._clock.call_later(
|
self._clock.call_later(
|
||||||
0,
|
Duration(seconds=0),
|
||||||
self.notify_new_event,
|
self.notify_new_event,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Delayed events that are already marked as processed on startup might not have been
|
# Now process any delayed events that are due to be sent.
|
||||||
# sent properly on the last run of the server, so unmark them to send them again.
|
#
|
||||||
|
# We set `reprocess_events` to True in case any events had been
|
||||||
|
# marked as processed, but had not yet actually been sent,
|
||||||
|
# before the homeserver stopped.
|
||||||
|
#
|
||||||
# Caveat: this will double-send delayed events that successfully persisted, but failed
|
# Caveat: this will double-send delayed events that successfully persisted, but failed
|
||||||
# to be removed from the DB table of delayed events.
|
# to be removed from the DB table of delayed events.
|
||||||
# TODO: To avoid double-sending, scan the timeline to find which of these events were
|
# TODO: To avoid double-sending, scan the timeline to find which of these events were
|
||||||
# already sent. To do so, must store delay_ids in sent events to retrieve them later.
|
# already sent. To do so, must store delay_ids in sent events to retrieve them later.
|
||||||
await self._store.unprocess_delayed_events()
|
|
||||||
|
|
||||||
events, next_send_ts = await self._store.process_timeout_delayed_events(
|
events, next_send_ts = await self._store.process_timeout_delayed_events(
|
||||||
self._get_current_ts()
|
self._get_current_ts(), reprocess_events=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if next_send_ts:
|
if next_send_ts:
|
||||||
@@ -423,18 +426,23 @@ class DelayedEventsHandler:
|
|||||||
Raises:
|
Raises:
|
||||||
NotFoundError: if no matching delayed event could be found.
|
NotFoundError: if no matching delayed event could be found.
|
||||||
"""
|
"""
|
||||||
assert self._is_master
|
|
||||||
await self._delayed_event_mgmt_ratelimiter.ratelimit(
|
await self._delayed_event_mgmt_ratelimiter.ratelimit(
|
||||||
None, request.getClientAddress().host
|
None, request.getClientAddress().host
|
||||||
)
|
)
|
||||||
await make_deferred_yieldable(self._initialized_from_db)
|
|
||||||
|
# Note: We don't need to wait on `self._initialized_from_db` here as the
|
||||||
|
# events that deals with are already marked as processed.
|
||||||
|
#
|
||||||
|
# `restart_delayed_events` will skip over such events entirely.
|
||||||
|
|
||||||
next_send_ts = await self._store.restart_delayed_event(
|
next_send_ts = await self._store.restart_delayed_event(
|
||||||
delay_id, self._get_current_ts()
|
delay_id, self._get_current_ts()
|
||||||
)
|
)
|
||||||
|
|
||||||
if self._next_send_ts_changed(next_send_ts):
|
# Only the main process handles sending delayed events.
|
||||||
self._schedule_next_at(next_send_ts)
|
if self._is_master:
|
||||||
|
if self._next_send_ts_changed(next_send_ts):
|
||||||
|
self._schedule_next_at(next_send_ts)
|
||||||
|
|
||||||
async def send(self, request: SynapseRequest, delay_id: str) -> None:
|
async def send(self, request: SynapseRequest, delay_id: str) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -501,17 +509,17 @@ class DelayedEventsHandler:
|
|||||||
|
|
||||||
def _schedule_next_at(self, next_send_ts: Timestamp) -> None:
|
def _schedule_next_at(self, next_send_ts: Timestamp) -> None:
|
||||||
delay = next_send_ts - self._get_current_ts()
|
delay = next_send_ts - self._get_current_ts()
|
||||||
delay_sec = delay / 1000 if delay > 0 else 0
|
delay_duration = Duration(milliseconds=max(delay, 0))
|
||||||
|
|
||||||
if self._next_delayed_event_call is None:
|
if self._next_delayed_event_call is None:
|
||||||
self._next_delayed_event_call = self._clock.call_later(
|
self._next_delayed_event_call = self._clock.call_later(
|
||||||
delay_sec,
|
delay_duration,
|
||||||
self.hs.run_as_background_process,
|
self.hs.run_as_background_process,
|
||||||
"_send_on_timeout",
|
"_send_on_timeout",
|
||||||
self._send_on_timeout,
|
self._send_on_timeout,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._next_delayed_event_call.reset(delay_sec)
|
self._next_delayed_event_call.reset(delay_duration.as_secs())
|
||||||
|
|
||||||
async def get_all_for_user(self, requester: Requester) -> list[JsonDict]:
|
async def get_all_for_user(self, requester: Requester) -> list[JsonDict]:
|
||||||
"""Return all pending delayed events requested by the given user."""
|
"""Return all pending delayed events requested by the given user."""
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ from synapse.util import stringutils
|
|||||||
from synapse.util.async_helpers import Linearizer
|
from synapse.util.async_helpers import Linearizer
|
||||||
from synapse.util.caches.expiringcache import ExpiringCache
|
from synapse.util.caches.expiringcache import ExpiringCache
|
||||||
from synapse.util.cancellation import cancellable
|
from synapse.util.cancellation import cancellable
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.metrics import measure_func
|
from synapse.util.metrics import measure_func
|
||||||
from synapse.util.retryutils import (
|
from synapse.util.retryutils import (
|
||||||
NotRetryingDestination,
|
NotRetryingDestination,
|
||||||
@@ -85,7 +86,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
DELETE_DEVICE_MSGS_TASK_NAME = "delete_device_messages"
|
DELETE_DEVICE_MSGS_TASK_NAME = "delete_device_messages"
|
||||||
MAX_DEVICE_DISPLAY_NAME_LEN = 100
|
MAX_DEVICE_DISPLAY_NAME_LEN = 100
|
||||||
DELETE_STALE_DEVICES_INTERVAL_MS = 24 * 60 * 60 * 1000
|
DELETE_STALE_DEVICES_INTERVAL = Duration(days=1)
|
||||||
|
|
||||||
|
|
||||||
def _check_device_name_length(name: str | None) -> None:
|
def _check_device_name_length(name: str | None) -> None:
|
||||||
@@ -186,7 +187,7 @@ class DeviceHandler:
|
|||||||
):
|
):
|
||||||
self.clock.looping_call(
|
self.clock.looping_call(
|
||||||
self.hs.run_as_background_process,
|
self.hs.run_as_background_process,
|
||||||
DELETE_STALE_DEVICES_INTERVAL_MS,
|
DELETE_STALE_DEVICES_INTERVAL,
|
||||||
desc="delete_stale_devices",
|
desc="delete_stale_devices",
|
||||||
func=self._delete_stale_devices,
|
func=self._delete_stale_devices,
|
||||||
)
|
)
|
||||||
@@ -915,7 +916,7 @@ class DeviceHandler:
|
|||||||
)
|
)
|
||||||
|
|
||||||
DEVICE_MSGS_DELETE_BATCH_LIMIT = 1000
|
DEVICE_MSGS_DELETE_BATCH_LIMIT = 1000
|
||||||
DEVICE_MSGS_DELETE_SLEEP_MS = 100
|
DEVICE_MSGS_DELETE_SLEEP = Duration(milliseconds=100)
|
||||||
|
|
||||||
async def _delete_device_messages(
|
async def _delete_device_messages(
|
||||||
self,
|
self,
|
||||||
@@ -941,9 +942,7 @@ class DeviceHandler:
|
|||||||
if from_stream_id is None:
|
if from_stream_id is None:
|
||||||
return TaskStatus.COMPLETE, None, None
|
return TaskStatus.COMPLETE, None, None
|
||||||
|
|
||||||
await self.clock.sleep(
|
await self.clock.sleep(DeviceWriterHandler.DEVICE_MSGS_DELETE_SLEEP)
|
||||||
DeviceWriterHandler.DEVICE_MSGS_DELETE_SLEEP_MS / 1000.0
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceWriterHandler(DeviceHandler):
|
class DeviceWriterHandler(DeviceHandler):
|
||||||
@@ -1469,7 +1468,7 @@ class DeviceListUpdater(DeviceListWorkerUpdater):
|
|||||||
self._resync_retry_lock = Lock()
|
self._resync_retry_lock = Lock()
|
||||||
self.clock.looping_call(
|
self.clock.looping_call(
|
||||||
self.hs.run_as_background_process,
|
self.hs.run_as_background_process,
|
||||||
30 * 1000,
|
Duration(seconds=30),
|
||||||
func=self._maybe_retry_device_resync,
|
func=self._maybe_retry_device_resync,
|
||||||
desc="_maybe_retry_device_resync",
|
desc="_maybe_retry_device_resync",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ from synapse.types import (
|
|||||||
)
|
)
|
||||||
from synapse.util.async_helpers import Linearizer, concurrently_execute
|
from synapse.util.async_helpers import Linearizer, concurrently_execute
|
||||||
from synapse.util.cancellation import cancellable
|
from synapse.util.cancellation import cancellable
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.json import json_decoder
|
from synapse.util.json import json_decoder
|
||||||
from synapse.util.retryutils import (
|
from synapse.util.retryutils import (
|
||||||
NotRetryingDestination,
|
NotRetryingDestination,
|
||||||
@@ -1634,7 +1635,7 @@ class E2eKeysHandler:
|
|||||||
# matrix.org has about 15M users in the e2e_one_time_keys_json table
|
# matrix.org has about 15M users in the e2e_one_time_keys_json table
|
||||||
# (comprising 20M devices). We want this to take about a week, so we need
|
# (comprising 20M devices). We want this to take about a week, so we need
|
||||||
# to do about one batch of 100 users every 4 seconds.
|
# to do about one batch of 100 users every 4 seconds.
|
||||||
await self.clock.sleep(4)
|
await self.clock.sleep(Duration(seconds=4))
|
||||||
|
|
||||||
|
|
||||||
def _check_cross_signing_key(
|
def _check_cross_signing_key(
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ from synapse.storage.invite_rule import InviteRule
|
|||||||
from synapse.types import JsonDict, StrCollection, get_domain_from_id
|
from synapse.types import JsonDict, StrCollection, get_domain_from_id
|
||||||
from synapse.types.state import StateFilter
|
from synapse.types.state import StateFilter
|
||||||
from synapse.util.async_helpers import Linearizer
|
from synapse.util.async_helpers import Linearizer
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.retryutils import NotRetryingDestination
|
from synapse.util.retryutils import NotRetryingDestination
|
||||||
from synapse.visibility import filter_events_for_server
|
from synapse.visibility import filter_events_for_server
|
||||||
|
|
||||||
@@ -1972,7 +1973,9 @@ class FederationHandler:
|
|||||||
logger.warning(
|
logger.warning(
|
||||||
"%s; waiting for %d ms...", e, e.retry_after_ms
|
"%s; waiting for %d ms...", e, e.retry_after_ms
|
||||||
)
|
)
|
||||||
await self.clock.sleep(e.retry_after_ms / 1000)
|
await self.clock.sleep(
|
||||||
|
Duration(milliseconds=e.retry_after_ms)
|
||||||
|
)
|
||||||
|
|
||||||
# Success, no need to try the rest of the destinations.
|
# Success, no need to try the rest of the destinations.
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ from synapse.types import (
|
|||||||
)
|
)
|
||||||
from synapse.types.state import StateFilter
|
from synapse.types.state import StateFilter
|
||||||
from synapse.util.async_helpers import Linearizer, concurrently_execute
|
from synapse.util.async_helpers import Linearizer, concurrently_execute
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.iterutils import batch_iter, partition, sorted_topologically
|
from synapse.util.iterutils import batch_iter, partition, sorted_topologically
|
||||||
from synapse.util.retryutils import NotRetryingDestination
|
from synapse.util.retryutils import NotRetryingDestination
|
||||||
from synapse.util.stringutils import shortstr
|
from synapse.util.stringutils import shortstr
|
||||||
@@ -1802,7 +1803,7 @@ class FederationEventHandler:
|
|||||||
# the reactor. For large rooms let's yield to the reactor
|
# the reactor. For large rooms let's yield to the reactor
|
||||||
# occasionally to ensure we don't block other work.
|
# occasionally to ensure we don't block other work.
|
||||||
if (i + 1) % 1000 == 0:
|
if (i + 1) % 1000 == 0:
|
||||||
await self._clock.sleep(0)
|
await self._clock.sleep(Duration(seconds=0))
|
||||||
|
|
||||||
# Also persist the new event in batches for similar reasons as above.
|
# Also persist the new event in batches for similar reasons as above.
|
||||||
for batch in batch_iter(events_and_contexts_to_persist, 1000):
|
for batch in batch_iter(events_and_contexts_to_persist, 1000):
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ from synapse.types.state import StateFilter
|
|||||||
from synapse.util import log_failure, unwrapFirstError
|
from synapse.util import log_failure, unwrapFirstError
|
||||||
from synapse.util.async_helpers import Linearizer, gather_results
|
from synapse.util.async_helpers import Linearizer, gather_results
|
||||||
from synapse.util.caches.expiringcache import ExpiringCache
|
from synapse.util.caches.expiringcache import ExpiringCache
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.json import json_decoder, json_encoder
|
from synapse.util.json import json_decoder, json_encoder
|
||||||
from synapse.util.metrics import measure_func
|
from synapse.util.metrics import measure_func
|
||||||
from synapse.visibility import get_effective_room_visibility_from_state
|
from synapse.visibility import get_effective_room_visibility_from_state
|
||||||
@@ -433,14 +434,11 @@ class MessageHandler:
|
|||||||
|
|
||||||
# Figure out how many seconds we need to wait before expiring the event.
|
# Figure out how many seconds we need to wait before expiring the event.
|
||||||
now_ms = self.clock.time_msec()
|
now_ms = self.clock.time_msec()
|
||||||
delay = (expiry_ts - now_ms) / 1000
|
delay = Duration(milliseconds=max(expiry_ts - now_ms, 0))
|
||||||
|
|
||||||
# callLater doesn't support negative delays, so trim the delay to 0 if we're
|
logger.info(
|
||||||
# in that case.
|
"Scheduling expiry for event %s in %.3fs", event_id, delay.as_secs()
|
||||||
if delay < 0:
|
)
|
||||||
delay = 0
|
|
||||||
|
|
||||||
logger.info("Scheduling expiry for event %s in %.3fs", event_id, delay)
|
|
||||||
|
|
||||||
self._scheduled_expiry = self.clock.call_later(
|
self._scheduled_expiry = self.clock.call_later(
|
||||||
delay,
|
delay,
|
||||||
@@ -551,7 +549,7 @@ class EventCreationHandler:
|
|||||||
"send_dummy_events_to_fill_extremities",
|
"send_dummy_events_to_fill_extremities",
|
||||||
self._send_dummy_events_to_fill_extremities,
|
self._send_dummy_events_to_fill_extremities,
|
||||||
),
|
),
|
||||||
5 * 60 * 1000,
|
Duration(minutes=5),
|
||||||
)
|
)
|
||||||
|
|
||||||
self._message_handler = hs.get_message_handler()
|
self._message_handler = hs.get_message_handler()
|
||||||
@@ -1012,7 +1010,7 @@ class EventCreationHandler:
|
|||||||
|
|
||||||
if not ignore_shadow_ban and requester.shadow_banned:
|
if not ignore_shadow_ban and requester.shadow_banned:
|
||||||
# We randomly sleep a bit just to annoy the requester.
|
# We randomly sleep a bit just to annoy the requester.
|
||||||
await self.clock.sleep(random.randint(1, 10))
|
await self.clock.sleep(Duration(seconds=random.randint(1, 10)))
|
||||||
raise ShadowBanError()
|
raise ShadowBanError()
|
||||||
|
|
||||||
room_version = None
|
room_version = None
|
||||||
@@ -1515,7 +1513,7 @@ class EventCreationHandler:
|
|||||||
and requester.shadow_banned
|
and requester.shadow_banned
|
||||||
):
|
):
|
||||||
# We randomly sleep a bit just to annoy the requester.
|
# We randomly sleep a bit just to annoy the requester.
|
||||||
await self.clock.sleep(random.randint(1, 10))
|
await self.clock.sleep(Duration(seconds=random.randint(1, 10)))
|
||||||
raise ShadowBanError()
|
raise ShadowBanError()
|
||||||
|
|
||||||
if event.is_state():
|
if event.is_state():
|
||||||
@@ -1957,6 +1955,12 @@ class EventCreationHandler:
|
|||||||
room_alias_str = event.content.get("alias", None)
|
room_alias_str = event.content.get("alias", None)
|
||||||
directory_handler = self.hs.get_directory_handler()
|
directory_handler = self.hs.get_directory_handler()
|
||||||
if room_alias_str and room_alias_str != original_alias:
|
if room_alias_str and room_alias_str != original_alias:
|
||||||
|
if not isinstance(room_alias_str, str):
|
||||||
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"The alias must be of type string.",
|
||||||
|
Codes.INVALID_PARAM,
|
||||||
|
)
|
||||||
await self._validate_canonical_alias(
|
await self._validate_canonical_alias(
|
||||||
directory_handler, room_alias_str, event.room_id
|
directory_handler, room_alias_str, event.room_id
|
||||||
)
|
)
|
||||||
@@ -1980,6 +1984,12 @@ class EventCreationHandler:
|
|||||||
new_alt_aliases = set(alt_aliases) - set(original_alt_aliases)
|
new_alt_aliases = set(alt_aliases) - set(original_alt_aliases)
|
||||||
if new_alt_aliases:
|
if new_alt_aliases:
|
||||||
for alias_str in new_alt_aliases:
|
for alias_str in new_alt_aliases:
|
||||||
|
if not isinstance(alias_str, str):
|
||||||
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"Each alt_alias must be of type string.",
|
||||||
|
Codes.INVALID_PARAM,
|
||||||
|
)
|
||||||
await self._validate_canonical_alias(
|
await self._validate_canonical_alias(
|
||||||
directory_handler, alias_str, event.room_id
|
directory_handler, alias_str, event.room_id
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,27 +21,31 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, cast
|
from typing import TYPE_CHECKING, cast
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
from twisted.python.failure import Failure
|
from twisted.python.failure import Failure
|
||||||
|
|
||||||
from synapse.api.constants import Direction, EventTypes, Membership
|
from synapse.api.constants import Direction, EventTypes, Membership
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import SynapseError
|
||||||
from synapse.api.filtering import Filter
|
from synapse.api.filtering import Filter
|
||||||
from synapse.events.utils import SerializeEventConfig
|
from synapse.events import EventBase
|
||||||
|
from synapse.handlers.relations import BundledAggregations
|
||||||
from synapse.handlers.worker_lock import NEW_EVENT_DURING_PURGE_LOCK_NAME
|
from synapse.handlers.worker_lock import NEW_EVENT_DURING_PURGE_LOCK_NAME
|
||||||
from synapse.logging.opentracing import trace
|
from synapse.logging.opentracing import trace
|
||||||
from synapse.rest.admin._base import assert_user_is_admin
|
from synapse.rest.admin._base import assert_user_is_admin
|
||||||
from synapse.streams.config import PaginationConfig
|
from synapse.streams.config import PaginationConfig
|
||||||
from synapse.types import (
|
from synapse.types import (
|
||||||
JsonDict,
|
|
||||||
JsonMapping,
|
JsonMapping,
|
||||||
Requester,
|
Requester,
|
||||||
ScheduledTask,
|
ScheduledTask,
|
||||||
StreamKeyType,
|
StreamKeyType,
|
||||||
|
StreamToken,
|
||||||
TaskStatus,
|
TaskStatus,
|
||||||
)
|
)
|
||||||
from synapse.types.handlers import ShutdownRoomParams, ShutdownRoomResponse
|
from synapse.types.handlers import ShutdownRoomParams, ShutdownRoomResponse
|
||||||
from synapse.types.state import StateFilter
|
from synapse.types.state import StateFilter
|
||||||
from synapse.util.async_helpers import ReadWriteLock
|
from synapse.util.async_helpers import ReadWriteLock
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.visibility import filter_events_for_client
|
from synapse.visibility import filter_events_for_client
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -69,6 +73,58 @@ PURGE_ROOM_ACTION_NAME = "purge_room"
|
|||||||
SHUTDOWN_AND_PURGE_ROOM_ACTION_NAME = "shutdown_and_purge_room"
|
SHUTDOWN_AND_PURGE_ROOM_ACTION_NAME = "shutdown_and_purge_room"
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True, auto_attribs=True)
|
||||||
|
class GetMessagesResult:
|
||||||
|
"""
|
||||||
|
Everything needed to serialize a `/messages` response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
messages_chunk: list[EventBase]
|
||||||
|
"""
|
||||||
|
A list of room events.
|
||||||
|
|
||||||
|
- When the request is `Direction.FORWARDS`, events will be in the range:
|
||||||
|
`start_token` < x <= `end_token`, (ascending topological_order)
|
||||||
|
- When the request is `Direction.BACKWARDS`, events will be in the range:
|
||||||
|
`start_token` >= x > `end_token`, (descending topological_order)
|
||||||
|
|
||||||
|
Note that an empty chunk does not necessarily imply that no more events are
|
||||||
|
available. Clients should continue to paginate until no `end_token` property is returned.
|
||||||
|
"""
|
||||||
|
|
||||||
|
bundled_aggregations: dict[str, BundledAggregations]
|
||||||
|
"""
|
||||||
|
A map of event ID to the bundled aggregations for the events in the chunk.
|
||||||
|
|
||||||
|
If an event doesn't have any bundled aggregations, it may not appear in the map.
|
||||||
|
"""
|
||||||
|
|
||||||
|
state: list[EventBase] | None
|
||||||
|
"""
|
||||||
|
A list of state events relevant to showing the chunk. For example, if
|
||||||
|
lazy_load_members is enabled in the filter then this may contain the membership
|
||||||
|
events for the senders of events in the chunk.
|
||||||
|
|
||||||
|
Omitted from the response when `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
start_token: StreamToken
|
||||||
|
"""
|
||||||
|
Token corresponding to the start of chunk. This will be the same as the value given
|
||||||
|
in `from` query parameter of the `/messages` request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
end_token: StreamToken | None
|
||||||
|
"""
|
||||||
|
A token corresponding to the end of chunk. This token can be passed back to this
|
||||||
|
endpoint to request further events.
|
||||||
|
|
||||||
|
If no further events are available (either because we have reached the start of the
|
||||||
|
timeline, or because the user does not have permission to see any more events), this
|
||||||
|
property is omitted from the response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class PaginationHandler:
|
class PaginationHandler:
|
||||||
"""Handles pagination and purge history requests.
|
"""Handles pagination and purge history requests.
|
||||||
|
|
||||||
@@ -116,7 +172,7 @@ class PaginationHandler:
|
|||||||
|
|
||||||
self.clock.looping_call(
|
self.clock.looping_call(
|
||||||
self.hs.run_as_background_process,
|
self.hs.run_as_background_process,
|
||||||
job.interval,
|
Duration(milliseconds=job.interval),
|
||||||
"purge_history_for_rooms_in_range",
|
"purge_history_for_rooms_in_range",
|
||||||
self.purge_history_for_rooms_in_range,
|
self.purge_history_for_rooms_in_range,
|
||||||
job.shortest_max_lifetime,
|
job.shortest_max_lifetime,
|
||||||
@@ -417,7 +473,7 @@ class PaginationHandler:
|
|||||||
as_client_event: bool = True,
|
as_client_event: bool = True,
|
||||||
event_filter: Filter | None = None,
|
event_filter: Filter | None = None,
|
||||||
use_admin_priviledge: bool = False,
|
use_admin_priviledge: bool = False,
|
||||||
) -> JsonDict:
|
) -> GetMessagesResult:
|
||||||
"""Get messages in a room.
|
"""Get messages in a room.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -616,10 +672,13 @@ class PaginationHandler:
|
|||||||
# In that case we do not return end, to tell the client
|
# In that case we do not return end, to tell the client
|
||||||
# there is no need for further queries.
|
# there is no need for further queries.
|
||||||
if not events:
|
if not events:
|
||||||
return {
|
return GetMessagesResult(
|
||||||
"chunk": [],
|
messages_chunk=[],
|
||||||
"start": await from_token.to_string(self.store),
|
bundled_aggregations={},
|
||||||
}
|
state=None,
|
||||||
|
start_token=from_token,
|
||||||
|
end_token=None,
|
||||||
|
)
|
||||||
|
|
||||||
if event_filter:
|
if event_filter:
|
||||||
events = await event_filter.filter(events)
|
events = await event_filter.filter(events)
|
||||||
@@ -635,11 +694,13 @@ class PaginationHandler:
|
|||||||
# if after the filter applied there are no more events
|
# if after the filter applied there are no more events
|
||||||
# return immediately - but there might be more in next_token batch
|
# return immediately - but there might be more in next_token batch
|
||||||
if not events:
|
if not events:
|
||||||
return {
|
return GetMessagesResult(
|
||||||
"chunk": [],
|
messages_chunk=[],
|
||||||
"start": await from_token.to_string(self.store),
|
bundled_aggregations={},
|
||||||
"end": await next_token.to_string(self.store),
|
state=None,
|
||||||
}
|
start_token=from_token,
|
||||||
|
end_token=next_token,
|
||||||
|
)
|
||||||
|
|
||||||
state = None
|
state = None
|
||||||
if event_filter and event_filter.lazy_load_members and len(events) > 0:
|
if event_filter and event_filter.lazy_load_members and len(events) > 0:
|
||||||
@@ -656,38 +717,20 @@ class PaginationHandler:
|
|||||||
|
|
||||||
if state_ids:
|
if state_ids:
|
||||||
state_dict = await self.store.get_events(list(state_ids.values()))
|
state_dict = await self.store.get_events(list(state_ids.values()))
|
||||||
state = state_dict.values()
|
state = list(state_dict.values())
|
||||||
|
|
||||||
aggregations = await self._relations_handler.get_bundled_aggregations(
|
aggregations = await self._relations_handler.get_bundled_aggregations(
|
||||||
events, user_id
|
events, user_id
|
||||||
)
|
)
|
||||||
|
|
||||||
time_now = self.clock.time_msec()
|
return GetMessagesResult(
|
||||||
|
messages_chunk=events,
|
||||||
serialize_options = SerializeEventConfig(
|
bundled_aggregations=aggregations,
|
||||||
as_client_event=as_client_event, requester=requester
|
state=state,
|
||||||
|
start_token=from_token,
|
||||||
|
end_token=next_token,
|
||||||
)
|
)
|
||||||
|
|
||||||
chunk = {
|
|
||||||
"chunk": (
|
|
||||||
await self._event_serializer.serialize_events(
|
|
||||||
events,
|
|
||||||
time_now,
|
|
||||||
config=serialize_options,
|
|
||||||
bundle_aggregations=aggregations,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
"start": await from_token.to_string(self.store),
|
|
||||||
"end": await next_token.to_string(self.store),
|
|
||||||
}
|
|
||||||
|
|
||||||
if state:
|
|
||||||
chunk["state"] = await self._event_serializer.serialize_events(
|
|
||||||
state, time_now, config=serialize_options
|
|
||||||
)
|
|
||||||
|
|
||||||
return chunk
|
|
||||||
|
|
||||||
async def _shutdown_and_purge_room(
|
async def _shutdown_and_purge_room(
|
||||||
self,
|
self,
|
||||||
task: ScheduledTask,
|
task: ScheduledTask,
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ from synapse.types import (
|
|||||||
get_domain_from_id,
|
get_domain_from_id,
|
||||||
)
|
)
|
||||||
from synapse.util.async_helpers import Linearizer
|
from synapse.util.async_helpers import Linearizer
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
from synapse.util.wheel_timer import WheelTimer
|
from synapse.util.wheel_timer import WheelTimer
|
||||||
|
|
||||||
@@ -203,7 +204,7 @@ EXTERNAL_PROCESS_EXPIRY = 5 * 60 * 1000
|
|||||||
|
|
||||||
# Delay before a worker tells the presence handler that a user has stopped
|
# Delay before a worker tells the presence handler that a user has stopped
|
||||||
# syncing.
|
# syncing.
|
||||||
UPDATE_SYNCING_USERS_MS = 10 * 1000
|
UPDATE_SYNCING_USERS = Duration(seconds=10)
|
||||||
|
|
||||||
assert LAST_ACTIVE_GRANULARITY < IDLE_TIMER
|
assert LAST_ACTIVE_GRANULARITY < IDLE_TIMER
|
||||||
|
|
||||||
@@ -528,7 +529,7 @@ class WorkerPresenceHandler(BasePresenceHandler):
|
|||||||
self._bump_active_client = ReplicationBumpPresenceActiveTime.make_client(hs)
|
self._bump_active_client = ReplicationBumpPresenceActiveTime.make_client(hs)
|
||||||
self._set_state_client = ReplicationPresenceSetState.make_client(hs)
|
self._set_state_client = ReplicationPresenceSetState.make_client(hs)
|
||||||
|
|
||||||
self.clock.looping_call(self.send_stop_syncing, UPDATE_SYNCING_USERS_MS)
|
self.clock.looping_call(self.send_stop_syncing, UPDATE_SYNCING_USERS)
|
||||||
|
|
||||||
hs.register_async_shutdown_handler(
|
hs.register_async_shutdown_handler(
|
||||||
phase="before",
|
phase="before",
|
||||||
@@ -581,7 +582,7 @@ class WorkerPresenceHandler(BasePresenceHandler):
|
|||||||
for (user_id, device_id), last_sync_ms in list(
|
for (user_id, device_id), last_sync_ms in list(
|
||||||
self._user_devices_going_offline.items()
|
self._user_devices_going_offline.items()
|
||||||
):
|
):
|
||||||
if now - last_sync_ms > UPDATE_SYNCING_USERS_MS:
|
if now - last_sync_ms > UPDATE_SYNCING_USERS.as_millis():
|
||||||
self._user_devices_going_offline.pop((user_id, device_id), None)
|
self._user_devices_going_offline.pop((user_id, device_id), None)
|
||||||
self.send_user_sync(user_id, device_id, False, last_sync_ms)
|
self.send_user_sync(user_id, device_id, False, last_sync_ms)
|
||||||
|
|
||||||
@@ -861,20 +862,20 @@ class PresenceHandler(BasePresenceHandler):
|
|||||||
# The initial delay is to allow disconnected clients a chance to
|
# The initial delay is to allow disconnected clients a chance to
|
||||||
# reconnect before we treat them as offline.
|
# reconnect before we treat them as offline.
|
||||||
self.clock.call_later(
|
self.clock.call_later(
|
||||||
30,
|
Duration(seconds=30),
|
||||||
self.clock.looping_call,
|
self.clock.looping_call,
|
||||||
self._handle_timeouts,
|
self._handle_timeouts,
|
||||||
5000,
|
Duration(seconds=5),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Presence information is persisted, whether or not it is being tracked
|
# Presence information is persisted, whether or not it is being tracked
|
||||||
# internally.
|
# internally.
|
||||||
if self._presence_enabled:
|
if self._presence_enabled:
|
||||||
self.clock.call_later(
|
self.clock.call_later(
|
||||||
60,
|
Duration(minutes=1),
|
||||||
self.clock.looping_call,
|
self.clock.looping_call,
|
||||||
self._persist_unpersisted_changes,
|
self._persist_unpersisted_changes,
|
||||||
60 * 1000,
|
Duration(minutes=1),
|
||||||
)
|
)
|
||||||
|
|
||||||
presence_wheel_timer_size_gauge.register_hook(
|
presence_wheel_timer_size_gauge.register_hook(
|
||||||
@@ -2430,7 +2431,7 @@ class PresenceFederationQueue:
|
|||||||
_KEEP_ITEMS_IN_QUEUE_FOR_MS = 5 * 60 * 1000
|
_KEEP_ITEMS_IN_QUEUE_FOR_MS = 5 * 60 * 1000
|
||||||
|
|
||||||
# How often to check if we can expire entries from the queue.
|
# How often to check if we can expire entries from the queue.
|
||||||
_CLEAR_ITEMS_EVERY_MS = 60 * 1000
|
_CLEAR_ITEMS_EVERY_MS = Duration(minutes=1)
|
||||||
|
|
||||||
def __init__(self, hs: "HomeServer", presence_handler: BasePresenceHandler):
|
def __init__(self, hs: "HomeServer", presence_handler: BasePresenceHandler):
|
||||||
self._clock = hs.get_clock()
|
self._clock = hs.get_clock()
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ from synapse.api.errors import (
|
|||||||
from synapse.storage.databases.main.media_repository import LocalMedia, RemoteMedia
|
from synapse.storage.databases.main.media_repository import LocalMedia, RemoteMedia
|
||||||
from synapse.types import JsonDict, JsonValue, Requester, UserID, create_requester
|
from synapse.types import JsonDict, JsonValue, Requester, UserID, create_requester
|
||||||
from synapse.util.caches.descriptors import cached
|
from synapse.util.caches.descriptors import cached
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.stringutils import parse_and_validate_mxc_uri
|
from synapse.util.stringutils import parse_and_validate_mxc_uri
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -583,7 +584,7 @@ class ProfileHandler:
|
|||||||
# Do not actually update the room state for shadow-banned users.
|
# Do not actually update the room state for shadow-banned users.
|
||||||
if requester.shadow_banned:
|
if requester.shadow_banned:
|
||||||
# We randomly sleep a bit just to annoy the requester.
|
# We randomly sleep a bit just to annoy the requester.
|
||||||
await self.clock.sleep(random.randint(1, 10))
|
await self.clock.sleep(Duration(seconds=random.randint(1, 10)))
|
||||||
return
|
return
|
||||||
|
|
||||||
room_ids = await self.store.get_rooms_for_user(target_user.to_string())
|
room_ids = await self.store.get_rooms_for_user(target_user.to_string())
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ from synapse.types.state import StateFilter
|
|||||||
from synapse.util import stringutils
|
from synapse.util import stringutils
|
||||||
from synapse.util.async_helpers import concurrently_execute
|
from synapse.util.async_helpers import concurrently_execute
|
||||||
from synapse.util.caches.response_cache import ResponseCache
|
from synapse.util.caches.response_cache import ResponseCache
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.iterutils import batch_iter
|
from synapse.util.iterutils import batch_iter
|
||||||
from synapse.util.stringutils import parse_and_validate_server_name
|
from synapse.util.stringutils import parse_and_validate_server_name
|
||||||
from synapse.visibility import filter_events_for_client
|
from synapse.visibility import filter_events_for_client
|
||||||
@@ -1179,7 +1180,7 @@ class RoomCreationHandler:
|
|||||||
|
|
||||||
if (invite_list or invite_3pid_list) and requester.shadow_banned:
|
if (invite_list or invite_3pid_list) and requester.shadow_banned:
|
||||||
# We randomly sleep a bit just to annoy the requester.
|
# We randomly sleep a bit just to annoy the requester.
|
||||||
await self.clock.sleep(random.randint(1, 10))
|
await self.clock.sleep(Duration(seconds=random.randint(1, 10)))
|
||||||
|
|
||||||
# Allow the request to go through, but remove any associated invites.
|
# Allow the request to go through, but remove any associated invites.
|
||||||
invite_3pid_list = []
|
invite_3pid_list = []
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ from synapse.types import (
|
|||||||
from synapse.types.state import StateFilter
|
from synapse.types.state import StateFilter
|
||||||
from synapse.util.async_helpers import Linearizer
|
from synapse.util.async_helpers import Linearizer
|
||||||
from synapse.util.distributor import user_left_room
|
from synapse.util.distributor import user_left_room
|
||||||
|
from synapse.util.duration import Duration
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
@@ -642,7 +643,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
if action == Membership.INVITE and requester.shadow_banned:
|
if action == Membership.INVITE and requester.shadow_banned:
|
||||||
# We randomly sleep a bit just to annoy the requester.
|
# We randomly sleep a bit just to annoy the requester.
|
||||||
await self.clock.sleep(random.randint(1, 10))
|
await self.clock.sleep(Duration(seconds=random.randint(1, 10)))
|
||||||
raise ShadowBanError()
|
raise ShadowBanError()
|
||||||
|
|
||||||
key = (room_id,)
|
key = (room_id,)
|
||||||
@@ -873,7 +874,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
|||||||
if target_id == self._server_notices_mxid:
|
if target_id == self._server_notices_mxid:
|
||||||
raise SynapseError(HTTPStatus.FORBIDDEN, "Cannot invite this user")
|
raise SynapseError(HTTPStatus.FORBIDDEN, "Cannot invite this user")
|
||||||
|
|
||||||
block_invite_result = None
|
block_invite_result: tuple[Codes, dict] | None = None
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self._server_notices_mxid is not None
|
self._server_notices_mxid is not None
|
||||||
@@ -1647,7 +1648,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
if requester.shadow_banned:
|
if requester.shadow_banned:
|
||||||
# We randomly sleep a bit just to annoy the requester.
|
# We randomly sleep a bit just to annoy the requester.
|
||||||
await self.clock.sleep(random.randint(1, 10))
|
await self.clock.sleep(Duration(seconds=random.randint(1, 10)))
|
||||||
raise ShadowBanError()
|
raise ShadowBanError()
|
||||||
|
|
||||||
# We need to rate limit *before* we send out any 3PID invites, so we
|
# We need to rate limit *before* we send out any 3PID invites, so we
|
||||||
@@ -2190,7 +2191,7 @@ class RoomForgetterHandler(StateDeltasHandler):
|
|||||||
|
|
||||||
# We kick this off to pick up outstanding work from before the last restart.
|
# We kick this off to pick up outstanding work from before the last restart.
|
||||||
self._clock.call_later(
|
self._clock.call_later(
|
||||||
0,
|
Duration(seconds=0),
|
||||||
self.notify_new_event,
|
self.notify_new_event,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2232,7 +2233,7 @@ class RoomForgetterHandler(StateDeltasHandler):
|
|||||||
#
|
#
|
||||||
# We wait for a short time so that we don't "tight" loop just
|
# We wait for a short time so that we don't "tight" loop just
|
||||||
# keeping the table up to date.
|
# keeping the table up to date.
|
||||||
await self._clock.sleep(0.5)
|
await self._clock.sleep(Duration(milliseconds=500))
|
||||||
|
|
||||||
self.pos = self._store.get_room_max_stream_ordering()
|
self.pos = self._store.get_room_max_stream_ordering()
|
||||||
await self._store.update_room_forgetter_stream_pos(self.pos)
|
await self._store.update_room_forgetter_stream_pos(self.pos)
|
||||||
|
|||||||
@@ -761,8 +761,6 @@ class SlidingSyncHandler:
|
|||||||
!= Membership.JOIN,
|
!= Membership.JOIN,
|
||||||
filter_send_to_client=True,
|
filter_send_to_client=True,
|
||||||
)
|
)
|
||||||
# TODO: Filter out `EventTypes.CallInvite` in public rooms,
|
|
||||||
# see https://github.com/element-hq/synapse/issues/17359
|
|
||||||
|
|
||||||
# TODO: Handle timeline gaps (`get_timeline_gaps()`)
|
# TODO: Handle timeline gaps (`get_timeline_gaps()`)
|
||||||
|
|
||||||
|
|||||||
@@ -34,10 +34,12 @@ from synapse.api.constants import (
|
|||||||
EventTypes,
|
EventTypes,
|
||||||
Membership,
|
Membership,
|
||||||
)
|
)
|
||||||
|
from synapse.api.errors import SlidingSyncUnknownPosition
|
||||||
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
|
||||||
from synapse.events import StrippedStateEvent
|
from synapse.events import StrippedStateEvent
|
||||||
from synapse.events.utils import parse_stripped_state_event
|
from synapse.events.utils import parse_stripped_state_event
|
||||||
from synapse.logging.opentracing import start_active_span, trace
|
from synapse.logging.opentracing import start_active_span, trace
|
||||||
|
from synapse.storage.databases.main.sliding_sync import UPDATE_INTERVAL_LAST_USED_TS
|
||||||
from synapse.storage.databases.main.state import (
|
from synapse.storage.databases.main.state import (
|
||||||
ROOM_UNKNOWN_SENTINEL,
|
ROOM_UNKNOWN_SENTINEL,
|
||||||
Sentinel as StateSentinel,
|
Sentinel as StateSentinel,
|
||||||
@@ -68,6 +70,7 @@ from synapse.types.handlers.sliding_sync import (
|
|||||||
)
|
)
|
||||||
from synapse.types.state import StateFilter
|
from synapse.types.state import StateFilter
|
||||||
from synapse.util import MutableOverlayMapping
|
from synapse.util import MutableOverlayMapping
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.sentinel import Sentinel
|
from synapse.util.sentinel import Sentinel
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -77,6 +80,27 @@ if TYPE_CHECKING:
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Minimum time in milliseconds since the last sync before we consider expiring
|
||||||
|
# the connection due to too many rooms to send. This stops from getting into
|
||||||
|
# tight loops with clients that request lots of data at once.
|
||||||
|
#
|
||||||
|
# c.f. `NUM_ROOMS_THRESHOLD`. These values are somewhat arbitrary picked.
|
||||||
|
MINIMUM_NOT_USED_AGE_EXPIRY = Duration(hours=1)
|
||||||
|
|
||||||
|
# How many rooms with updates we allow before we consider the connection expired
|
||||||
|
# due to too many rooms to send.
|
||||||
|
#
|
||||||
|
# c.f. `MINIMUM_NOT_USED_AGE_EXPIRY_MS`. These values are somewhat arbitrary
|
||||||
|
# picked.
|
||||||
|
NUM_ROOMS_THRESHOLD = 100
|
||||||
|
|
||||||
|
# Sanity check that our minimum age is sensible compared to the update interval,
|
||||||
|
# i.e. if `MINIMUM_NOT_USED_AGE_EXPIRY_MS` is too small then we might expire the
|
||||||
|
# connection even if it is actively being used (and we're just not updating the
|
||||||
|
# DB frequently enough). We arbitrarily double the update interval to give some
|
||||||
|
# wiggle room.
|
||||||
|
assert 2 * UPDATE_INTERVAL_LAST_USED_TS < MINIMUM_NOT_USED_AGE_EXPIRY
|
||||||
|
|
||||||
# Helper definition for the types that we might return. We do this to avoid
|
# Helper definition for the types that we might return. We do this to avoid
|
||||||
# copying data between types (which can be expensive for many rooms).
|
# copying data between types (which can be expensive for many rooms).
|
||||||
RoomsForUserType = RoomsForUserStateReset | RoomsForUser | RoomsForUserSlidingSync
|
RoomsForUserType = RoomsForUserStateReset | RoomsForUser | RoomsForUserSlidingSync
|
||||||
@@ -176,6 +200,7 @@ class SlidingSyncRoomLists:
|
|||||||
self.storage_controllers = hs.get_storage_controllers()
|
self.storage_controllers = hs.get_storage_controllers()
|
||||||
self.rooms_to_exclude_globally = hs.config.server.rooms_to_exclude_from_sync
|
self.rooms_to_exclude_globally = hs.config.server.rooms_to_exclude_from_sync
|
||||||
self.is_mine_id = hs.is_mine_id
|
self.is_mine_id = hs.is_mine_id
|
||||||
|
self._clock = hs.get_clock()
|
||||||
|
|
||||||
async def compute_interested_rooms(
|
async def compute_interested_rooms(
|
||||||
self,
|
self,
|
||||||
@@ -857,11 +882,41 @@ class SlidingSyncRoomLists:
|
|||||||
|
|
||||||
# We only need to check for new events since any state changes
|
# We only need to check for new events since any state changes
|
||||||
# will also come down as new events.
|
# will also come down as new events.
|
||||||
rooms_that_have_updates = (
|
|
||||||
self.store.get_rooms_that_might_have_updates(
|
rooms_that_have_updates = await (
|
||||||
|
self.store.get_rooms_that_have_updates_since_sliding_sync_table(
|
||||||
relevant_room_map.keys(), from_token.room_key
|
relevant_room_map.keys(), from_token.room_key
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check if we have lots of updates to send, if so then its
|
||||||
|
# better for us to tell the client to do a full resync
|
||||||
|
# instead (to try and avoid long SSS response times when
|
||||||
|
# there is new data).
|
||||||
|
#
|
||||||
|
# Due to the construction of the SSS API, the client is in
|
||||||
|
# charge of setting the range of rooms to request updates
|
||||||
|
# for. Generally, it will start with a small range and then
|
||||||
|
# expand (and occasionally it may contract the range again
|
||||||
|
# if its been offline for a while). If we know there are a
|
||||||
|
# lot of updates, it's better to reset the connection and
|
||||||
|
# wait for the client to start again (with a much smaller
|
||||||
|
# range) than to try and send down a large number of updates
|
||||||
|
# (which can take a long time).
|
||||||
|
#
|
||||||
|
# We only do this if the last sync was over
|
||||||
|
# `MINIMUM_NOT_USED_AGE_EXPIRY_MS` to ensure we don't get
|
||||||
|
# into tight loops with clients that keep requesting large
|
||||||
|
# sliding sync windows.
|
||||||
|
if len(rooms_that_have_updates) > NUM_ROOMS_THRESHOLD:
|
||||||
|
last_sync_ts = previous_connection_state.last_used_ts
|
||||||
|
if (
|
||||||
|
last_sync_ts is not None
|
||||||
|
and (self._clock.time_msec() - last_sync_ts)
|
||||||
|
> MINIMUM_NOT_USED_AGE_EXPIRY.as_millis()
|
||||||
|
):
|
||||||
|
raise SlidingSyncUnknownPosition()
|
||||||
|
|
||||||
rooms_should_send.update(rooms_that_have_updates)
|
rooms_should_send.update(rooms_that_have_updates)
|
||||||
relevant_rooms_to_send_map = {
|
relevant_rooms_to_send_map = {
|
||||||
room_id: room_sync_config
|
room_id: room_sync_config
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class SlidingSyncConnectionStore:
|
|||||||
"""
|
"""
|
||||||
# If this is our first request, there is no previous connection state to fetch out of the database
|
# If this is our first request, there is no previous connection state to fetch out of the database
|
||||||
if from_token is None or from_token.connection_position == 0:
|
if from_token is None or from_token.connection_position == 0:
|
||||||
return PerConnectionState()
|
return PerConnectionState(last_used_ts=None)
|
||||||
|
|
||||||
conn_id = sync_config.conn_id or ""
|
conn_id = sync_config.conn_id or ""
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from synapse.api.constants import EventContentFields, EventTypes, Membership
|
|||||||
from synapse.metrics import SERVER_NAME_LABEL, event_processing_positions
|
from synapse.metrics import SERVER_NAME_LABEL, event_processing_positions
|
||||||
from synapse.storage.databases.main.state_deltas import StateDelta
|
from synapse.storage.databases.main.state_deltas import StateDelta
|
||||||
from synapse.types import JsonDict
|
from synapse.types import JsonDict
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.events import get_plain_text_topic_from_event_content
|
from synapse.util.events import get_plain_text_topic_from_event_content
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -72,7 +73,7 @@ class StatsHandler:
|
|||||||
# We kick this off so that we don't have to wait for a change before
|
# We kick this off so that we don't have to wait for a change before
|
||||||
# we start populating stats
|
# we start populating stats
|
||||||
self.clock.call_later(
|
self.clock.call_later(
|
||||||
0,
|
Duration(seconds=0),
|
||||||
self.notify_new_event,
|
self.notify_new_event,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ from synapse.api.constants import (
|
|||||||
Direction,
|
Direction,
|
||||||
EventContentFields,
|
EventContentFields,
|
||||||
EventTypes,
|
EventTypes,
|
||||||
JoinRules,
|
|
||||||
Membership,
|
Membership,
|
||||||
)
|
)
|
||||||
from synapse.api.filtering import FilterCollection
|
from synapse.api.filtering import FilterCollection
|
||||||
@@ -790,22 +789,13 @@ class SyncHandler:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
filtered_recents = await filter_events_for_client(
|
loaded_recents = await filter_events_for_client(
|
||||||
self._storage_controllers,
|
self._storage_controllers,
|
||||||
sync_config.user.to_string(),
|
sync_config.user.to_string(),
|
||||||
loaded_recents,
|
loaded_recents,
|
||||||
always_include_ids=current_state_ids,
|
always_include_ids=current_state_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
loaded_recents = []
|
|
||||||
for event in filtered_recents:
|
|
||||||
if event.type == EventTypes.CallInvite:
|
|
||||||
room_info = await self.store.get_room_with_stats(event.room_id)
|
|
||||||
assert room_info is not None
|
|
||||||
if room_info.join_rules == JoinRules.PUBLIC:
|
|
||||||
continue
|
|
||||||
loaded_recents.append(event)
|
|
||||||
|
|
||||||
log_kv({"loaded_recents_after_client_filtering": len(loaded_recents)})
|
log_kv({"loaded_recents_after_client_filtering": len(loaded_recents)})
|
||||||
|
|
||||||
loaded_recents.extend(recents)
|
loaded_recents.extend(recents)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ from synapse.types import (
|
|||||||
UserID,
|
UserID,
|
||||||
)
|
)
|
||||||
from synapse.util.caches.stream_change_cache import StreamChangeCache
|
from synapse.util.caches.stream_change_cache import StreamChangeCache
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
from synapse.util.retryutils import filter_destinations_by_retry_limiter
|
from synapse.util.retryutils import filter_destinations_by_retry_limiter
|
||||||
from synapse.util.wheel_timer import WheelTimer
|
from synapse.util.wheel_timer import WheelTimer
|
||||||
@@ -60,15 +61,15 @@ class RoomMember:
|
|||||||
|
|
||||||
|
|
||||||
# How often we expect remote servers to resend us presence.
|
# How often we expect remote servers to resend us presence.
|
||||||
FEDERATION_TIMEOUT = 60 * 1000
|
FEDERATION_TIMEOUT = Duration(minutes=1)
|
||||||
|
|
||||||
# How often to resend typing across federation.
|
# How often to resend typing across federation.
|
||||||
FEDERATION_PING_INTERVAL = 40 * 1000
|
FEDERATION_PING_INTERVAL = Duration(seconds=40)
|
||||||
|
|
||||||
|
|
||||||
# How long to remember a typing notification happened in a room before
|
# How long to remember a typing notification happened in a room before
|
||||||
# forgetting about it.
|
# forgetting about it.
|
||||||
FORGET_TIMEOUT = 10 * 60 * 1000
|
FORGET_TIMEOUT = Duration(minutes=10)
|
||||||
|
|
||||||
|
|
||||||
class FollowerTypingHandler:
|
class FollowerTypingHandler:
|
||||||
@@ -106,7 +107,7 @@ class FollowerTypingHandler:
|
|||||||
|
|
||||||
self._rooms_updated: set[str] = set()
|
self._rooms_updated: set[str] = set()
|
||||||
|
|
||||||
self.clock.looping_call(self._handle_timeouts, 5000)
|
self.clock.looping_call(self._handle_timeouts, Duration(seconds=5))
|
||||||
self.clock.looping_call(self._prune_old_typing, FORGET_TIMEOUT)
|
self.clock.looping_call(self._prune_old_typing, FORGET_TIMEOUT)
|
||||||
|
|
||||||
def _reset(self) -> None:
|
def _reset(self) -> None:
|
||||||
@@ -141,7 +142,10 @@ class FollowerTypingHandler:
|
|||||||
# user.
|
# user.
|
||||||
if self.federation and self.is_mine_id(member.user_id):
|
if self.federation and self.is_mine_id(member.user_id):
|
||||||
last_fed_poke = self._member_last_federation_poke.get(member, None)
|
last_fed_poke = self._member_last_federation_poke.get(member, None)
|
||||||
if not last_fed_poke or last_fed_poke + FEDERATION_PING_INTERVAL <= now:
|
if (
|
||||||
|
not last_fed_poke
|
||||||
|
or last_fed_poke + FEDERATION_PING_INTERVAL.as_millis() <= now
|
||||||
|
):
|
||||||
self.hs.run_as_background_process(
|
self.hs.run_as_background_process(
|
||||||
"typing._push_remote",
|
"typing._push_remote",
|
||||||
self._push_remote,
|
self._push_remote,
|
||||||
@@ -165,7 +169,7 @@ class FollowerTypingHandler:
|
|||||||
|
|
||||||
now = self.clock.time_msec()
|
now = self.clock.time_msec()
|
||||||
self.wheel_timer.insert(
|
self.wheel_timer.insert(
|
||||||
now=now, obj=member, then=now + FEDERATION_PING_INTERVAL
|
now=now, obj=member, then=now + FEDERATION_PING_INTERVAL.as_millis()
|
||||||
)
|
)
|
||||||
|
|
||||||
hosts: StrCollection = (
|
hosts: StrCollection = (
|
||||||
@@ -315,7 +319,7 @@ class TypingWriterHandler(FollowerTypingHandler):
|
|||||||
|
|
||||||
if requester.shadow_banned:
|
if requester.shadow_banned:
|
||||||
# We randomly sleep a bit just to annoy the requester.
|
# We randomly sleep a bit just to annoy the requester.
|
||||||
await self.clock.sleep(random.randint(1, 10))
|
await self.clock.sleep(Duration(seconds=random.randint(1, 10)))
|
||||||
raise ShadowBanError()
|
raise ShadowBanError()
|
||||||
|
|
||||||
await self.auth.check_user_in_room(room_id, requester)
|
await self.auth.check_user_in_room(room_id, requester)
|
||||||
@@ -350,7 +354,7 @@ class TypingWriterHandler(FollowerTypingHandler):
|
|||||||
|
|
||||||
if requester.shadow_banned:
|
if requester.shadow_banned:
|
||||||
# We randomly sleep a bit just to annoy the requester.
|
# We randomly sleep a bit just to annoy the requester.
|
||||||
await self.clock.sleep(random.randint(1, 10))
|
await self.clock.sleep(Duration(seconds=random.randint(1, 10)))
|
||||||
raise ShadowBanError()
|
raise ShadowBanError()
|
||||||
|
|
||||||
await self.auth.check_user_in_room(room_id, requester)
|
await self.auth.check_user_in_room(room_id, requester)
|
||||||
@@ -428,8 +432,10 @@ class TypingWriterHandler(FollowerTypingHandler):
|
|||||||
if user.domain in domains:
|
if user.domain in domains:
|
||||||
logger.info("Got typing update from %s: %r", user_id, content)
|
logger.info("Got typing update from %s: %r", user_id, content)
|
||||||
now = self.clock.time_msec()
|
now = self.clock.time_msec()
|
||||||
self._member_typing_until[member] = now + FEDERATION_TIMEOUT
|
self._member_typing_until[member] = now + FEDERATION_TIMEOUT.as_millis()
|
||||||
self.wheel_timer.insert(now=now, obj=member, then=now + FEDERATION_TIMEOUT)
|
self.wheel_timer.insert(
|
||||||
|
now=now, obj=member, then=now + FEDERATION_TIMEOUT.as_millis()
|
||||||
|
)
|
||||||
self._push_update_local(member=member, typing=content["typing"])
|
self._push_update_local(member=member, typing=content["typing"])
|
||||||
|
|
||||||
def _push_update_local(self, member: RoomMember, typing: bool) -> None:
|
def _push_update_local(self, member: RoomMember, typing: bool) -> None:
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ from synapse.storage.databases.main.state_deltas import StateDelta
|
|||||||
from synapse.storage.databases.main.user_directory import SearchResult
|
from synapse.storage.databases.main.user_directory import SearchResult
|
||||||
from synapse.storage.roommember import ProfileInfo
|
from synapse.storage.roommember import ProfileInfo
|
||||||
from synapse.types import UserID
|
from synapse.types import UserID
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
from synapse.util.retryutils import NotRetryingDestination
|
from synapse.util.retryutils import NotRetryingDestination
|
||||||
from synapse.util.stringutils import non_null_str_or_none
|
from synapse.util.stringutils import non_null_str_or_none
|
||||||
@@ -52,7 +53,7 @@ logger = logging.getLogger(__name__)
|
|||||||
# Don't refresh a stale user directory entry, using a Federation /profile request,
|
# Don't refresh a stale user directory entry, using a Federation /profile request,
|
||||||
# for 60 seconds. This gives time for other state events to arrive (which will
|
# for 60 seconds. This gives time for other state events to arrive (which will
|
||||||
# then be coalesced such that only one /profile request is made).
|
# then be coalesced such that only one /profile request is made).
|
||||||
USER_DIRECTORY_STALE_REFRESH_TIME_MS = 60 * 1000
|
USER_DIRECTORY_STALE_REFRESH_TIME = Duration(minutes=1)
|
||||||
|
|
||||||
# Maximum number of remote servers that we will attempt to refresh profiles for
|
# Maximum number of remote servers that we will attempt to refresh profiles for
|
||||||
# in one go.
|
# in one go.
|
||||||
@@ -60,7 +61,7 @@ MAX_SERVERS_TO_REFRESH_PROFILES_FOR_IN_ONE_GO = 5
|
|||||||
|
|
||||||
# As long as we have servers to refresh (without backoff), keep adding more
|
# As long as we have servers to refresh (without backoff), keep adding more
|
||||||
# every 15 seconds.
|
# every 15 seconds.
|
||||||
INTERVAL_TO_ADD_MORE_SERVERS_TO_REFRESH_PROFILES = 15
|
INTERVAL_TO_ADD_MORE_SERVERS_TO_REFRESH_PROFILES = Duration(seconds=15)
|
||||||
|
|
||||||
|
|
||||||
def calculate_time_of_next_retry(now_ts: int, retry_count: int) -> int:
|
def calculate_time_of_next_retry(now_ts: int, retry_count: int) -> int:
|
||||||
@@ -137,13 +138,13 @@ class UserDirectoryHandler(StateDeltasHandler):
|
|||||||
# We kick this off so that we don't have to wait for a change before
|
# We kick this off so that we don't have to wait for a change before
|
||||||
# we start populating the user directory
|
# we start populating the user directory
|
||||||
self.clock.call_later(
|
self.clock.call_later(
|
||||||
0,
|
Duration(seconds=0),
|
||||||
self.notify_new_event,
|
self.notify_new_event,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Kick off the profile refresh process on startup
|
# Kick off the profile refresh process on startup
|
||||||
self._refresh_remote_profiles_call_later = self.clock.call_later(
|
self._refresh_remote_profiles_call_later = self.clock.call_later(
|
||||||
10,
|
Duration(seconds=10),
|
||||||
self.kick_off_remote_profile_refresh_process,
|
self.kick_off_remote_profile_refresh_process,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -550,7 +551,7 @@ class UserDirectoryHandler(StateDeltasHandler):
|
|||||||
now_ts = self.clock.time_msec()
|
now_ts = self.clock.time_msec()
|
||||||
await self.store.set_remote_user_profile_in_user_dir_stale(
|
await self.store.set_remote_user_profile_in_user_dir_stale(
|
||||||
user_id,
|
user_id,
|
||||||
next_try_at_ms=now_ts + USER_DIRECTORY_STALE_REFRESH_TIME_MS,
|
next_try_at_ms=now_ts + USER_DIRECTORY_STALE_REFRESH_TIME.as_millis(),
|
||||||
retry_counter=0,
|
retry_counter=0,
|
||||||
)
|
)
|
||||||
# Schedule a wake-up to refresh the user directory for this server.
|
# Schedule a wake-up to refresh the user directory for this server.
|
||||||
@@ -558,13 +559,13 @@ class UserDirectoryHandler(StateDeltasHandler):
|
|||||||
# other servers ahead of it in the queue to get in the way of updating
|
# other servers ahead of it in the queue to get in the way of updating
|
||||||
# the profile if the server only just sent us an event.
|
# the profile if the server only just sent us an event.
|
||||||
self.clock.call_later(
|
self.clock.call_later(
|
||||||
USER_DIRECTORY_STALE_REFRESH_TIME_MS // 1000 + 1,
|
USER_DIRECTORY_STALE_REFRESH_TIME + Duration(seconds=1),
|
||||||
self.kick_off_remote_profile_refresh_process_for_remote_server,
|
self.kick_off_remote_profile_refresh_process_for_remote_server,
|
||||||
UserID.from_string(user_id).domain,
|
UserID.from_string(user_id).domain,
|
||||||
)
|
)
|
||||||
# Schedule a wake-up to handle any backoffs that may occur in the future.
|
# Schedule a wake-up to handle any backoffs that may occur in the future.
|
||||||
self.clock.call_later(
|
self.clock.call_later(
|
||||||
2 * USER_DIRECTORY_STALE_REFRESH_TIME_MS // 1000 + 1,
|
USER_DIRECTORY_STALE_REFRESH_TIME * 2 + Duration(seconds=1),
|
||||||
self.kick_off_remote_profile_refresh_process,
|
self.kick_off_remote_profile_refresh_process,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -656,7 +657,9 @@ class UserDirectoryHandler(StateDeltasHandler):
|
|||||||
if not users:
|
if not users:
|
||||||
return
|
return
|
||||||
_, _, next_try_at_ts = users[0]
|
_, _, next_try_at_ts = users[0]
|
||||||
delay = ((next_try_at_ts - self.clock.time_msec()) // 1000) + 2
|
delay = Duration(
|
||||||
|
milliseconds=next_try_at_ts - self.clock.time_msec()
|
||||||
|
) + Duration(seconds=2)
|
||||||
self._refresh_remote_profiles_call_later = self.clock.call_later(
|
self._refresh_remote_profiles_call_later = self.clock.call_later(
|
||||||
delay,
|
delay,
|
||||||
self.kick_off_remote_profile_refresh_process,
|
self.kick_off_remote_profile_refresh_process,
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ from synapse.metrics.background_process_metrics import wrap_as_background_proces
|
|||||||
from synapse.storage.databases.main.lock import Lock, LockStore
|
from synapse.storage.databases.main.lock import Lock, LockStore
|
||||||
from synapse.util.async_helpers import timeout_deferred
|
from synapse.util.async_helpers import timeout_deferred
|
||||||
from synapse.util.clock import Clock
|
from synapse.util.clock import Clock
|
||||||
from synapse.util.constants import ONE_MINUTE_SECONDS
|
from synapse.util.duration import Duration
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.logging.opentracing import opentracing
|
from synapse.logging.opentracing import opentracing
|
||||||
@@ -72,7 +72,7 @@ class WorkerLocksHandler:
|
|||||||
# that lock.
|
# that lock.
|
||||||
self._locks: dict[tuple[str, str], WeakSet[WaitingLock | WaitingMultiLock]] = {}
|
self._locks: dict[tuple[str, str], WeakSet[WaitingLock | WaitingMultiLock]] = {}
|
||||||
|
|
||||||
self._clock.looping_call(self._cleanup_locks, 30_000)
|
self._clock.looping_call(self._cleanup_locks, Duration(seconds=30))
|
||||||
|
|
||||||
self._notifier.add_lock_released_callback(self._on_lock_released)
|
self._notifier.add_lock_released_callback(self._on_lock_released)
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ class WorkerLocksHandler:
|
|||||||
lock.release_lock()
|
lock.release_lock()
|
||||||
|
|
||||||
self._clock.call_later(
|
self._clock.call_later(
|
||||||
0,
|
Duration(seconds=0),
|
||||||
_wake_all_locks,
|
_wake_all_locks,
|
||||||
locks,
|
locks,
|
||||||
)
|
)
|
||||||
@@ -276,7 +276,7 @@ class WaitingLock:
|
|||||||
def _get_next_retry_interval(self) -> float:
|
def _get_next_retry_interval(self) -> float:
|
||||||
next = self._retry_interval
|
next = self._retry_interval
|
||||||
self._retry_interval = max(5, next * 2)
|
self._retry_interval = max(5, next * 2)
|
||||||
if self._retry_interval > 10 * ONE_MINUTE_SECONDS: # >7 iterations
|
if self._retry_interval > Duration(minutes=10).as_secs(): # >7 iterations
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Lock timeout is getting excessive: %ss. There may be a deadlock.",
|
"Lock timeout is getting excessive: %ss. There may be a deadlock.",
|
||||||
self._retry_interval,
|
self._retry_interval,
|
||||||
@@ -363,7 +363,7 @@ class WaitingMultiLock:
|
|||||||
def _get_next_retry_interval(self) -> float:
|
def _get_next_retry_interval(self) -> float:
|
||||||
next = self._retry_interval
|
next = self._retry_interval
|
||||||
self._retry_interval = max(5, next * 2)
|
self._retry_interval = max(5, next * 2)
|
||||||
if self._retry_interval > 10 * ONE_MINUTE_SECONDS: # >7 iterations
|
if self._retry_interval > Duration(minutes=10).as_secs(): # >7 iterations
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Lock timeout is getting excessive: %ss. There may be a deadlock.",
|
"Lock timeout is getting excessive: %ss. There may be a deadlock.",
|
||||||
self._retry_interval,
|
self._retry_interval,
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ from synapse.metrics import SERVER_NAME_LABEL
|
|||||||
from synapse.types import ISynapseReactor, StrSequence
|
from synapse.types import ISynapseReactor, StrSequence
|
||||||
from synapse.util.async_helpers import timeout_deferred
|
from synapse.util.async_helpers import timeout_deferred
|
||||||
from synapse.util.clock import Clock
|
from synapse.util.clock import Clock
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.json import json_decoder
|
from synapse.util.json import json_decoder
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -161,7 +162,9 @@ def _is_ip_blocked(
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
_EPSILON = 0.00000001
|
# The delay used by the scheduler to schedule tasks "as soon as possible", while
|
||||||
|
# still allowing other tasks to run between runs.
|
||||||
|
_EPSILON = Duration(microseconds=1)
|
||||||
|
|
||||||
|
|
||||||
def _make_scheduler(clock: Clock) -> Callable[[Callable[[], object]], IDelayedCall]:
|
def _make_scheduler(clock: Clock) -> Callable[[Callable[[], object]], IDelayedCall]:
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from synapse.logging.context import make_deferred_yieldable
|
|||||||
from synapse.types import ISynapseThreadlessReactor
|
from synapse.types import ISynapseThreadlessReactor
|
||||||
from synapse.util.caches.ttlcache import TTLCache
|
from synapse.util.caches.ttlcache import TTLCache
|
||||||
from synapse.util.clock import Clock
|
from synapse.util.clock import Clock
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.json import json_decoder
|
from synapse.util.json import json_decoder
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
|
|
||||||
@@ -315,7 +316,7 @@ class WellKnownResolver:
|
|||||||
logger.info("Error fetching %s: %s. Retrying", uri_str, e)
|
logger.info("Error fetching %s: %s. Retrying", uri_str, e)
|
||||||
|
|
||||||
# Sleep briefly in the hopes that they come back up
|
# Sleep briefly in the hopes that they come back up
|
||||||
await self._clock.sleep(0.5)
|
await self._clock.sleep(Duration(milliseconds=500))
|
||||||
|
|
||||||
|
|
||||||
def _cache_period_from_headers(
|
def _cache_period_from_headers(
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ from synapse.logging.opentracing import active_span, start_active_span, trace_se
|
|||||||
from synapse.util.caches import intern_dict
|
from synapse.util.caches import intern_dict
|
||||||
from synapse.util.cancellation import is_function_cancellable
|
from synapse.util.cancellation import is_function_cancellable
|
||||||
from synapse.util.clock import Clock
|
from synapse.util.clock import Clock
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.iterutils import chunk_seq
|
from synapse.util.iterutils import chunk_seq
|
||||||
from synapse.util.json import json_encoder
|
from synapse.util.json import json_encoder
|
||||||
|
|
||||||
@@ -334,7 +335,7 @@ class _AsyncResource(resource.Resource, metaclass=abc.ABCMeta):
|
|||||||
callback_return = await self._async_render(request)
|
callback_return = await self._async_render(request)
|
||||||
except LimitExceededError as e:
|
except LimitExceededError as e:
|
||||||
if e.pause:
|
if e.pause:
|
||||||
await self._clock.sleep(e.pause)
|
await self._clock.sleep(Duration(seconds=e.pause))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if callback_return is not None:
|
if callback_return is not None:
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
@@ -36,6 +37,7 @@ from twisted.web.http import HTTPChannel
|
|||||||
from twisted.web.resource import IResource, Resource
|
from twisted.web.resource import IResource, Resource
|
||||||
from twisted.web.server import Request
|
from twisted.web.server import Request
|
||||||
|
|
||||||
|
from synapse.api.errors import Codes, SynapseError
|
||||||
from synapse.config.server import ListenerConfig
|
from synapse.config.server import ListenerConfig
|
||||||
from synapse.http import get_request_user_agent, redact_uri
|
from synapse.http import get_request_user_agent, redact_uri
|
||||||
from synapse.http.proxy import ProxySite
|
from synapse.http.proxy import ProxySite
|
||||||
@@ -59,6 +61,10 @@ logger = logging.getLogger(__name__)
|
|||||||
_next_request_seq = 0
|
_next_request_seq = 0
|
||||||
|
|
||||||
|
|
||||||
|
class ContentLengthError(SynapseError):
|
||||||
|
"""Raised when content-length validation fails."""
|
||||||
|
|
||||||
|
|
||||||
class SynapseRequest(Request):
|
class SynapseRequest(Request):
|
||||||
"""Class which encapsulates an HTTP request to synapse.
|
"""Class which encapsulates an HTTP request to synapse.
|
||||||
|
|
||||||
@@ -144,36 +150,150 @@ class SynapseRequest(Request):
|
|||||||
self.synapse_site.site_tag,
|
self.synapse_site.site_tag,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _respond_with_error(self, synapse_error: SynapseError) -> None:
|
||||||
|
"""Send an error response and close the connection."""
|
||||||
|
self.setResponseCode(synapse_error.code)
|
||||||
|
error_response_bytes = json.dumps(synapse_error.error_dict(None)).encode()
|
||||||
|
|
||||||
|
self.responseHeaders.setRawHeaders(b"Content-Type", [b"application/json"])
|
||||||
|
self.responseHeaders.setRawHeaders(
|
||||||
|
b"Content-Length", [f"{len(error_response_bytes)}"]
|
||||||
|
)
|
||||||
|
self.write(error_response_bytes)
|
||||||
|
self.loseConnection()
|
||||||
|
|
||||||
|
def _get_content_length_from_headers(self) -> int | None:
|
||||||
|
"""Attempts to obtain the `Content-Length` value from the request's headers.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Content length as `int` if present. Otherwise `None`.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ContentLengthError: if multiple `Content-Length` headers are present or the
|
||||||
|
value is not an `int`.
|
||||||
|
"""
|
||||||
|
content_length_headers = self.requestHeaders.getRawHeaders(b"Content-Length")
|
||||||
|
if content_length_headers is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# If there are multiple `Content-Length` headers return an error.
|
||||||
|
# We don't want to even try to pick the right one if there are multiple
|
||||||
|
# as we could run into problems similar to request smuggling vulnerabilities
|
||||||
|
# which rely on the mismatch of how different systems interpret information.
|
||||||
|
if len(content_length_headers) != 1:
|
||||||
|
raise ContentLengthError(
|
||||||
|
HTTPStatus.BAD_REQUEST,
|
||||||
|
"Multiple Content-Length headers received",
|
||||||
|
Codes.UNKNOWN,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return int(content_length_headers[0])
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
raise ContentLengthError(
|
||||||
|
HTTPStatus.BAD_REQUEST,
|
||||||
|
"Content-Length header value is not a valid integer",
|
||||||
|
Codes.UNKNOWN,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _validate_content_length(self) -> None:
|
||||||
|
"""Validate Content-Length header and actual content size.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ContentLengthError: If validation fails.
|
||||||
|
"""
|
||||||
|
# we should have a `content` by now.
|
||||||
|
assert self.content, "_validate_content_length() called before gotLength()"
|
||||||
|
content_length = self._get_content_length_from_headers()
|
||||||
|
|
||||||
|
if content_length is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
actual_content_length = self.content.tell()
|
||||||
|
|
||||||
|
if content_length > self._max_request_body_size:
|
||||||
|
logger.info(
|
||||||
|
"Rejecting request from %s because Content-Length %d exceeds maximum size %d: %s %s",
|
||||||
|
self.client,
|
||||||
|
content_length,
|
||||||
|
self._max_request_body_size,
|
||||||
|
self.get_method(),
|
||||||
|
self.get_redacted_uri(),
|
||||||
|
)
|
||||||
|
raise ContentLengthError(
|
||||||
|
HTTPStatus.REQUEST_ENTITY_TOO_LARGE,
|
||||||
|
f"Request content is too large (>{self._max_request_body_size})",
|
||||||
|
Codes.TOO_LARGE,
|
||||||
|
)
|
||||||
|
|
||||||
|
if content_length != actual_content_length:
|
||||||
|
comparison = (
|
||||||
|
"smaller" if content_length < actual_content_length else "larger"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Rejecting request from %s because Content-Length %d is %s than the request content size %d: %s %s",
|
||||||
|
self.client,
|
||||||
|
content_length,
|
||||||
|
comparison,
|
||||||
|
actual_content_length,
|
||||||
|
self.get_method(),
|
||||||
|
self.get_redacted_uri(),
|
||||||
|
)
|
||||||
|
raise ContentLengthError(
|
||||||
|
HTTPStatus.BAD_REQUEST,
|
||||||
|
f"Rejecting request as the Content-Length header value {content_length} "
|
||||||
|
f"is {comparison} than the actual request content size {actual_content_length}",
|
||||||
|
Codes.UNKNOWN,
|
||||||
|
)
|
||||||
|
|
||||||
# Twisted machinery: this method is called by the Channel once the full request has
|
# Twisted machinery: this method is called by the Channel once the full request has
|
||||||
# been received, to dispatch the request to a resource.
|
# been received, to dispatch the request to a resource.
|
||||||
#
|
|
||||||
# We're patching Twisted to bail/abort early when we see someone trying to upload
|
|
||||||
# `multipart/form-data` so we can avoid Twisted parsing the entire request body into
|
|
||||||
# in-memory (specific problem of this specific `Content-Type`). This protects us
|
|
||||||
# from an attacker uploading something bigger than the available RAM and crashing
|
|
||||||
# the server with a `MemoryError`, or carefully block just enough resources to cause
|
|
||||||
# all other requests to fail.
|
|
||||||
#
|
|
||||||
# FIXME: This can be removed once we Twisted releases a fix and we update to a
|
|
||||||
# version that is patched
|
|
||||||
def requestReceived(self, command: bytes, path: bytes, version: bytes) -> None:
|
def requestReceived(self, command: bytes, path: bytes, version: bytes) -> None:
|
||||||
|
# In the case of a Content-Length header being present, and it's value being too
|
||||||
|
# large, throw a proper error to make debugging issues due to overly large requests much
|
||||||
|
# easier. Currently we handle such cases in `handleContentChunk` and abort the
|
||||||
|
# connection without providing a proper HTTP response.
|
||||||
|
#
|
||||||
|
# Attempting to write an HTTP response from within `handleContentChunk` does not
|
||||||
|
# work, so the code here has been added to at least provide a response in the
|
||||||
|
# case of the Content-Length header being present.
|
||||||
|
self.method, self.uri = command, path
|
||||||
|
self.clientproto = version
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._validate_content_length()
|
||||||
|
except ContentLengthError as e:
|
||||||
|
self._respond_with_error(e)
|
||||||
|
return
|
||||||
|
|
||||||
|
# We're patching Twisted to bail/abort early when we see someone trying to upload
|
||||||
|
# `multipart/form-data` so we can avoid Twisted parsing the entire request body into
|
||||||
|
# in-memory (specific problem of this specific `Content-Type`). This protects us
|
||||||
|
# from an attacker uploading something bigger than the available RAM and crashing
|
||||||
|
# the server with a `MemoryError`, or carefully block just enough resources to cause
|
||||||
|
# all other requests to fail.
|
||||||
|
#
|
||||||
|
# FIXME: This can be removed once Twisted releases a fix and we update to a
|
||||||
|
# version that is patched
|
||||||
|
# See: https://github.com/element-hq/synapse/security/advisories/GHSA-rfq8-j7rh-8hf2
|
||||||
if command == b"POST":
|
if command == b"POST":
|
||||||
ctype = self.requestHeaders.getRawHeaders(b"content-type")
|
ctype = self.requestHeaders.getRawHeaders(b"content-type")
|
||||||
if ctype and b"multipart/form-data" in ctype[0]:
|
if ctype and b"multipart/form-data" in ctype[0]:
|
||||||
self.method, self.uri = command, path
|
logger.warning(
|
||||||
self.clientproto = version
|
"Aborting connection from %s because `content-type: multipart/form-data` is unsupported: %s %s",
|
||||||
|
self.client,
|
||||||
|
self.get_method(),
|
||||||
|
self.get_redacted_uri(),
|
||||||
|
)
|
||||||
|
|
||||||
self.code = HTTPStatus.UNSUPPORTED_MEDIA_TYPE.value
|
self.code = HTTPStatus.UNSUPPORTED_MEDIA_TYPE.value
|
||||||
self.code_message = bytes(
|
self.code_message = bytes(
|
||||||
HTTPStatus.UNSUPPORTED_MEDIA_TYPE.phrase, "ascii"
|
HTTPStatus.UNSUPPORTED_MEDIA_TYPE.phrase, "ascii"
|
||||||
)
|
)
|
||||||
self.responseHeaders.setRawHeaders(b"content-length", [b"0"])
|
|
||||||
|
|
||||||
logger.warning(
|
# FIXME: Return a better error response here similar to the
|
||||||
"Aborting connection from %s because `content-type: multipart/form-data` is unsupported: %s %s",
|
# `error_response_json` returned in other code paths here.
|
||||||
self.client,
|
self.responseHeaders.setRawHeaders(b"Content-Length", [b"0"])
|
||||||
command,
|
|
||||||
path,
|
|
||||||
)
|
|
||||||
self.write(b"")
|
self.write(b"")
|
||||||
self.loseConnection()
|
self.loseConnection()
|
||||||
return
|
return
|
||||||
@@ -815,6 +935,13 @@ class SynapseSite(ProxySite):
|
|||||||
protocol.transport.loseConnection()
|
protocol.transport.loseConnection()
|
||||||
self.connections.clear()
|
self.connections.clear()
|
||||||
|
|
||||||
|
# Replace the resource tree with an empty resource to break circular references
|
||||||
|
# to the resource tree which holds a bunch of homeserver references. This is
|
||||||
|
# important if we try to call `hs.shutdown()` after `start` fails. For some
|
||||||
|
# reason, this doesn't seem to be necessary in the normal case where `start`
|
||||||
|
# succeeds and we call `hs.shutdown()` later.
|
||||||
|
self.resource = Resource()
|
||||||
|
|
||||||
def log(self, request: SynapseRequest) -> None: # type: ignore[override]
|
def log(self, request: SynapseRequest) -> None: # type: ignore[override]
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ from synapse.media.url_previewer import UrlPreviewer
|
|||||||
from synapse.storage.databases.main.media_repository import LocalMedia, RemoteMedia
|
from synapse.storage.databases.main.media_repository import LocalMedia, RemoteMedia
|
||||||
from synapse.types import UserID
|
from synapse.types import UserID
|
||||||
from synapse.util.async_helpers import Linearizer
|
from synapse.util.async_helpers import Linearizer
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.retryutils import NotRetryingDestination
|
from synapse.util.retryutils import NotRetryingDestination
|
||||||
from synapse.util.stringutils import random_string
|
from synapse.util.stringutils import random_string
|
||||||
|
|
||||||
@@ -80,10 +81,10 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# How often to run the background job to update the "recently accessed"
|
# How often to run the background job to update the "recently accessed"
|
||||||
# attribute of local and remote media.
|
# attribute of local and remote media.
|
||||||
UPDATE_RECENTLY_ACCESSED_TS = 60 * 1000 # 1 minute
|
UPDATE_RECENTLY_ACCESSED_TS = Duration(minutes=1)
|
||||||
# How often to run the background job to check for local and remote media
|
# How often to run the background job to check for local and remote media
|
||||||
# that should be purged according to the configured media retention settings.
|
# that should be purged according to the configured media retention settings.
|
||||||
MEDIA_RETENTION_CHECK_PERIOD_MS = 60 * 60 * 1000 # 1 hour
|
MEDIA_RETENTION_CHECK_PERIOD = Duration(hours=1)
|
||||||
|
|
||||||
|
|
||||||
class MediaRepository:
|
class MediaRepository:
|
||||||
@@ -166,7 +167,7 @@ class MediaRepository:
|
|||||||
# with the duration between runs dictated by the homeserver config.
|
# with the duration between runs dictated by the homeserver config.
|
||||||
self.clock.looping_call(
|
self.clock.looping_call(
|
||||||
self._start_apply_media_retention_rules,
|
self._start_apply_media_retention_rules,
|
||||||
MEDIA_RETENTION_CHECK_PERIOD_MS,
|
MEDIA_RETENTION_CHECK_PERIOD,
|
||||||
)
|
)
|
||||||
|
|
||||||
if hs.config.media.url_preview_enabled:
|
if hs.config.media.url_preview_enabled:
|
||||||
@@ -485,7 +486,7 @@ class MediaRepository:
|
|||||||
if now >= wait_until:
|
if now >= wait_until:
|
||||||
break
|
break
|
||||||
|
|
||||||
await self.clock.sleep(0.5)
|
await self.clock.sleep(Duration(milliseconds=500))
|
||||||
|
|
||||||
logger.info("Media %s has not yet been uploaded", media_id)
|
logger.info("Media %s has not yet been uploaded", media_id)
|
||||||
self.respond_not_yet_uploaded(request)
|
self.respond_not_yet_uploaded(request)
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ from synapse.logging.context import defer_to_thread, run_in_background
|
|||||||
from synapse.logging.opentracing import start_active_span, trace, trace_with_opname
|
from synapse.logging.opentracing import start_active_span, trace, trace_with_opname
|
||||||
from synapse.media._base import ThreadedFileSender
|
from synapse.media._base import ThreadedFileSender
|
||||||
from synapse.util.clock import Clock
|
from synapse.util.clock import Clock
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.file_consumer import BackgroundFileConsumer
|
from synapse.util.file_consumer import BackgroundFileConsumer
|
||||||
|
|
||||||
from ..types import JsonDict
|
from ..types import JsonDict
|
||||||
@@ -457,7 +458,7 @@ class ReadableFileWrapper:
|
|||||||
callback(chunk)
|
callback(chunk)
|
||||||
|
|
||||||
# We yield to the reactor by sleeping for 0 seconds.
|
# We yield to the reactor by sleeping for 0 seconds.
|
||||||
await self.clock.sleep(0)
|
await self.clock.sleep(Duration(seconds=0))
|
||||||
|
|
||||||
|
|
||||||
@implementer(interfaces.IConsumer)
|
@implementer(interfaces.IConsumer)
|
||||||
@@ -652,7 +653,7 @@ class MultipartFileConsumer:
|
|||||||
self.paused = False
|
self.paused = False
|
||||||
while not self.paused:
|
while not self.paused:
|
||||||
producer.resumeProducing()
|
producer.resumeProducing()
|
||||||
await self.clock.sleep(0)
|
await self.clock.sleep(Duration(seconds=0))
|
||||||
|
|
||||||
|
|
||||||
class Header:
|
class Header:
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ from synapse.media.preview_html import decode_body, parse_html_to_open_graph
|
|||||||
from synapse.types import JsonDict, UserID
|
from synapse.types import JsonDict, UserID
|
||||||
from synapse.util.async_helpers import ObservableDeferred
|
from synapse.util.async_helpers import ObservableDeferred
|
||||||
from synapse.util.caches.expiringcache import ExpiringCache
|
from synapse.util.caches.expiringcache import ExpiringCache
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.json import json_encoder
|
from synapse.util.json import json_encoder
|
||||||
from synapse.util.stringutils import random_string
|
from synapse.util.stringutils import random_string
|
||||||
|
|
||||||
@@ -208,7 +209,9 @@ class UrlPreviewer:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self._worker_run_media_background_jobs:
|
if self._worker_run_media_background_jobs:
|
||||||
self.clock.looping_call(self._start_expire_url_cache_data, 10 * 1000)
|
self.clock.looping_call(
|
||||||
|
self._start_expire_url_cache_data, Duration(seconds=10)
|
||||||
|
)
|
||||||
|
|
||||||
async def preview(self, url: str, user: UserID, ts: int) -> bytes:
|
async def preview(self, url: str, user: UserID, ts: int) -> bytes:
|
||||||
# the in-memory cache:
|
# the in-memory cache:
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from typing import TYPE_CHECKING
|
|||||||
import attr
|
import attr
|
||||||
|
|
||||||
from synapse.metrics import SERVER_NAME_LABEL
|
from synapse.metrics import SERVER_NAME_LABEL
|
||||||
|
from synapse.util.duration import Duration
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
@@ -70,7 +71,7 @@ class CommonUsageMetricsManager:
|
|||||||
)
|
)
|
||||||
self._clock.looping_call(
|
self._clock.looping_call(
|
||||||
self._hs.run_as_background_process,
|
self._hs.run_as_background_process,
|
||||||
5 * 60 * 1000,
|
Duration(minutes=5),
|
||||||
desc="common_usage_metrics_update_gauges",
|
desc="common_usage_metrics_update_gauges",
|
||||||
func=self._update_gauges,
|
func=self._update_gauges,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ from synapse.types.state import StateFilter
|
|||||||
from synapse.util.async_helpers import maybe_awaitable
|
from synapse.util.async_helpers import maybe_awaitable
|
||||||
from synapse.util.caches.descriptors import CachedFunction, cached as _cached
|
from synapse.util.caches.descriptors import CachedFunction, cached as _cached
|
||||||
from synapse.util.clock import Clock
|
from synapse.util.clock import Clock
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.frozenutils import freeze
|
from synapse.util.frozenutils import freeze
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -1389,7 +1390,7 @@ class ModuleApi:
|
|||||||
if self._hs.config.worker.run_background_tasks or run_on_all_instances:
|
if self._hs.config.worker.run_background_tasks or run_on_all_instances:
|
||||||
self._clock.looping_call(
|
self._clock.looping_call(
|
||||||
self._hs.run_as_background_process,
|
self._hs.run_as_background_process,
|
||||||
msec,
|
Duration(milliseconds=msec),
|
||||||
desc,
|
desc,
|
||||||
lambda: maybe_awaitable(f(*args, **kwargs)),
|
lambda: maybe_awaitable(f(*args, **kwargs)),
|
||||||
)
|
)
|
||||||
@@ -1444,8 +1445,7 @@ class ModuleApi:
|
|||||||
desc = f.__name__
|
desc = f.__name__
|
||||||
|
|
||||||
return self._clock.call_later(
|
return self._clock.call_later(
|
||||||
# convert ms to seconds as needed by call_later.
|
Duration(milliseconds=msec),
|
||||||
msec * 0.001,
|
|
||||||
self._hs.run_as_background_process,
|
self._hs.run_as_background_process,
|
||||||
desc,
|
desc,
|
||||||
lambda: maybe_awaitable(f(*args, **kwargs)),
|
lambda: maybe_awaitable(f(*args, **kwargs)),
|
||||||
@@ -1457,7 +1457,7 @@ class ModuleApi:
|
|||||||
Added in Synapse v1.49.0.
|
Added in Synapse v1.49.0.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await self._clock.sleep(seconds)
|
await self._clock.sleep(Duration(seconds=seconds))
|
||||||
|
|
||||||
async def send_http_push_notification(
|
async def send_http_push_notification(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ from synapse.types import (
|
|||||||
from synapse.util.async_helpers import (
|
from synapse.util.async_helpers import (
|
||||||
timeout_deferred,
|
timeout_deferred,
|
||||||
)
|
)
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.stringutils import shortstr
|
from synapse.util.stringutils import shortstr
|
||||||
from synapse.visibility import filter_events_for_client
|
from synapse.visibility import filter_events_for_client
|
||||||
|
|
||||||
@@ -235,7 +236,7 @@ class Notifier:
|
|||||||
Primarily used from the /events stream.
|
Primarily used from the /events stream.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
UNUSED_STREAM_EXPIRY_MS = 10 * 60 * 1000
|
UNUSED_STREAM_EXPIRY = Duration(minutes=10)
|
||||||
|
|
||||||
def __init__(self, hs: "HomeServer"):
|
def __init__(self, hs: "HomeServer"):
|
||||||
self.user_to_user_stream: dict[str, _NotifierUserStream] = {}
|
self.user_to_user_stream: dict[str, _NotifierUserStream] = {}
|
||||||
@@ -269,9 +270,7 @@ class Notifier:
|
|||||||
|
|
||||||
self.state_handler = hs.get_state_handler()
|
self.state_handler = hs.get_state_handler()
|
||||||
|
|
||||||
self.clock.looping_call(
|
self.clock.looping_call(self.remove_expired_streams, self.UNUSED_STREAM_EXPIRY)
|
||||||
self.remove_expired_streams, self.UNUSED_STREAM_EXPIRY_MS
|
|
||||||
)
|
|
||||||
|
|
||||||
# This is not a very cheap test to perform, but it's only executed
|
# This is not a very cheap test to perform, but it's only executed
|
||||||
# when rendering the metrics page, which is likely once per minute at
|
# when rendering the metrics page, which is likely once per minute at
|
||||||
@@ -861,7 +860,7 @@ class Notifier:
|
|||||||
logged = True
|
logged = True
|
||||||
|
|
||||||
# TODO: be better
|
# TODO: be better
|
||||||
await self.clock.sleep(0.5)
|
await self.clock.sleep(Duration(milliseconds=500))
|
||||||
|
|
||||||
async def _get_room_ids(
|
async def _get_room_ids(
|
||||||
self, user: UserID, explicit_room_id: str | None
|
self, user: UserID, explicit_room_id: str | None
|
||||||
@@ -889,7 +888,7 @@ class Notifier:
|
|||||||
def remove_expired_streams(self) -> None:
|
def remove_expired_streams(self) -> None:
|
||||||
time_now_ms = self.clock.time_msec()
|
time_now_ms = self.clock.time_msec()
|
||||||
expired_streams = []
|
expired_streams = []
|
||||||
expire_before_ts = time_now_ms - self.UNUSED_STREAM_EXPIRY_MS
|
expire_before_ts = time_now_ms - self.UNUSED_STREAM_EXPIRY.as_millis()
|
||||||
for stream in self.user_to_user_stream.values():
|
for stream in self.user_to_user_stream.values():
|
||||||
if stream.count_listeners():
|
if stream.count_listeners():
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from synapse.push import Pusher, PusherConfig, PusherConfigException, ThrottlePa
|
|||||||
from synapse.push.mailer import Mailer
|
from synapse.push.mailer import Mailer
|
||||||
from synapse.push.push_types import EmailReason
|
from synapse.push.push_types import EmailReason
|
||||||
from synapse.storage.databases.main.event_push_actions import EmailPushAction
|
from synapse.storage.databases.main.event_push_actions import EmailPushAction
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.threepids import validate_email
|
from synapse.util.threepids import validate_email
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -229,7 +230,7 @@ class EmailPusher(Pusher):
|
|||||||
if soonest_due_at is not None:
|
if soonest_due_at is not None:
|
||||||
delay = self.seconds_until(soonest_due_at)
|
delay = self.seconds_until(soonest_due_at)
|
||||||
self.timed_call = self.hs.get_clock().call_later(
|
self.timed_call = self.hs.get_clock().call_later(
|
||||||
delay,
|
Duration(seconds=delay),
|
||||||
self.on_timer,
|
self.on_timer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ from . import push_tools
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
from synapse.util.duration import Duration
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -336,7 +337,7 @@ class HttpPusher(Pusher):
|
|||||||
else:
|
else:
|
||||||
logger.info("Push failed: delaying for %ds", self.backoff_delay)
|
logger.info("Push failed: delaying for %ds", self.backoff_delay)
|
||||||
self.timed_call = self.hs.get_clock().call_later(
|
self.timed_call = self.hs.get_clock().call_later(
|
||||||
self.backoff_delay,
|
Duration(seconds=self.backoff_delay),
|
||||||
self.on_timer,
|
self.on_timer,
|
||||||
)
|
)
|
||||||
self.backoff_delay = min(
|
self.backoff_delay = min(
|
||||||
@@ -371,7 +372,7 @@ class HttpPusher(Pusher):
|
|||||||
delay_ms = random.randint(1, self.push_jitter_delay_ms)
|
delay_ms = random.randint(1, self.push_jitter_delay_ms)
|
||||||
diff_ms = event.origin_server_ts + delay_ms - self.clock.time_msec()
|
diff_ms = event.origin_server_ts + delay_ms - self.clock.time_msec()
|
||||||
if diff_ms > 0:
|
if diff_ms > 0:
|
||||||
await self.clock.sleep(diff_ms / 1000)
|
await self.clock.sleep(Duration(milliseconds=diff_ms))
|
||||||
|
|
||||||
rejected = await self.dispatch_push_event(event, tweaks, badge)
|
rejected = await self.dispatch_push_event(event, tweaks, badge)
|
||||||
if rejected is False:
|
if rejected is False:
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ from synapse.metrics import SERVER_NAME_LABEL
|
|||||||
from synapse.types import JsonDict
|
from synapse.types import JsonDict
|
||||||
from synapse.util.caches.response_cache import ResponseCache
|
from synapse.util.caches.response_cache import ResponseCache
|
||||||
from synapse.util.cancellation import is_function_cancellable
|
from synapse.util.cancellation import is_function_cancellable
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.stringutils import random_string
|
from synapse.util.stringutils import random_string
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -317,7 +318,7 @@ class ReplicationEndpoint(metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
# If we timed out we probably don't need to worry about backing
|
# If we timed out we probably don't need to worry about backing
|
||||||
# off too much, but lets just wait a little anyway.
|
# off too much, but lets just wait a little anyway.
|
||||||
await clock.sleep(1)
|
await clock.sleep(Duration(seconds=1))
|
||||||
except (ConnectError, DNSLookupError) as e:
|
except (ConnectError, DNSLookupError) as e:
|
||||||
if not cls.RETRY_ON_CONNECT_ERROR:
|
if not cls.RETRY_ON_CONNECT_ERROR:
|
||||||
raise
|
raise
|
||||||
@@ -332,7 +333,7 @@ class ReplicationEndpoint(metaclass=abc.ABCMeta):
|
|||||||
e,
|
e,
|
||||||
)
|
)
|
||||||
|
|
||||||
await clock.sleep(delay)
|
await clock.sleep(Duration(seconds=delay))
|
||||||
attempts += 1
|
attempts += 1
|
||||||
except HttpResponseException as e:
|
except HttpResponseException as e:
|
||||||
# We convert to SynapseError as we know that it was a SynapseError
|
# We convert to SynapseError as we know that it was a SynapseError
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ from synapse.replication.tcp.streams.partial_state import (
|
|||||||
)
|
)
|
||||||
from synapse.types import PersistedEventPosition, ReadReceipt, StreamKeyType, UserID
|
from synapse.types import PersistedEventPosition, ReadReceipt, StreamKeyType, UserID
|
||||||
from synapse.util.async_helpers import Linearizer, timeout_deferred
|
from synapse.util.async_helpers import Linearizer, timeout_deferred
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.iterutils import batch_iter
|
from synapse.util.iterutils import batch_iter
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
|
|
||||||
@@ -173,7 +174,7 @@ class ReplicationDataHandler:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Yield to reactor so that we don't block.
|
# Yield to reactor so that we don't block.
|
||||||
await self._clock.sleep(0)
|
await self._clock.sleep(Duration(seconds=0))
|
||||||
elif stream_name == PushersStream.NAME:
|
elif stream_name == PushersStream.NAME:
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if row.deleted:
|
if row.deleted:
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ from synapse.replication.tcp.commands import (
|
|||||||
parse_command_from_line,
|
parse_command_from_line,
|
||||||
)
|
)
|
||||||
from synapse.util.clock import Clock
|
from synapse.util.clock import Clock
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.stringutils import random_string
|
from synapse.util.stringutils import random_string
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -193,7 +194,9 @@ class BaseReplicationStreamProtocol(LineOnlyReceiver):
|
|||||||
self._send_pending_commands()
|
self._send_pending_commands()
|
||||||
|
|
||||||
# Starts sending pings
|
# Starts sending pings
|
||||||
self._send_ping_loop = self.clock.looping_call(self.send_ping, 5000)
|
self._send_ping_loop = self.clock.looping_call(
|
||||||
|
self.send_ping, Duration(seconds=5)
|
||||||
|
)
|
||||||
|
|
||||||
# Always send the initial PING so that the other side knows that they
|
# Always send the initial PING so that the other side knows that they
|
||||||
# can time us out.
|
# can time us out.
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ from synapse.replication.tcp.protocol import (
|
|||||||
tcp_inbound_commands_counter,
|
tcp_inbound_commands_counter,
|
||||||
tcp_outbound_commands_counter,
|
tcp_outbound_commands_counter,
|
||||||
)
|
)
|
||||||
|
from synapse.util.duration import Duration
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.replication.tcp.handler import ReplicationCommandHandler
|
from synapse.replication.tcp.handler import ReplicationCommandHandler
|
||||||
@@ -317,7 +318,7 @@ class SynapseRedisFactory(RedisFactory):
|
|||||||
self.hs = hs # nb must be called this for @wrap_as_background_process
|
self.hs = hs # nb must be called this for @wrap_as_background_process
|
||||||
self.server_name = hs.hostname
|
self.server_name = hs.hostname
|
||||||
|
|
||||||
hs.get_clock().looping_call(self._send_ping, 30 * 1000)
|
hs.get_clock().looping_call(self._send_ping, Duration(seconds=30))
|
||||||
|
|
||||||
@wrap_as_background_process("redis_ping")
|
@wrap_as_background_process("redis_ping")
|
||||||
async def _send_ping(self) -> None:
|
async def _send_ping(self) -> None:
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ from synapse.replication.tcp.commands import PositionCommand
|
|||||||
from synapse.replication.tcp.protocol import ServerReplicationStreamProtocol
|
from synapse.replication.tcp.protocol import ServerReplicationStreamProtocol
|
||||||
from synapse.replication.tcp.streams import EventsStream
|
from synapse.replication.tcp.streams import EventsStream
|
||||||
from synapse.replication.tcp.streams._base import CachesStream, StreamRow, Token
|
from synapse.replication.tcp.streams._base import CachesStream, StreamRow, Token
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.metrics import Measure
|
from synapse.util.metrics import Measure
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -116,7 +117,7 @@ class ReplicationStreamer:
|
|||||||
#
|
#
|
||||||
# Note that if the position hasn't advanced then we won't send anything.
|
# Note that if the position hasn't advanced then we won't send anything.
|
||||||
if any(EventsStream.NAME == s.NAME for s in self.streams):
|
if any(EventsStream.NAME == s.NAME for s in self.streams):
|
||||||
self.clock.looping_call(self.on_notifier_poke, 1000)
|
self.clock.looping_call(self.on_notifier_poke, Duration(seconds=1))
|
||||||
|
|
||||||
def on_notifier_poke(self) -> None:
|
def on_notifier_poke(self) -> None:
|
||||||
"""Checks if there is actually any new data and sends it to the
|
"""Checks if there is actually any new data and sends it to the
|
||||||
|
|||||||
@@ -28,9 +28,13 @@ from immutabledict import immutabledict
|
|||||||
from synapse.api.constants import Direction, EventTypes, JoinRules, Membership
|
from synapse.api.constants import Direction, EventTypes, JoinRules, Membership
|
||||||
from synapse.api.errors import AuthError, Codes, NotFoundError, SynapseError
|
from synapse.api.errors import AuthError, Codes, NotFoundError, SynapseError
|
||||||
from synapse.api.filtering import Filter
|
from synapse.api.filtering import Filter
|
||||||
|
from synapse.events.utils import (
|
||||||
|
SerializeEventConfig,
|
||||||
|
)
|
||||||
from synapse.handlers.pagination import (
|
from synapse.handlers.pagination import (
|
||||||
PURGE_ROOM_ACTION_NAME,
|
PURGE_ROOM_ACTION_NAME,
|
||||||
SHUTDOWN_AND_PURGE_ROOM_ACTION_NAME,
|
SHUTDOWN_AND_PURGE_ROOM_ACTION_NAME,
|
||||||
|
GetMessagesResult,
|
||||||
)
|
)
|
||||||
from synapse.http.servlet import (
|
from synapse.http.servlet import (
|
||||||
ResolveRoomIdMixin,
|
ResolveRoomIdMixin,
|
||||||
@@ -44,11 +48,13 @@ from synapse.http.servlet import (
|
|||||||
parse_string,
|
parse_string,
|
||||||
)
|
)
|
||||||
from synapse.http.site import SynapseRequest
|
from synapse.http.site import SynapseRequest
|
||||||
|
from synapse.logging.opentracing import trace
|
||||||
from synapse.rest.admin._base import (
|
from synapse.rest.admin._base import (
|
||||||
admin_patterns,
|
admin_patterns,
|
||||||
assert_requester_is_admin,
|
assert_requester_is_admin,
|
||||||
assert_user_is_admin,
|
assert_user_is_admin,
|
||||||
)
|
)
|
||||||
|
from synapse.rest.client.room import SerializeMessagesDeps, encode_messages_response
|
||||||
from synapse.storage.databases.main.room import RoomSortOrder
|
from synapse.storage.databases.main.room import RoomSortOrder
|
||||||
from synapse.streams.config import PaginationConfig
|
from synapse.streams.config import PaginationConfig
|
||||||
from synapse.types import JsonDict, RoomID, ScheduledTask, UserID, create_requester
|
from synapse.types import JsonDict, RoomID, ScheduledTask, UserID, create_requester
|
||||||
@@ -976,6 +982,7 @@ class RoomMessagesRestServlet(RestServlet):
|
|||||||
self._pagination_handler = hs.get_pagination_handler()
|
self._pagination_handler = hs.get_pagination_handler()
|
||||||
self._auth = hs.get_auth()
|
self._auth = hs.get_auth()
|
||||||
self._store = hs.get_datastores().main
|
self._store = hs.get_datastores().main
|
||||||
|
self._event_serializer = hs.get_event_client_serializer()
|
||||||
|
|
||||||
async def on_GET(
|
async def on_GET(
|
||||||
self, request: SynapseRequest, room_id: str
|
self, request: SynapseRequest, room_id: str
|
||||||
@@ -999,7 +1006,11 @@ class RoomMessagesRestServlet(RestServlet):
|
|||||||
):
|
):
|
||||||
as_client_event = False
|
as_client_event = False
|
||||||
|
|
||||||
msgs = await self._pagination_handler.get_messages(
|
serialize_options = SerializeEventConfig(
|
||||||
|
as_client_event=as_client_event, requester=requester
|
||||||
|
)
|
||||||
|
|
||||||
|
get_messages_result = await self._pagination_handler.get_messages(
|
||||||
room_id=room_id,
|
room_id=room_id,
|
||||||
requester=requester,
|
requester=requester,
|
||||||
pagin_config=pagination_config,
|
pagin_config=pagination_config,
|
||||||
@@ -1008,7 +1019,27 @@ class RoomMessagesRestServlet(RestServlet):
|
|||||||
use_admin_priviledge=True,
|
use_admin_priviledge=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
return HTTPStatus.OK, msgs
|
response_content = await self.encode_response(
|
||||||
|
get_messages_result, serialize_options
|
||||||
|
)
|
||||||
|
|
||||||
|
return HTTPStatus.OK, response_content
|
||||||
|
|
||||||
|
@trace
|
||||||
|
async def encode_response(
|
||||||
|
self,
|
||||||
|
get_messages_result: GetMessagesResult,
|
||||||
|
serialize_options: SerializeEventConfig,
|
||||||
|
) -> JsonDict:
|
||||||
|
return await encode_messages_response(
|
||||||
|
get_messages_result=get_messages_result,
|
||||||
|
serialize_options=serialize_options,
|
||||||
|
serialize_deps=SerializeMessagesDeps(
|
||||||
|
clock=self._clock,
|
||||||
|
event_serializer=self._event_serializer,
|
||||||
|
store=self._store,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RoomTimestampToEventRestServlet(RestServlet):
|
class RoomTimestampToEventRestServlet(RestServlet):
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ from synapse.types.rest.client import (
|
|||||||
EmailRequestTokenBody,
|
EmailRequestTokenBody,
|
||||||
MsisdnRequestTokenBody,
|
MsisdnRequestTokenBody,
|
||||||
)
|
)
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.msisdn import phone_number_to_msisdn
|
from synapse.util.msisdn import phone_number_to_msisdn
|
||||||
from synapse.util.stringutils import assert_valid_client_secret, random_string
|
from synapse.util.stringutils import assert_valid_client_secret, random_string
|
||||||
from synapse.util.threepids import check_3pid_allowed, validate_email
|
from synapse.util.threepids import check_3pid_allowed, validate_email
|
||||||
@@ -125,7 +126,9 @@ class EmailPasswordRequestTokenRestServlet(RestServlet):
|
|||||||
# comments for request_token_inhibit_3pid_errors.
|
# comments for request_token_inhibit_3pid_errors.
|
||||||
# Also wait for some random amount of time between 100ms and 1s to make it
|
# Also wait for some random amount of time between 100ms and 1s to make it
|
||||||
# look like we did something.
|
# look like we did something.
|
||||||
await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
|
await self.hs.get_clock().sleep(
|
||||||
|
Duration(milliseconds=random.randint(100, 1000))
|
||||||
|
)
|
||||||
return 200, {"sid": random_string(16)}
|
return 200, {"sid": random_string(16)}
|
||||||
|
|
||||||
raise SynapseError(400, "Email not found", Codes.THREEPID_NOT_FOUND)
|
raise SynapseError(400, "Email not found", Codes.THREEPID_NOT_FOUND)
|
||||||
@@ -383,7 +386,9 @@ class EmailThreepidRequestTokenRestServlet(RestServlet):
|
|||||||
# comments for request_token_inhibit_3pid_errors.
|
# comments for request_token_inhibit_3pid_errors.
|
||||||
# Also wait for some random amount of time between 100ms and 1s to make it
|
# Also wait for some random amount of time between 100ms and 1s to make it
|
||||||
# look like we did something.
|
# look like we did something.
|
||||||
await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
|
await self.hs.get_clock().sleep(
|
||||||
|
Duration(milliseconds=random.randint(100, 1000))
|
||||||
|
)
|
||||||
return 200, {"sid": random_string(16)}
|
return 200, {"sid": random_string(16)}
|
||||||
|
|
||||||
raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE)
|
raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE)
|
||||||
@@ -449,7 +454,9 @@ class MsisdnThreepidRequestTokenRestServlet(RestServlet):
|
|||||||
# comments for request_token_inhibit_3pid_errors.
|
# comments for request_token_inhibit_3pid_errors.
|
||||||
# Also wait for some random amount of time between 100ms and 1s to make it
|
# Also wait for some random amount of time between 100ms and 1s to make it
|
||||||
# look like we did something.
|
# look like we did something.
|
||||||
await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
|
await self.hs.get_clock().sleep(
|
||||||
|
Duration(milliseconds=random.randint(100, 1000))
|
||||||
|
)
|
||||||
return 200, {"sid": random_string(16)}
|
return 200, {"sid": random_string(16)}
|
||||||
|
|
||||||
logger.info("MSISDN %s is already in use by %s", msisdn, existing_user_id)
|
logger.info("MSISDN %s is already in use by %s", msisdn, existing_user_id)
|
||||||
|
|||||||
@@ -156,10 +156,10 @@ class DelayedEventsServlet(RestServlet):
|
|||||||
|
|
||||||
|
|
||||||
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
||||||
# The following can't currently be instantiated on workers.
|
# Most of the following can't currently be instantiated on workers.
|
||||||
if hs.config.worker.worker_app is None:
|
if hs.config.worker.worker_app is None:
|
||||||
UpdateDelayedEventServlet(hs).register(http_server)
|
UpdateDelayedEventServlet(hs).register(http_server)
|
||||||
CancelDelayedEventServlet(hs).register(http_server)
|
CancelDelayedEventServlet(hs).register(http_server)
|
||||||
RestartDelayedEventServlet(hs).register(http_server)
|
|
||||||
SendDelayedEventServlet(hs).register(http_server)
|
SendDelayedEventServlet(hs).register(http_server)
|
||||||
|
RestartDelayedEventServlet(hs).register(http_server)
|
||||||
DelayedEventsServlet(hs).register(http_server)
|
DelayedEventsServlet(hs).register(http_server)
|
||||||
|
|||||||
@@ -90,4 +90,5 @@ class UserMutualRoomsServlet(RestServlet):
|
|||||||
|
|
||||||
|
|
||||||
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
||||||
UserMutualRoomsServlet(hs).register(http_server)
|
if hs.config.experimental.msc2666_enabled:
|
||||||
|
UserMutualRoomsServlet(hs).register(http_server)
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ from synapse.http.site import SynapseRequest
|
|||||||
from synapse.metrics import SERVER_NAME_LABEL, threepid_send_requests
|
from synapse.metrics import SERVER_NAME_LABEL, threepid_send_requests
|
||||||
from synapse.push.mailer import Mailer
|
from synapse.push.mailer import Mailer
|
||||||
from synapse.types import JsonDict
|
from synapse.types import JsonDict
|
||||||
|
from synapse.util.duration import Duration
|
||||||
from synapse.util.msisdn import phone_number_to_msisdn
|
from synapse.util.msisdn import phone_number_to_msisdn
|
||||||
from synapse.util.ratelimitutils import FederationRateLimiter
|
from synapse.util.ratelimitutils import FederationRateLimiter
|
||||||
from synapse.util.stringutils import assert_valid_client_secret, random_string
|
from synapse.util.stringutils import assert_valid_client_secret, random_string
|
||||||
@@ -150,7 +151,9 @@ class EmailRegisterRequestTokenRestServlet(RestServlet):
|
|||||||
# Also wait for some random amount of time between 100ms and 1s to make it
|
# Also wait for some random amount of time between 100ms and 1s to make it
|
||||||
# look like we did something.
|
# look like we did something.
|
||||||
await self.already_in_use_mailer.send_already_in_use_mail(email)
|
await self.already_in_use_mailer.send_already_in_use_mail(email)
|
||||||
await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
|
await self.hs.get_clock().sleep(
|
||||||
|
Duration(milliseconds=random.randint(100, 1000))
|
||||||
|
)
|
||||||
return 200, {"sid": random_string(16)}
|
return 200, {"sid": random_string(16)}
|
||||||
|
|
||||||
raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE)
|
raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE)
|
||||||
@@ -219,7 +222,9 @@ class MsisdnRegisterRequestTokenRestServlet(RestServlet):
|
|||||||
# comments for request_token_inhibit_3pid_errors.
|
# comments for request_token_inhibit_3pid_errors.
|
||||||
# Also wait for some random amount of time between 100ms and 1s to make it
|
# Also wait for some random amount of time between 100ms and 1s to make it
|
||||||
# look like we did something.
|
# look like we did something.
|
||||||
await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
|
await self.hs.get_clock().sleep(
|
||||||
|
Duration(milliseconds=random.randint(100, 1000))
|
||||||
|
)
|
||||||
return 200, {"sid": random_string(16)}
|
return 200, {"sid": random_string(16)}
|
||||||
|
|
||||||
raise SynapseError(
|
raise SynapseError(
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user