[project]
name = "matrix-synapse"
version = "1.152.0"
description = "Homeserver for the Matrix decentralised comms protocol"
readme = "README.rst"
authors = [
    { name = "Matrix.org Team and Contributors", email = "packages@matrix.org" }
]
requires-python = ">=3.10.0,<4.0.0"
license = "AGPL-3.0-or-later OR LicenseRef-Element-Commercial"
classifiers = [
    "Development Status :: 5 - Production/Stable",
    "Topic :: Communications :: Chat",
]

# Mandatory Dependencies
dependencies = [
    # we use the TYPE_CHECKER.redefine method added in jsonschema 3.0.0
    "jsonschema>=3.0.0",
    # 0.25.0 is the first version to support Python 3.14.
    # We can remove this once https://github.com/python-jsonschema/jsonschema/issues/1426 is fixed
    # and included in a release.
    "rpds-py>=0.25.0",
    # We choose 2.0 as a lower bound: the most recent backwards incompatible release.
    # It seems generally available, judging by https://pkgs.org/search/?q=immutabledict
    "immutabledict>=2.0",
    # We require 2.1.0 or higher for type hints. Previous guard was >= 1.1.0
    "unpaddedbase64>=2.1.0",
    # We require 2.0.0 for immutabledict support.
    "canonicaljson>=2.0.0,<3.0.0",
    # we use the type definitions added in signedjson 1.1.
    # 1.1.0 erroneously removed decode_verify_key_base64 (reintroduced in 1.1.1).
    # 1.1.1 is mispackaged (importlib-metadata dependency without minimum version bound)
    # 1.1.2, 1.1.3 and 1.1.4 were all released on the same day, so no good reason to use the older version.
    "signedjson>=1.1.4,<2.0.0",
    # validating SSL certs for IP addresses requires service_identity 18.1.
    "service-identity>=18.1.0",
    # Twisted 18.9 introduces some logger improvements that the structured
    # logger utilises
    # Twisted 19.7.0 moves test helpers to a new module and deprecates the old location.
    # Twisted 21.2.0 introduces contextvar support.
    # We could likely bump this to 22.1 without making distro packagers'
    # lives hard (as of 2025-07, distro support is Ubuntu LTS: 22.1, Debian stable: 22.4,
    # RHEL 9: 22.10)
    "Twisted[tls]>=21.2.0",
    "treq>=21.5.0",
    # Twisted has required pyopenssl 16.0 since about Twisted 16.6.
    # pyOpenSSL 16.2.0 fixes compatibility with OpenSSL 1.1.0. 
    "pyOpenSSL>=16.2.0",
    "PyYAML>=5.3",
    "pyasn1>=0.1.9",
    "pyasn1-modules>=0.0.7",
    "bcrypt>=3.1.7",
    # 10.0.1 minimum is mandatory here because of libwebp CVE-2023-4863.
    # Packagers that already took care of libwebp can lower that down to 5.4.0.
    "Pillow>=10.0.1",
    # We use SortedDict.peekitem(), which was added in sortedcontainers 1.5.2.
    # 2.0.5 updates collections.abc imports to avoid Python 3.10 incompatibility.
    "sortedcontainers>=2.0.5",
    "pymacaroons>=0.13.0",
    "msgpack>=0.5.2",
    "phonenumbers>=8.2.0",
    # we use GaugeHistogramMetric, which was added in prom-client 0.4.0.
    # `prometheus_client.metrics` was added in 0.5.0, so we require that too.
    # We chose 0.6.0 as that is the current version in Debian Buster (oldstable).
    "prometheus-client>=0.6.0",
    # we use `order`, which arrived in attrs 19.2.0.
    # Note: 21.1.0 broke `/sync`, see https://github.com/matrix-org/synapse/issues/9936
    "attrs>=19.2.0,!=21.1.0",
    "netaddr>=0.7.18",
    # Jinja 2.x is incompatible with MarkupSafe>=2.1. To ensure that admins do not
    # end up with a broken installation, with recent MarkupSafe but old Jinja, we
    # add a lower bound to the Jinja2 dependency.
    "Jinja2>=3.0",
    # 3.2.0 updates collections.abc imports to avoid Python 3.10 incompatibility.
    "bleach>=3.2.0",
    # pydantic 2.12 depends on typing-extensions>=4.14.1
    "typing-extensions>=4.14.1",
    # We enforce that we have a `cryptography` version that bundles an `openssl`
    # with the latest security patches.
    "cryptography>=3.4.7",
    # ijson 3.1.4 fixes a bug with "." in property names
    "ijson>=3.1.4",
    "matrix-common>=1.3.0,<2.0.0",
    # We need packaging.verison.Version(...).major added in 20.0.
    "packaging>=20.0",
    "pydantic>=2.8;python_version < '3.14'",
    "pydantic>=2.12;python_version >= '3.14'",

    # This is for building the rust components during "poetry install", which
    # currently ignores the `build-system.requires` directive (c.f.
    # https://github.com/python-poetry/poetry/issues/6154). Both `pip install` and
    # `poetry build` do the right thing without this explicit dependency.
    #
    # This isn't really a dev-dependency, as `poetry install --without dev` will fail,
    # but the alternative is to add it to the main list of deps where it isn't
    # needed.
    "setuptools_rust>=1.3",

    # This is used for parsing multipart responses
    "python-multipart>=0.0.9",

    # Transitive dependency constraints
    # These dependencies aren't directly required by Synapse.
    # However, in order for Synapse to build, Synapse requires a higher minimum version
    # for these dependencies than the minimum specified by the direct dependency.
    # We should periodically check to see if these dependencies are still necessary and
    # remove any that are no longer required.
    "cffi>=1.15", # via cryptography
    "pynacl>=1.3", # via signedjson
    "pyparsing>=2.4",  # via packaging
    "pyrsistent>=0.18.0",  # via jsonschema
    "requests>=2.16.0", # 2.16.0+ no longer vendors urllib3, avoiding Python 3.10+ incompatibility
    "urllib3>=1.26.5", # via treq; 1.26.5 fixes Python 3.10+ collections.abc compatibility
    # 5.2 is the current version in Debian oldstable. If we don't care to support that, then 5.4 is
    # the minimum version from Ubuntu 22.04 and RHEL 9. (as of 2025-12)
    # When bumping this version to 6.2 or above, refer to https://github.com/element-hq/synapse/pull/19274
    # for details of Synapse improvements that may be unlocked. Particularly around the use of `|`
    # syntax with zope interface types.
    "zope-interface>=5.2", # via twisted
]

