[project] name = "zulip-server" version = "0.1.0" requires-python = ">=3.10" [dependency-groups] prod = [ # Django itself "django[argon2]==5.2.*", "asgiref", # needed for NotRequired, ParamSpec "typing-extensions", # Needed for rendering backend templates "jinja2", # Needed for Markdown processing "markdown", "pygments", "jsx-lexer", "uri-template", "regex", # Needed for manage.py "ipython", # Needed for image processing and thumbnailing "pyvips", # Needed for building complex DB queries "sqlalchemy==1.4.*", "greenlet", # Needed for S3 file uploads and other AWS tools "boto3", # The runtime-relevant part of boto3-stubs (see mypy.in) "mypy-boto3-s3", "mypy-boto3-ses", "mypy-boto3-sns", "mypy-boto3-sqs", # Needed for integrations "defusedxml", # Needed for LDAP support "python-ldap", "django-auth-ldap", # Django extension providing bitfield support "django-bitfield", # Needed for Android push notifications "firebase-admin", # Needed for the email mirror "html2text", "talon-core", # Needed for inlining the CSS in emails "css-inline", # Needed for JWT-based auth "pyjwt", # Needed to access RabbitMQ "pika", # Needed to access our database "psycopg2", # Needed for memcached usage "python-binary-memcached", # Needed for compression support in memcached via python-binary-memcached "django-bmemcached", # Needed for zerver/tests/test_timestamp.py "python-dateutil", # Needed for Redis "redis", # Tornado used for server->client push system "tornado", # Fast JSON parser "orjson", # Needed for iOS push notifications "aioapns", # To parse po files "polib", # Needed for cloning virtual environments "virtualenv-clone", # Needed for link preview "beautifulsoup4", "pyoembed", "python-magic", # The Zulip API bindings, from its own repository. "zulip", "zulip-bots", # Used for Hesiod lookups, etc. "dnspython", # Install Python Social Auth "social-auth-app-django", "social-auth-core[azuread,saml]", "python3-saml", # For encrypting a login token to the desktop app "cryptography", # Needed for messages' rendered content parsing in push notifications. "lxml", # Needed for 2-factor authentication "django-two-factor-auth[call,phonenumberslite,sms]", # Needed for processing payments (in corporate) "stripe", # For checking whether email of the user is from a disposable email provider. "disposable-email-domains", # Needed for parsing YAML with JSON references from the REST API spec files "jsonref", # Needed for string matching in AlertWordProcessor "pyahocorasick", # Needed for function decorators that don't break introspection. # Used for rate limiting authentication. "decorator", # For server-side enforcement of password strength "zxcvbn", # Needed for sending HTTP requests "requests[security]", "requests-oauthlib", # For OpenAPI schema validation. "openapi-core", "werkzeug<3.1.2", # https://github.com/python-openapi/openapi-core/issues/938 # For reporting errors to sentry.io "sentry-sdk", # For detecting URLs to link "tlds", # Unicode Collation Algorithm for sorting multilingual strings "pyuca", # Handle connection retries with exponential backoff "backoff", # Needed for reading bson files in rocketchat import tool "pymongo", # Non-backtracking regular expressions "google-re2", # For querying recursive group membership "django-cte", # SCIM integration "django-scim2", # Circuit-breaking for outgoing services "circuitbreaker", # Runtime monkeypatching of django-stubs generics "django-stubs-ext", # Structured data representation with parsing. "pydantic", "annotated-types", # For requesting LLM API endpoints. "litellm", # Used for running the Zulip production Django server "uwsgi", # Used for monitoring memcached "prometheus-client", # For captchas on unauth'd pages which can generate emails "altcha", # SMTP server for accepting incoming email "aiosmtpd>=1.4.6", # For using Missing sentinel "pydantic-partials", # For E2EE of push notifications "pynacl", # Character set detection for text/plain "chardet>=5.1.0" ] docs = [ # Needed to build RTD docs "sphinx", "sphinx-rtd-theme", "sphinx-design", # Needed to build Markdown docs "myst-parser", ] dev = [ { include-group = "prod" }, { include-group = "docs" }, # moto s3 mock "moto[s3]", # For tools/run-dev "aiohttp", # Needed for documentation links test "scrapy", # Needed to compute test coverage "coverage", # fake for LDAP testing "fakeldap", # For testing mock http requests "responses", # For doing highly usable Python profiling "line-profiler", # Python reformatter "black", # Python linter "ruff", # Needed for watching file changes "pyinotify", "pyasyncore", # https://github.com/seb-m/pyinotify/issues/204 # Needed to run tests in parallel "tblib", # For linting Git commit messages "gitlint-core", # Needed for visualising cProfile reports "snakeviz", # Needed for creating DigitalOcean droplets "python-digitalocean", # zulip's linting framework - zulint "zulint", # For type checking "mypy[faster-cache]", "boto3-stubs[s3,ses,sns,sqs]", "django-stubs", "google-re2-stubs", "lxml-stubs", "SQLAlchemy[mypy]", "types-beautifulsoup4", "types-boto", "types-decorator", "types-defusedxml", "types-jsonschema", "types-Markdown", "types-oauthlib", "types-polib", "types-pika", "types-psycopg2", "types-Pygments", "types-pyOpenSSL", "types-python-dateutil", "types-PyYAML", "types-redis", "types-regex", "types-requests", "types-requests-oauthlib", "types-uwsgi", "types-zxcvbn", # Needed for tools/check-thirdparty "python-debian", # Pattern-based lint tool "semgrep<1.80.0", # https://github.com/semgrep/semgrep/issues/10408 # For sorting versions when uploading releases "natsort", # For spell check linter "codespell", # For mocking time "time-machine", ] [tool.uv] no-binary-package = ["lxml", "xmlsec"] [tool.uv.sources] # https://github.com/typeddjango/django-stubs/pull/2738 django-stubs = { url = "https://github.com/typeddjango/django-stubs/archive/9b2a9e83ee31dfd3de34a361e6098926014b599b.zip" } django-stubs-ext = { url = "https://github.com/typeddjango/django-stubs/archive/9b2a9e83ee31dfd3de34a361e6098926014b599b.zip", subdirectory = "ext" } # Forked to avoid pulling in scipy: https://github.com/mailgun/talon/pull/200 # and chardet, cchardet: https://github.com/mailgun/talon/pull/239 # and fix invalid escape sequences: https://github.com/mailgun/talon/pull/245 talon-core = { url = "https://github.com/zulip/talon/archive/e87a64dccc3c5ee1b8ea157d4b6e15ecd46f2bed.zip", subdirectory = "talon-core" } # We integrate with these tightly, including fetching content not included in # the official PyPI release tarballs, such as logos, assets and documentation # files that we render on our /integrations/ page. Therefore, we need to pin the # version from Git rather than a PyPI release. Keeping everything in one # repository simplifies the process of implementing and documenting new bots for # new contributors. zulip = { url = "https://github.com/zulip/python-zulip-api/archive/9e131ac626976b9c3da6c11b6365b4939656f7c3.zip", subdirectory = "zulip" } zulip-bots = { url = "https://github.com/zulip/python-zulip-api/archive/0.9.0.zip", subdirectory = "zulip_bots" } # zulip's linting framework - zulint zulint = { url = "https://github.com/zulip/zulint/archive/9be0a32bf75a9d8738b005f0b880567fff64e943.zip" } [tool.black] line-length = 100 target-version = ["py310"] [tool.isort] src_paths = [".", "tools"] known_third_party = "zulip" profile = "black" line_length = 100 [tool.mypy] # Logistics of what code to check and how to handle the data. scripts_are_modules = true show_traceback = true # See https://zulip.readthedocs.io/en/latest/testing/mypy.html#mypy-stubs-for-third-party-modules # for notes on how we manage mypy stubs. mypy_path = "$MYPY_CONFIG_FILE_DIR/stubs" cache_dir = "$MYPY_CONFIG_FILE_DIR/var/mypy-cache" # Enable strict mode, with some exceptions. strict = true disallow_subclassing_any = false disallow_untyped_calls = false disallow_untyped_decorators = false warn_return_any = false # Enable optional errors. enable_error_code = [ "redundant-self", "redundant-expr", "truthy-bool", "truthy-iterable", "ignore-without-code", "unused-awaitable", "explicit-override", ] # Display the codes needed for # type: ignore[code] annotations. show_error_codes = true # Warn of unreachable or redundant code. warn_unreachable = true # dmypy enables local_partial_types implicitly. We need mypy to align # with this behavior. local_partial_types = true plugins = [ "mypy_django_plugin.main", "pydantic.mypy", ] [[tool.mypy.overrides]] module = [ "ahocorasick.*", "bitfield.*", "bmemcached.*", "circuitbreaker.*", "digitalocean.*", "django_auth_ldap.*", "django_bmemcached.*", "django_cte.*", "django_otp.*", "django_scim.*", "fakeldap.*", "firebase_admin.*", "gitlint.*", "integrations.*", "jsonref.*", "ldap.*", # https://github.com/python-ldap/python-ldap/issues/368 "onelogin.*", "pyinotify.*", "pyoembed.*", "pyuca.*", "pyvips.*", "scim2_filter_parser.attr_paths", "social_django.*", "talon_core.*", "tlds.*", "two_factor.*", ] ignore_missing_imports = true [tool.django-stubs] django_settings_module = "zproject.settings" [tool.pydantic-mypy] # See https://docs.pydantic.dev/latest/integrations/mypy/#mypy-plugin-capabilities for the effects of these options. init_forbid_extra = true init_typed = true warn_required_dynamic_aliases = true [tool.ruff] line-length = 100 src = [".", "tools"] target-version = "py310" [tool.ruff.lint] # See https://github.com/astral-sh/ruff#rules for error code definitions. select = [ "ANN", # annotations "B", # bugbear "C4", # comprehensions "COM", # trailing comma "DJ", # Django "DTZ", # naive datetime "E", # style errors "EXE", # shebang "F", # flakes "FLY", # string formatting "FURB", # refurbishing "G", # logging format "I", # import sorting "INT", # gettext "ISC", # string concatenation "LOG", # logging "N", # naming "PERF", # performance "PGH", # pygrep-hooks "PIE", # miscellaneous "PL", # pylint "PYI", # typing stubs "Q", # quotes "RSE", # raise "RUF", # Ruff "S", # security "SLOT", # __slots__ "SIM", # simplify "T10", # debugger "TC", # type-checking "TID", # tidy imports "UP", # upgrade "W", # style warnings "YTT", # sys.version ] ignore = [ "ANN401", # Dynamically typed expressions (typing.Any) are disallowed "B007", # Loop control variable not used within the loop body "B904", # Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling "C408", # Unnecessary `dict` call (rewrite as a literal) "COM812", # Trailing comma missing "DJ001", # Avoid using `null=True` on string-based fields "DJ008", # Model does not define `__str__` method "E402", # Module level import not at top of file "E501", # Line too long "E731", # Do not assign a lambda expression, use a def "LOG015", # `error()` call on root logger "N802", # Function name should be lowercase "N806", # Variable in function should be lowercase "PERF203", # `try`-`except` within a loop incurs performance overhead "PLC0414", # Import alias does not rename original package "PLC0415", # `import` should be at the top-level of a file "PLC1901", # `s == ""` can be simplified to `not s` as an empty string is falsey "PLR0911", # Too many return statements "PLR0912", # Too many branches "PLR0913", # Too many arguments to function call "PLR0915", # Too many statements "PLR2004", # Magic value used in comparison "PLR5501", # Consider using `elif` instead of `else` then `if` to remove one indentation level "PLW0603", # Using the global statement is discouraged "PLW2901", # Outer for loop variable overwritten by inner for loop target "RUF001", # String contains ambiguous unicode character "RUF002", # Docstring contains ambiguous unicode character "RUF003", # Comment contains ambiguous unicode character "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` "S101", # Use of `assert` detected "S105", # Possible hardcoded password "S106", # Possible hardcoded password "S107", # Possible hardcoded password "S110", # `try`-`except`-`pass` detected, consider logging the exception "S113", # Probable use of requests call without timeout "S310", # Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes "S324", # Probable use of insecure hash functions in `hashlib` "S603", # `subprocess` call: check for execution of untrusted input "S606", # Starting a process without a shell "S607", # Starting a process with a partial executable path "SIM103", # Return the condition directly "SIM108", # Use ternary operator `action = "[commented]" if action == "created" else f"{action} a [comment]"` instead of if-else-block "SIM114", # Combine `if` branches using logical `or` operator "SIM401", # Use `d.get(key, default)` instead of an `if` block "TC001", # Move application import into a type-checking block "TC002", # Move third-party import into a type-checking block "TC003", # Move standard library import into a type-checking block "TC006", # Add quotes to type expression in `typing.cast()` ] [tool.ruff.lint.flake8-bandit] allowed-markup-calls = ["bs4.BeautifulSoup.decode", "lxml.html.tostring"] [tool.ruff.lint.flake8-gettext] extend-function-names = ["gettext_lazy"] [tool.ruff.lint.isort] known-third-party = ["zulip"] split-on-trailing-comma = false