mirror of
https://github.com/onyx-dot-app/onyx.git
synced 2026-10-19 01:50:51 -05:00
build: upgrade Python from 3.11 to 3.13 (#11721)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
22266e06d5
commit
8ffafb0c8a
32 files changed
+639
-4607
No files matched your search
@@ -118,12 +118,13 @@ RUN apt-get update \
|
||||
uvx --quiet --from playwright playwright install-deps chromium \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python 3.11 via uv into a stable, image-level path so venvs
|
||||
# Install Python 3.13 via uv into a stable, image-level path so venvs
|
||||
# created in bind-mounted workspaces survive across `docker run`s.
|
||||
# Ubuntu 26.04's system Python is 3.14, but onnxruntime only ships
|
||||
# cp311–cp313 wheels; the repo pins 3.11 via .python-version.
|
||||
# cp311–cp313 wheels, so the repo pins 3.13 (the top of that range)
|
||||
# via .python-version.
|
||||
ENV UV_PYTHON_INSTALL_DIR=/opt/uv/python
|
||||
RUN uv python install 3.11
|
||||
RUN uv python install 3.13
|
||||
|
||||
# Create non-root dev user with passwordless sudo
|
||||
RUN useradd -m -s /bin/zsh dev && \
|
||||
|
||||
@@ -43,7 +43,7 @@ runs:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # ratchet:actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Create virtual environment
|
||||
shell: bash
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # ratchet:actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.11"
|
||||
python-version: "3.13"
|
||||
- name: Setup Terraform
|
||||
uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # ratchet:hashicorp/setup-terraform@v4.0.0
|
||||
- name: Setup bun
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
3.11
|
||||
3.13
|
||||
@@ -135,7 +135,7 @@ NOTE: Always make sure everything is strictly typed (both in Python and Typescri
|
||||
|
||||
### Technology Stack
|
||||
|
||||
- **Backend**: Python 3.11, FastAPI, SQLAlchemy, Alembic, Celery
|
||||
- **Backend**: Python 3.13, FastAPI, SQLAlchemy, Alembic, Celery
|
||||
- **Frontend**: Next.js 15+, React 18, TypeScript, Tailwind CSS
|
||||
- **Database**: PostgreSQL with Redis caching
|
||||
- **Search**: Vespa vector database
|
||||
|
||||
+6
-6
@@ -91,7 +91,7 @@ Onyx being a fully functional app, relies on some external software, specificall
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Python 3.11** — If using a lower version, modifications will have to be made to the code. Higher versions may have library compatibility issues.
|
||||
- **Python 3.13** — Other versions may require code or dependency changes; 3.14 is not yet supported (some dependencies, e.g. `onnxruntime` and CUDA `torch`, do not yet ship 3.14 wheels).
|
||||
- **Docker** — Required for running external services (Postgres, OpenSearch, Redis, MinIO).
|
||||
- **Bun** — We use [bun](https://bun.sh) as the JavaScript package manager. Install it from https://bun.sh/docs/installation.
|
||||
|
||||
@@ -100,7 +100,7 @@ Onyx being a fully functional app, relies on some external software, specificall
|
||||
We use [uv](https://docs.astral.sh/uv/) and recommend creating a [virtual environment](https://docs.astral.sh/uv/pip/environments/#using-a-virtual-environment).
|
||||
|
||||
```bash
|
||||
uv venv .venv --python 3.11
|
||||
uv venv .venv --python 3.13
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
@@ -312,16 +312,16 @@ docker compose up -d --build
|
||||
|
||||
### Setting up Python
|
||||
|
||||
Ensure [Homebrew](https://brew.sh/) is already set up, then install Python 3.11:
|
||||
Ensure [Homebrew](https://brew.sh/) is already set up, then install Python 3.13:
|
||||
|
||||
```bash
|
||||
brew install python@3.11
|
||||
brew install python@3.13
|
||||
```
|
||||
|
||||
Add Python 3.11 to your path by adding the following line to `~/.zshrc`:
|
||||
Add Python 3.13 to your path by adding the following line to `~/.zshrc`:
|
||||
|
||||
```
|
||||
export PATH="$(brew --prefix)/opt/python@3.11/libexec/bin:$PATH"
|
||||
export PATH="$(brew --prefix)/opt/python@3.13/libexec/bin:$PATH"
|
||||
```
|
||||
|
||||
> **Note:** You will need to open a new terminal for the path change above to take effect.
|
||||
|
||||
+4
-4
@@ -9,7 +9,7 @@
|
||||
# pull-through cache (<account>.dkr.ecr.us-east-2.amazonaws.com/docker-hub) to avoid Docker
|
||||
# Hub rate limits. Declared before the first FROM so it applies to every base image below.
|
||||
ARG BASE_IMAGE_REGISTRY=docker.io
|
||||
FROM ${BASE_IMAGE_REGISTRY}/library/python:3.11-slim@sha256:6d85378d88a19cd4d76079817532d62232be95757cb45945a99fec8e8084b9c2 AS builder
|
||||
FROM ${BASE_IMAGE_REGISTRY}/library/python:3.13-slim@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3 AS builder
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.9.9 /uv /uvx /bin/
|
||||
|
||||
@@ -47,7 +47,7 @@ RUN uv pip install --system --no-cache-dir --no-deps --require-hashes \
|
||||
-r /tmp/ee-requirements.txt && \
|
||||
pip uninstall -y py && \
|
||||
# https://github.com/tornadoweb/tornado/issues/3107
|
||||
rm -f /usr/local/lib/python3.11/site-packages/tornado/test/test.key && \
|
||||
rm -f /usr/local/lib/python3.13/site-packages/tornado/test/test.key && \
|
||||
rm -rf ~/.cache/uv /tmp/*.txt
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ RUN uv pip install --system --no-cache-dir --no-deps --require-hashes \
|
||||
# The actual shipped image. Installs only runtime system libraries and copies the already-built
|
||||
# Python packages from the builder stage.
|
||||
####################################################################################################
|
||||
FROM ${BASE_IMAGE_REGISTRY}/library/python:3.11-slim@sha256:6d85378d88a19cd4d76079817532d62232be95757cb45945a99fec8e8084b9c2 AS runtime
|
||||
FROM ${BASE_IMAGE_REGISTRY}/library/python:3.13-slim@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3 AS runtime
|
||||
|
||||
LABEL com.danswer.maintainer="founders@onyx.app"
|
||||
LABEL com.danswer.description="This image is the web/frontend container of Onyx which \
|
||||
@@ -132,7 +132,7 @@ ENV PATH="/root/.opencode/bin:${PATH}"
|
||||
# celery workers/beat, alembic migrations, supervisord, Playwright). Both stages share the same base
|
||||
# image digest, so paths and ABI match. Listing the scripts explicitly keeps the base interpreter
|
||||
# binaries and any build-stage executables out of the shipped /usr/local/bin.
|
||||
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
||||
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
|
||||
COPY --from=builder \
|
||||
/usr/local/bin/alembic \
|
||||
/usr/local/bin/celery \
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
ARG BASE_IMAGE_REGISTRY=docker.io
|
||||
|
||||
# Base stage with dependencies
|
||||
FROM ${BASE_IMAGE_REGISTRY}/library/python:3.11-slim@sha256:6d85378d88a19cd4d76079817532d62232be95757cb45945a99fec8e8084b9c2 AS base
|
||||
FROM ${BASE_IMAGE_REGISTRY}/library/python:3.13-slim@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3 AS base
|
||||
|
||||
ENV ONYX_RUNNING_IN_DOCKER="true" \
|
||||
HF_HOME=/app/.cache/huggingface
|
||||
|
||||
@@ -49,7 +49,7 @@ def upgrade() -> None:
|
||||
"time_updated",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
server_onupdate=sa.text("now()"), # ty: ignore[invalid-argument-type]
|
||||
server_onupdate=sa.text("now()"),
|
||||
nullable=True,
|
||||
),
|
||||
sa.Column(
|
||||
|
||||
@@ -28,7 +28,7 @@ def fetch_query_analytics(
|
||||
stmt = (
|
||||
select(
|
||||
func.count(ChatMessage.id),
|
||||
func.sum(case((ChatMessageFeedback.is_positive, 1), else_=0)),
|
||||
func.sum(case((ChatMessageFeedback.is_positive, 1), else_=0)), # ty: ignore[invalid-argument-type]
|
||||
func.sum(
|
||||
case(
|
||||
(ChatMessageFeedback.is_positive == False, 1), # noqa: E712
|
||||
@@ -64,7 +64,7 @@ def fetch_per_user_query_analytics(
|
||||
stmt = (
|
||||
select(
|
||||
func.count(ChatMessage.id),
|
||||
func.sum(case((ChatMessageFeedback.is_positive, 1), else_=0)),
|
||||
func.sum(case((ChatMessageFeedback.is_positive, 1), else_=0)), # ty: ignore[invalid-argument-type]
|
||||
func.sum(
|
||||
case(
|
||||
(ChatMessageFeedback.is_positive == False, 1), # noqa: E712
|
||||
|
||||
@@ -165,9 +165,9 @@ def get_used_seats(tenant_id: str | None = None) -> int:
|
||||
select(func.count())
|
||||
.select_from(User)
|
||||
.where(
|
||||
User.is_active == True, # noqa: E712
|
||||
User.is_active == True, # noqa: E712 # ty: ignore[invalid-argument-type]
|
||||
User.role != UserRole.EXT_PERM_USER,
|
||||
User.email != ANONYMOUS_USER_EMAIL,
|
||||
User.email != ANONYMOUS_USER_EMAIL, # ty: ignore[invalid-argument-type]
|
||||
User.account_type != AccountType.SERVICE_ACCOUNT,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -16,9 +16,7 @@ logger = setup_logger()
|
||||
|
||||
|
||||
def get_pg_conn_from_session(db_session: Session) -> connection:
|
||||
return (
|
||||
db_session.connection().connection.connection # ty: ignore[unresolved-attribute]
|
||||
)
|
||||
return db_session.connection().connection.connection
|
||||
|
||||
|
||||
def create_populate_lobj(
|
||||
|
||||
@@ -81,7 +81,7 @@ def get_live_users_count(db_session: Session) -> int:
|
||||
This does NOT include invited users, "users" pulled in
|
||||
from external connectors, or API keys.
|
||||
"""
|
||||
count_stmt = func.count(User.id)
|
||||
count_stmt = func.count(User.id) # ty: ignore[invalid-argument-type]
|
||||
select_stmt = select(count_stmt)
|
||||
select_stmt_w_filters = _add_live_user_count_where_clause(select_stmt, False)
|
||||
user_count = db_session.scalar(select_stmt_w_filters)
|
||||
@@ -92,7 +92,7 @@ def get_live_users_count(db_session: Session) -> int:
|
||||
|
||||
async def get_user_count(only_admin_users: bool = False) -> int:
|
||||
async with get_async_session_context_manager() as session:
|
||||
count_stmt = func.count(User.id)
|
||||
count_stmt = func.count(User.id) # ty: ignore[invalid-argument-type]
|
||||
stmt = select(count_stmt)
|
||||
stmt_w_filters = _add_live_user_count_where_clause(stmt, only_admin_users)
|
||||
user_count = await session.scalar(stmt_w_filters)
|
||||
|
||||
@@ -719,7 +719,7 @@ def upsert_documents(
|
||||
# Use COALESCE to preserve existing permissions when new values are NULL.
|
||||
# This prevents subsequent indexing runs (which don't fetch permissions)
|
||||
# from overwriting permissions set by permission sync jobs.
|
||||
update_set.update(
|
||||
update_set.update( # ty: ignore[no-matching-overload]
|
||||
{
|
||||
"external_user_emails": func.coalesce(
|
||||
insert_stmt.excluded.external_user_emails,
|
||||
|
||||
@@ -178,7 +178,7 @@ def batch_create_notifications(
|
||||
result = db_session.execute(stmt)
|
||||
inserted_ids = set(result.scalars())
|
||||
db_session.commit()
|
||||
return inserted_ids
|
||||
return inserted_ids # ty: ignore[invalid-return-type]
|
||||
|
||||
|
||||
def update_notification_last_shown(
|
||||
|
||||
@@ -57,9 +57,9 @@ def _discover_encrypted_columns() -> list[tuple[type, str, list[str], bool]]:
|
||||
for prop in mapper.column_attrs:
|
||||
for col in prop.columns:
|
||||
if isinstance(col.type, EncryptedJson):
|
||||
results.append((model_cls, prop.key, pk_names, True))
|
||||
results.append((model_cls, prop.key, pk_names, True)) # ty: ignore[invalid-argument-type]
|
||||
elif isinstance(col.type, EncryptedString):
|
||||
results.append((model_cls, prop.key, pk_names, False))
|
||||
results.append((model_cls, prop.key, pk_names, False)) # ty: ignore[invalid-argument-type]
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@@ -68,10 +68,7 @@ def is_fk_violation(exc: IntegrityError) -> bool:
|
||||
|
||||
|
||||
def model_to_dict(model: Base) -> dict[str, Any]:
|
||||
return {
|
||||
c.key: getattr(model, c.key)
|
||||
for c in inspect(model).mapper.column_attrs # ty: ignore[unresolved-attribute]
|
||||
}
|
||||
return {c.key: getattr(model, c.key) for c in inspect(model).mapper.column_attrs}
|
||||
|
||||
|
||||
RETRYABLE_PG_CODES = {
|
||||
|
||||
@@ -77,7 +77,7 @@ class HuggingFaceTokenizer(BaseTokenizer):
|
||||
"""
|
||||
Encode a string using the HuggingFaceTokenizer, but if it fails,
|
||||
encode the string as ASCII and decode it back to a string. This helps
|
||||
in cases where the string has weird characters like \udeb4.
|
||||
in cases where the string has weird characters like U+DEB4.
|
||||
"""
|
||||
try:
|
||||
return self.encoder.encode(string, add_special_tokens=False)
|
||||
|
||||
@@ -282,7 +282,7 @@ def remove_invalid_unicode_chars(text: str) -> str:
|
||||
|
||||
This handles:
|
||||
- Control characters (except tab, newline, carriage return)
|
||||
- Unpaired UTF-16 surrogates (e.g. \udc00) that cause 'surrogates not allowed' errors
|
||||
- Unpaired UTF-16 surrogates (e.g. U+DC00) that cause 'surrogates not allowed' errors
|
||||
- Unicode non-characters
|
||||
"""
|
||||
return _INVALID_UNICODE_CHARS_RE.sub("", text)
|
||||
|
||||
+227
-1152
File diff suppressed because it is too large.
Load diff
+36
-783
File diff suppressed because it is too large.
Load diff
+9
-600
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -131,7 +131,7 @@ def _skill_table_column_keys(model: type[Any]) -> list[str]:
|
||||
|
||||
|
||||
def _skill_table_pk_keys(model: type[Any]) -> list[str]:
|
||||
return [col.key for col in class_mapper(model).primary_key]
|
||||
return [col.key for col in class_mapper(model).primary_key] # ty: ignore[invalid-return-type]
|
||||
|
||||
|
||||
def _snapshot_skill_tables(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# requires-python = ">=3.13"
|
||||
# dependencies = [
|
||||
# "fastapi>=0.110",
|
||||
# "uvicorn>=0.29",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# pull-through cache (<account>.dkr.ecr.us-east-2.amazonaws.com/docker-hub) to avoid Docker
|
||||
# Hub rate limits.
|
||||
ARG BASE_IMAGE_REGISTRY=docker.io
|
||||
FROM ${BASE_IMAGE_REGISTRY}/library/python:3.11-slim@sha256:6d85378d88a19cd4d76079817532d62232be95757cb45945a99fec8e8084b9c2
|
||||
FROM ${BASE_IMAGE_REGISTRY}/library/python:3.13-slim@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -239,15 +239,19 @@ def test_contextual_rag(
|
||||
assert "tag1" in chunks[0].metadata_suffix_keyword
|
||||
assert "tag2" in chunks[0].metadata_suffix_semantic
|
||||
|
||||
doc_summary = "Test1" if enable_contextual_rag else ""
|
||||
chunk_context = ""
|
||||
count = 2
|
||||
for chunk in chunks:
|
||||
if enable_contextual_rag:
|
||||
chunk_context = f"Test{count}"
|
||||
count += 1
|
||||
assert chunk.doc_summary == doc_summary
|
||||
assert chunk.chunk_context == chunk_context
|
||||
# The doc summary is computed once (the first LLM call) and shared by every
|
||||
# chunk. The per-chunk context calls then run in parallel
|
||||
# (run_functions_tuples_in_parallel), so the mock's "TestN" counter is assigned
|
||||
# to chunks in nondeterministic order — assert the SET of contexts rather than a
|
||||
# per-chunk ordering.
|
||||
if enable_contextual_rag:
|
||||
assert all(chunk.doc_summary == "Test1" for chunk in chunks)
|
||||
assert {chunk.chunk_context for chunk in chunks} == {
|
||||
f"Test{n}" for n in range(2, 2 + len(chunks))
|
||||
}
|
||||
else:
|
||||
assert all(chunk.doc_summary == "" for chunk in chunks)
|
||||
assert all(chunk.chunk_context == "" for chunk in chunks)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
+46
-13
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -228,6 +229,27 @@ def _make_mock_response(
|
||||
return resp
|
||||
|
||||
|
||||
def _respond_by_url(
|
||||
responses: dict[str, MagicMock | Exception],
|
||||
) -> Callable[..., MagicMock]:
|
||||
"""Build a ``side_effect`` that maps each URL to its response, raising any
|
||||
mapped exception — independent of call order.
|
||||
|
||||
``OnyxWebCrawler.contents`` fetches concurrently via a ThreadPoolExecutor, so
|
||||
a positional ``side_effect`` list binds responses to URLs nondeterministically
|
||||
(whichever thread calls ``ssrf_safe_get`` first consumes the first element).
|
||||
Keying on the URL keeps the failure-isolation assertions deterministic.
|
||||
"""
|
||||
|
||||
def _side_effect(url: str, *_args: object, **_kwargs: object) -> MagicMock:
|
||||
result = responses[url]
|
||||
if isinstance(result, Exception):
|
||||
raise result
|
||||
return result
|
||||
|
||||
return _side_effect
|
||||
|
||||
|
||||
class TestParallelExecution:
|
||||
"""Verify that contents() fetches URLs in parallel."""
|
||||
|
||||
@@ -274,8 +296,15 @@ class TestFailureIsolation:
|
||||
good_resp = _make_mock_response()
|
||||
bad_resp = _make_mock_response(status_code=500)
|
||||
|
||||
# First and third URLs succeed, second fails
|
||||
mock_get.side_effect = [good_resp, bad_resp, good_resp]
|
||||
# First and third URLs succeed, second fails. Keyed by URL because the
|
||||
# crawler fetches concurrently, so call order is nondeterministic.
|
||||
mock_get.side_effect = _respond_by_url(
|
||||
{
|
||||
"http://a.com": good_resp,
|
||||
"http://b.com": bad_resp,
|
||||
"http://c.com": good_resp,
|
||||
}
|
||||
)
|
||||
|
||||
crawler = OnyxWebCrawler()
|
||||
results = crawler.contents(["http://a.com", "http://b.com", "http://c.com"])
|
||||
@@ -289,12 +318,14 @@ class TestFailureIsolation:
|
||||
def test_exception_doesnt_kill_batch(self, mock_get: MagicMock) -> None:
|
||||
good_resp = _make_mock_response()
|
||||
|
||||
# Second URL raises an exception
|
||||
mock_get.side_effect = [
|
||||
good_resp,
|
||||
RuntimeError("connection reset"),
|
||||
_make_mock_response(),
|
||||
]
|
||||
# Second URL raises an exception. Keyed by URL (concurrent fetch).
|
||||
mock_get.side_effect = _respond_by_url(
|
||||
{
|
||||
"http://a.com": good_resp,
|
||||
"http://b.com": RuntimeError("connection reset"),
|
||||
"http://c.com": _make_mock_response(),
|
||||
}
|
||||
)
|
||||
|
||||
crawler = OnyxWebCrawler()
|
||||
results = crawler.contents(["http://a.com", "http://b.com", "http://c.com"])
|
||||
@@ -309,11 +340,13 @@ class TestFailureIsolation:
|
||||
from onyx.utils.url import SSRFException
|
||||
|
||||
good_resp = _make_mock_response()
|
||||
mock_get.side_effect = [
|
||||
good_resp,
|
||||
SSRFException("blocked"),
|
||||
_make_mock_response(),
|
||||
]
|
||||
mock_get.side_effect = _respond_by_url(
|
||||
{
|
||||
"http://a.com": good_resp,
|
||||
"http://internal.local": SSRFException("blocked"),
|
||||
"http://c.com": _make_mock_response(),
|
||||
}
|
||||
)
|
||||
|
||||
crawler = OnyxWebCrawler()
|
||||
results = crawler.contents(
|
||||
|
||||
Generated
+1
-1
@@ -1,3 +1,3 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.11"
|
||||
requires-python = ">=3.13"
|
||||
@@ -13,7 +13,7 @@ need a sandbox.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Builds on the CONTRIBUTING.md prereqs (Python 3.11, uv, Node.js 22, the venv,
|
||||
Builds on the CONTRIBUTING.md prereqs (Python 3.13, uv, Node.js 22, the venv,
|
||||
`.vscode/.env`). Docker Desktop must be running with at least 8 CPU / 16 GB
|
||||
allocated.
|
||||
|
||||
|
||||
+14
-12
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
||||
[project]
|
||||
name = "onyx"
|
||||
version = "0.0.0"
|
||||
requires-python = ">=3.11"
|
||||
requires-python = ">=3.13"
|
||||
# Shared dependencies between backend and model_server
|
||||
dependencies = [
|
||||
"aioboto3==15.1.0",
|
||||
@@ -43,7 +43,7 @@ backend = [
|
||||
"croniter==6.2.2",
|
||||
"cron-descriptor==2.0.8",
|
||||
"dask==2026.1.1",
|
||||
"discord.py==2.4.0",
|
||||
"discord.py==2.7.1",
|
||||
"distributed==2026.1.1",
|
||||
"fastapi-users==15.0.4",
|
||||
"fastapi-users-db-sqlalchemy==7.0.0",
|
||||
@@ -77,10 +77,13 @@ backend = [
|
||||
# NOTE: This is frozen to avoid https://foss.heptapod.net/openpyxl/openpyxl/-/issues/2147
|
||||
"openpyxl==3.0.10",
|
||||
"opensearch-py==3.0.0",
|
||||
"passlib==1.7.4",
|
||||
# libpass is the maintained drop-in fork of passlib (same `passlib.*`
|
||||
# import namespace). passlib 1.7.4 is unmaintained and imports the stdlib
|
||||
# `crypt` module, which was removed in Python 3.13.
|
||||
"libpass==1.9.3",
|
||||
"playwright==1.58.0",
|
||||
"psutil==7.1.3",
|
||||
"psycopg2-binary==2.9.9",
|
||||
"psycopg2-binary==2.9.10",
|
||||
"puremagic==1.28",
|
||||
"pyairtable==3.0.1",
|
||||
"pycryptodome==3.19.1",
|
||||
@@ -97,7 +100,7 @@ backend = [
|
||||
"requests-oauthlib==1.3.1",
|
||||
"simple-salesforce==1.12.6",
|
||||
"slack-sdk==3.20.2",
|
||||
"SQLAlchemy==2.0.15",
|
||||
"SQLAlchemy==2.0.50",
|
||||
"starlette==0.49.3",
|
||||
"supervisor==4.3.0",
|
||||
"RapidFuzz==3.14.5",
|
||||
@@ -112,8 +115,7 @@ backend = [
|
||||
"stripe==10.12.0",
|
||||
"urllib3==2.7.0",
|
||||
"mistune==3.2.1",
|
||||
# Pinned: 11.1+ requires Python 3.12.
|
||||
"mitmproxy==11.0.2",
|
||||
"mitmproxy==12.2.3",
|
||||
"sendgrid==6.12.5",
|
||||
"exa_py==1.15.4",
|
||||
"braintrust==0.3.9",
|
||||
@@ -203,14 +205,14 @@ override-dependencies = [
|
||||
"cryptography>=42.0,<50.0",
|
||||
"h2>=4.3.0,<5.0",
|
||||
"hyperframe>=6.1.0,<7.0",
|
||||
# mitmproxy 11.0.2 caps tornado at <=6.4.2; 11.1+/12.x that bump it require
|
||||
# Python 3.12 (we're on 3.11). Override to pull in the 6.5 series, which
|
||||
# mitmproxy 12.1.1 pins to anyway, for CVE-2025-47287 (multipart logging DoS).
|
||||
# mitmproxy 12.x already pins tornado to the 6.5 series; keep this floor
|
||||
# explicit so a future resolver change can't drop back to the <=6.4.2 that
|
||||
# mitmproxy 11.x capped to (CVE-2025-47287, multipart logging DoS).
|
||||
"tornado>=6.5.0,<7.0",
|
||||
]
|
||||
|
||||
[tool.ty.environment]
|
||||
python-version = "3.11"
|
||||
python-version = "3.13"
|
||||
root = ["./backend"]
|
||||
|
||||
[tool.ty.src]
|
||||
@@ -244,7 +246,7 @@ typeCheckingMode = "off"
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
target-version = "py311"
|
||||
target-version = "py313"
|
||||
|
||||
[tool.ruff.lint]
|
||||
ignore = [
|
||||
|
||||
Reference in new issue
Block a user