[project.optional-dependencies]
matrix-synapse-ldap3 = ["matrix-synapse-ldap3>=0.1"]
postgres = [
    "psycopg2>=2.8;platform_python_implementation != 'PyPy'",
    "psycopg2cffi>=2.8;platform_python_implementation == 'PyPy'",
    "psycopg2cffi-compat==1.1;platform_python_implementation == 'PyPy'",
]
saml2 = [
    "pysaml2>=4.5.0",

    # Transitive dependencies from pysaml2
    # These dependencies aren't directly required by Synapse.
    # However, in order for Synapse to build, Synapse requires a higher minimum version
    # for these dependencies than the minimum specified by the direct dependency.
    "defusedxml>=0.7.1", # via pysaml2
    "pytz>=2018.3",  # via pysaml2
]
oidc = ["authlib>=1.6.11"]
url-preview = ["lxml>=4.6.3"]
sentry = ["sentry-sdk>=0.7.2"]
opentracing = [
    "jaeger-client>=4.2.0",
    "opentracing>=2.2.0",

    # Transitive dependencies from jaeger-client
    # These dependencies aren't directly required by Synapse.
    # However, in order for Synapse to build, Synapse requires a higher minimum version
    # for these dependencies than the minimum specified by the direct dependency.
    "thrift>=0.10",  # via jaeger-client
    "tornado>=6.0",  # via jaeger-client
]
jwt = ["authlib"]
# hiredis is not a *strict* dependency, but it makes things much faster.
# (if it is not installed, we fall back to slow code.)
redis = ["txredisapi>=1.4.7", "hiredis>=0.3"]
# Required to use experimental `caches.track_memory_usage` config option.
cache-memory = ["pympler>=1.0"]
# If this is updated, don't forget to update the equivalent lines in
# `dependency-groups.dev` below.
test = ["parameterized>=0.9.0", "idna>=3.3"]

# The duplication here is awful.
#
# TODO: This can be resolved via PEP 735 dependency groups, which poetry supports
# since 2.2.0. However, switching to that would require updating the command
# developers use to install the `all` group. This would require some coordination.
#
# NB: the strings in this list must be *package* names, not extra names.
# Some of our extra names _are_ package names, which can lead to great confusion.
all = [
    # matrix-synapse-ldap3
    "matrix-synapse-ldap3>=0.1",
    # postgres
    "psycopg2>=2.8;platform_python_implementation != 'PyPy'",
    "psycopg2cffi>=2.8;platform_python_implementation == 'PyPy'",
    "psycopg2cffi-compat==1.1;platform_python_implementation == 'PyPy'",
    # saml2
    "pysaml2>=4.5.0",
    # oidc and jwt
    "authlib>=1.6.11",
    # url-preview
    "lxml>=4.6.3",
    # sentry
    "sentry-sdk>=0.7.2",
    # opentracing
    "jaeger-client>=4.2.0", "opentracing>=2.2.0",
    # redis
    "txredisapi>=1.4.7", "hiredis>=0.3",
    # cache-memory
    # 1.0 added support for python 3.10, our current minimum supported python version
    "pympler>=1.0",
    # omitted:
    #   - test: it's useful to have this separate from dev deps in the olddeps job

    # Transitive dependencies
    # These dependencies aren't directly required by Synapse.
    # However, in order for Synapse to build, Synapse requires a higher minimum version
    # for these dependencies than the minimum specified by the direct dependency.
    "defusedxml>=0.7.1", # via pysaml2
    "pytz>=2018.3",  # via pysaml2
    "thrift>=0.10",  # via jaeger-client
    "tornado>=6.0",  # via jaeger-client
]

[project.urls]
repository = "https://github.com/element-hq/synapse"
documentation = "https://element-hq.github.io/synapse/latest"
"Issue Tracker" = "https://github.com/element-hq/synapse/issues"

[project.scripts]
synapse_homeserver = "synapse.app.homeserver:main"
synapse_worker = "synapse.app.generic_worker:main"
synctl = "synapse._scripts.synctl:main"

export_signing_key = "synapse._scripts.export_signing_key:main"
generate_config = "synapse._scripts.generate_config:main"
generate_log_config = "synapse._scripts.generate_log_config:main"
generate_signing_key = "synapse._scripts.generate_signing_key:main"
hash_password = "synapse._scripts.hash_password:main"
register_new_matrix_user = "synapse._scripts.register_new_matrix_user:main"
synapse_port_db = "synapse._scripts.synapse_port_db:main"
synapse_review_recent_signups = "synapse._scripts.review_recent_signups:main"
update_synapse_database = "synapse._scripts.update_synapse_database:main"

[tool.poetry]
packages = [{ include = "synapse" }]

# We're using PEP 735 dependency groups, which requires Poetry 2.2.0+
requires-poetry = ">=2.2.0"

[tool.poetry.build]
# Compile our rust module when using `poetry install`. This is still required
# while using `poetry` as the build frontend. Saves the developer from needing
# to run both:
#
# $ poetry install
# $ maturin develop
script = "build_rust.py"
# Create a `setup.py` file which will call the `build` method in our build
# script.
#
# Our build script currently uses the "old" build method, where we define a
# `build` method and `setup.py` calls it. Poetry developers have mentioned that
# this will eventually be removed:
# https://github.com/matrix-org/synapse/pull/14949#issuecomment-1418001859
#
# The new build method is defined here:
# https://python-poetry.org/docs/building-extension-modules/#maturin-build-script
# but is still marked as "unstable" at the time of writing. This would also
# bump our minimum `poetry-core` version to 1.5.0.
#
# We can just drop this work-around entirely if migrating away from
# Poetry, thus there's little motivation to update the build script.
generate-setup-file = true

# Dependencies used for developing Synapse itself.
#
# We pin development dependencies in poetry.lock so that our tests don't start
# failing on new releases. Keeping lower bounds loose here means that dependabot
# can bump versions without having to update the content-hash in the lockfile.
# This helps prevents merge conflicts when running a batch of dependabot updates.
[dependency-groups]
dev = [
    "ruff==0.14.6",

    # Typechecking
    "lxml-stubs>=0.4.0",
    "mypy",
    "mypy-zope",
    "types-bleach>=4.1.0",
    "types-jsonschema>=3.2.0",
    "types-netaddr>=0.8.0.6",
    "types-opentracing>=2.4.2",
    "types-Pillow>=8.3.4",
    "types-psycopg2>=2.9.9",
    "types-pyOpenSSL>=20.0.7",
    "types-PyYAML>=5.4.10",
    "types-requests>=2.26.0",
    "types-setuptools>=57.4.0",

    # Dependencies which are exclusively required by unit test code. This is
    # NOT a list of all modules that are necessary to run the unit tests.
    # Tests assume that all optional dependencies are installed.
    #
    # If this is updated, don't forget to update the equivalent lines in
    # project.optional-dependencies.test.
    "parameterized>=0.9.0",
    "idna>=3.3",

    # The following are used by the release script
    "click>=8.1.3",
    # GitPython was == 3.1.14; bumped to 3.1.20, the first release with type hints.
    "GitPython>=3.1.20",
    "markdown-it-py>=3.0.0",
    "pygithub>=1.59",
    # The following are executed as commands by the release script.
    "twine",
    # Towncrier min version comes from https://github.com/matrix-org/synapse/pull/3425. Rationale unclear.
    "towncrier>=18.6.0rc1",

    # Used for checking the Poetry lockfile
    "tomli>=1.2.3",

    # Used for checking the schema delta files
    "sqlglot>=28.0.0",
]

[tool.towncrier]
    package = "synapse"
    filename = "CHANGES.md"
    directory = "changelog.d"
    issue_format = "[\\#{issue}](https://github.com/element-hq/synapse/issues/{issue})"

    [[tool.towncrier.type]]
        directory = "feature"
        name = "Features"
        showcontent = true

    [[tool.towncrier.type]]
        directory = "bugfix"
        name = "Bugfixes"
        showcontent = true

    [[tool.towncrier.type]]
        directory = "docker"
        name = "Updates to the Docker image"
        showcontent = true

    [[tool.towncrier.type]]
        directory = "doc"
        name = "Improved Documentation"
        showcontent = true

    [[tool.towncrier.type]]
        directory = "removal"
        name = "Deprecations and Removals"
        showcontent = true

    [[tool.towncrier.type]]
        directory = "misc"
        name = "Internal Changes"
        showcontent = true

[tool.ruff]
line-length = 88
target-version = "py310"

[tool.ruff.lint]
# See https://beta.ruff.rs/docs/rules/#error-e
# for error codes. The ones we ignore are:
#  E501: Line too long (black enforces this for us)
#  E731: do not assign a lambda expression, use a def
#
# flake8-bugbear compatible checks. Its error codes are described at
# https://beta.ruff.rs/docs/rules/#flake8-bugbear-b
#  B023: Functions defined inside a loop must not use variables redefined in the loop
ignore = [
    "B023",
    "E501",
    "E731",
]
select = [
    # pycodestyle
    "E",
    "W",
    # pyflakes
    "F",
    # isort
    "I001",
    # flake8-bugbear
    "B0",
    # flake8-comprehensions
    "C4",
    # flake8-2020
    "YTT",
    # flake8-slots
    "SLOT",
    # flake8-debugger
    "T10",
    # flake8-pie
    "PIE",
    # flake8-executable
    "EXE",
    # flake8-logging
    "LOG",
    # flake8-logging-format
    "G",
    # pyupgrade
    "UP006",
]
extend-safe-fixes = [
    # pyupgrade rules compatible with Python >= 3.9
    "UP006",
    # Allow ruff to automatically fix trailing spaces within a multi-line string/comment.
    "W293"
]

[tool.ruff.lint.isort]
combine-as-imports = true
section-order = ["future", "standard-library", "third-party", "twisted", "first-party", "testing", "local-folder"]
known-first-party = ["synapse"]

[tool.ruff.lint.isort.sections]
twisted = ["twisted", "OpenSSL"]
testing = ["tests"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"

[tool.ruff.lint.flake8-bugbear]
extend-immutable-calls = [
    # Durations are immutable
    "synapse.util.duration.Duration",
]

[tool.maturin]
manifest-path = "rust/Cargo.toml"
module-name = "synapse.synapse_rust"
python-source = "."
include = [
    { path = "AUTHORS.rst", format = "sdist" },
    { path = "book.toml", format = "sdist" },
    { path = "changelog.d/**/*", format = "sdist" },
    { path = "CHANGES.md", format = "sdist" },
    { path = "CONTRIBUTING.md", format = "sdist" },
    { path = "demo/**/*", format = "sdist" },
    { path = "docs/**/*", format = "sdist" },
    { path = "INSTALL.md", format = "sdist" },
    { path = "LICENSE-AGPL-3.0", format = "sdist" },
    { path = "LICENSE-COMMERCIAL", format = "sdist" },
    { path = "mypy.ini", format = "sdist" },
    { path = "scripts-dev/**/*", format = "sdist" },
    { path = "synmark/**/*", format = "sdist" },
    { path = "sytest-blacklist", format = "sdist" },
    { path = "tests/**/*", format = "sdist" },
    { path = "UPGRADE.rst", format = "sdist" },
    { path = "Cargo.toml", format = "sdist" },
    { path = "Cargo.lock", format = "sdist" },
    { path = "rust/Cargo.toml", format = "sdist" },
    { path = "rust/build.rs", format = "sdist" },
    { path = "rust/src/**", format = "sdist" },
]
exclude = [
    { path = "synapse/*.so", format = "sdist" },
]

[build-system]
# The upper bounds here are defensive, intended to prevent situations like
# https://github.com/matrix-org/synapse/issues/13849 and
# https://github.com/matrix-org/synapse/issues/14079 where we see buildtime or
# runtime errors caused by build system changes.
# We are happy to raise these upper bounds upon request,
# provided we check that it's safe to do so (i.e. that CI passes).
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"


[tool.cibuildwheel]
# Skip unsupported platforms (by us or by Rust).
#
# See https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip for the
# list of supported build targets.
#
# Also see `.github/workflows/release-artifacts.yml` for the list of
# architectures we build for (based on the runner OS types we use), as well as
# the platforms we exclude from testing in CI.
#
# We skip:
#  - free-threaded cpython builds: these are not currently supported.
#  - i686: We don't support 32-bit platforms.
#  - *macosx*: we don't support building wheels for MacOS.
skip = "cp3??t-* *i686* *macosx*"
# Enable non-default builds. See the list of available options:
# https://cibuildwheel.pypa.io/en/stable/options#enable
#
# "pypy" used to be included by default up until cibuildwheel 3.
enable = "pypy"

# We need a rust compiler.
before-all =  "sh .ci/before_build_wheel.sh"
environment= { PATH = "$PATH:$HOME/.cargo/bin" }

# For some reason if we don't manually clean the build directory we
# can end up polluting the next build with a .so that is for the wrong
# Python version.
before-build = "rm -rf {project}/build"
build-frontend = "build"
test-command = "python -c 'from synapse.synapse_rust import sum_as_string; print(sum_as_string(1, 2))'"
