Skip to content

Commit ee210a9

Browse files
Copilotpamelafox
andauthored
Add Python 3.14 support and drop Python 3.9 (#2787)
* Initial plan * Add Python 3.14 support and drop Python 3.9 - Update workflow to test Python 3.10-3.14 instead of 3.9-3.13 - Update pyproject.toml to target Python 3.10 as minimum version - Upgrade Pillow from 10.4.0 to 12.0.0 for Python 3.14 support - Update Dockerfile to use Python 3.13-bookworm - Update devcontainer to use Python 3.13-bookworm - Update README and AGENTS.md documentation Co-authored-by: pamelafox <[email protected]> * Fix pyproject.toml python_version format for mypy Co-authored-by: pamelafox <[email protected]> * Update uv version to 0.9.5 in GitHub workflows Co-authored-by: pamelafox <[email protected]> * Upgrade type annotations to Python 3.10+ syntax (X | Y) Applied ruff check --fix to automatically upgrade type annotations: - Replace Optional[X] with X | None - Replace Union[X, Y] with X | Y - Move Callable import from typing to collections.abc This is possible now that Python 3.9 support is dropped. Co-authored-by: pamelafox <[email protected]> * Upgrade pydantic-core to 2.41.4 for Python 3.14 support Upgraded dependencies to get Python 3.14 wheel support: - pydantic: 2.8.2 → 2.12.3 - pydantic-core: 2.20.1 → 2.41.4 (has cp314 wheels) - openai: 1.99.8 → 2.6.1 - Also upgraded Azure SDKs and other dependencies Verified pydantic-core 2.41.4 has Python 3.14 (cp314) wheels available on PyPI and installs successfully. Co-authored-by: pamelafox <[email protected]> * Fix openai version to stay on v1.x to avoid breaking changes Constrained openai to <2.0 in requirements.in to prevent upgrading to v2.x which has breaking API changes. Downgraded from 2.6.1 to 1.109.1 (latest v1.x). Kept pydantic-core at 2.41.4 for Python 3.14 support. Co-authored-by: pamelafox <[email protected]> * Fix: Use minimal pydantic upgrade for Python 3.14 support Reverted to original requirements and applied minimal upgrades: - pydantic: 2.8.2 → 2.12.3 - pydantic-core: 2.20.1 → 2.41.4 (has cp314 wheels) - typing-extensions: 4.13.2 → 4.15.0 - Added typing-inspection==0.4.2 (new pydantic dependency) - Constrained openai to <2.0 in requirements.in This avoids upgrading tiktoken, uvicorn, and other unrelated packages that were causing test failures. Co-authored-by: pamelafox <[email protected]> * Upgrade Pillow to 12.0.0 for Python 3.14 support Pillow 10.4.0 doesn't have Python 3.14 (cp314) wheels. Upgraded to Pillow 12.0.0 which includes cp314 wheels for Python 3.14 support. Co-authored-by: pamelafox <[email protected]> * Upgrade tiktoken to 0.12.0 for Python 3.14 support tiktoken 0.8.0 doesn't have Python 3.14 (cp314) wheels. Upgraded to tiktoken 0.12.0 which includes cp314 wheels for Python 3.14 support. Co-authored-by: pamelafox <[email protected]> * Remove unneeded upper bound on openai * Bring back optional * Undo unneeded change * Fix mypy * Add tests for missing lines --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: pamelafox <[email protected]> Co-authored-by: Pamela Fox <[email protected]> Co-authored-by: Pamela Fox <[email protected]>
1 parent 6c9c9ab commit ee210a9

26 files changed

+302
-234
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Azure Search OpenAI Demo",
3-
"image": "mcr.microsoft.com/devcontainers/python:3.11-bookworm",
3+
"image": "mcr.microsoft.com/devcontainers/python:3.13-bookworm",
44
"features": {
55
"ghcr.io/devcontainers/features/node:1": {
66
// This should match the version of Node.js in Github Actions workflows

.github/workflows/evaluate.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ jobs:
128128
uses: astral-sh/setup-uv@v6
129129
with:
130130
enable-cache: true
131-
version: "0.4.20"
131+
version: "0.9.5"
132132
cache-dependency-glob: "requirements**.txt"
133133
python-version: "3.11"
134134

.github/workflows/python-test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
fail-fast: false
2626
matrix:
2727
os: ["ubuntu-latest", "windows-latest"]
28-
python_version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
28+
python_version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
2929
node_version: ["20.14", "22"]
3030
steps:
3131
- uses: actions/checkout@v5
@@ -36,7 +36,7 @@ jobs:
3636
uses: astral-sh/setup-uv@v6
3737
with:
3838
enable-cache: true
39-
version: "0.4.20"
39+
version: "0.9.5"
4040
cache-dependency-glob: "requirements**.txt"
4141
python-version: ${{ matrix.python_version }}
4242
activate-environment: true

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ repos:
77
- id: end-of-file-fixer
88
- id: trailing-whitespace
99
- repo: https://github.com/astral-sh/ruff-pre-commit
10-
rev: v0.9.3
10+
rev: v0.14.2
1111
hooks:
1212
- id: ruff
1313
- repo: https://github.com/psf/black

AGENTS.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ When you're running tests, make sure you activate the .venv virtual environment
8686
source .venv/bin/activate
8787
```
8888

89+
To check for coverage, run the following command:
90+
91+
```shell
92+
pytest --cov --cov-report=annotate:cov_annotate
93+
```
94+
95+
Open the cov_annotate directory to view the annotated source code. There will be one file per source file. If a file has 100% source coverage, it means all lines are covered by tests, so you do not need to open the file.
96+
97+
For each file that has less than 100% test coverage, find the matching file in cov_annotate and review the file.
98+
99+
If a line starts with a ! (exclamation mark), it means that the line is not covered by tests. Add tests to cover the missing lines.
100+
89101
## Sending pull requests
90102

91103
When sending pull requests, make sure to follow the PULL_REQUEST_TEMPLATE.md format.
@@ -95,15 +107,15 @@ When sending pull requests, make sure to follow the PULL_REQUEST_TEMPLATE.md for
95107
To upgrade a particular package in the backend, use the following command, replacing `<package-name>` with the name of the package you want to upgrade:
96108

97109
```shell
98-
cd app/backend && uv pip compile requirements.in -o requirements.txt --python-version 3.9 --upgrade-package package-name
110+
cd app/backend && uv pip compile requirements.in -o requirements.txt --python-version 3.10 --upgrade-package package-name
99111
```
100112

101113
## Checking Python type hints
102114

103115
To check Python type hints, use the following command:
104116

105117
```shell
106-
cd app/backend && mypy . --config-file=../pyproject.toml
118+
cd app/backend && mypy . --config-file=../../pyproject.toml
107119
```
108120

109121
```shell

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ A related option is VS Code Dev Containers, which will open the project in your
131131
1. Install the required tools:
132132

133133
- [Azure Developer CLI](https://aka.ms/azure-dev/install)
134-
- [Python 3.9, 3.10, or 3.11](https://www.python.org/downloads/)
134+
- [Python 3.10, 3.11, 3.12, 3.13, or 3.14](https://www.python.org/downloads/)
135135
- **Important**: Python and the pip package manager must be in the path in Windows for the setup scripts to work.
136136
- **Important**: Ensure you can run `python --version` from console. On Ubuntu, you might need to run `sudo apt install python-is-python3` to link `python` to `python3`.
137137
- [Node.js 20+](https://nodejs.org/download/)

app/backend/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.11-bullseye
1+
FROM python:3.13-bookworm
22

33
WORKDIR /app
44

app/backend/app.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
import mimetypes
66
import os
77
import time
8-
from collections.abc import AsyncGenerator, Awaitable
8+
from collections.abc import AsyncGenerator, Awaitable, Callable
99
from pathlib import Path
10-
from typing import Any, Callable, Union, cast
10+
from typing import Any, cast
1111

1212
from azure.cognitiveservices.speech import (
1313
ResultReason,
@@ -477,7 +477,7 @@ async def setup_clients():
477477
# Use the current user identity for keyless authentication to Azure services.
478478
# This assumes you use 'azd auth login' locally, and managed identity when deployed on Azure.
479479
# The managed identity is setup in the infra/ folder.
480-
azure_credential: Union[AzureDeveloperCliCredential, ManagedIdentityCredential]
480+
azure_credential: AzureDeveloperCliCredential | ManagedIdentityCredential
481481
azure_ai_token_provider: Callable[[], Awaitable[str]]
482482
if RUNNING_ON_AZURE:
483483
current_app.logger.info("Setting up Azure credential using ManagedIdentityCredential")

app/backend/approaches/approach.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from abc import ABC
33
from collections.abc import AsyncGenerator, Awaitable
44
from dataclasses import dataclass, field
5-
from typing import Any, Optional, TypedDict, Union, cast
5+
from typing import Any, Optional, TypedDict, cast
66

77
from azure.search.documents.agent.aio import KnowledgeAgentRetrievalClient
88
from azure.search.documents.agent.models import (
@@ -190,7 +190,7 @@ def build_filter(self, overrides: dict[str, Any]) -> Optional[str]:
190190
filters.append("category eq '{}'".format(include_category.replace("'", "''")))
191191
if exclude_category:
192192
filters.append("category ne '{}'".format(exclude_category.replace("'", "''")))
193-
return None if len(filters) == 0 else " and ".join(filters)
193+
return None if not filters else " and ".join(filters)
194194

195195
async def search(
196196
self,
@@ -520,7 +520,7 @@ def create_chat_completion(
520520
temperature: Optional[float] = None,
521521
n: Optional[int] = None,
522522
reasoning_effort: Optional[ChatCompletionReasoningEffort] = None,
523-
) -> Union[Awaitable[ChatCompletion], Awaitable[AsyncStream[ChatCompletionChunk]]]:
523+
) -> Awaitable[ChatCompletion] | Awaitable[AsyncStream[ChatCompletionChunk]]:
524524
if chatgpt_model in self.GPT_REASONING_MODELS:
525525
params: dict[str, Any] = {
526526
# max_tokens is not supported

app/backend/approaches/chatreadretrieveread.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json
22
import re
33
from collections.abc import AsyncGenerator, Awaitable
4-
from typing import Any, Optional, Union, cast
4+
from typing import Any, Optional, cast
55

66
from azure.search.documents.agent.aio import KnowledgeAgentRetrievalClient
77
from azure.search.documents.aio import SearchClient
@@ -215,7 +215,7 @@ async def run_until_final_call(
215215
overrides: dict[str, Any],
216216
auth_claims: dict[str, Any],
217217
should_stream: bool = False,
218-
) -> tuple[ExtraInfo, Union[Awaitable[ChatCompletion], Awaitable[AsyncStream[ChatCompletionChunk]]]]:
218+
) -> tuple[ExtraInfo, Awaitable[ChatCompletion] | Awaitable[AsyncStream[ChatCompletionChunk]]]:
219219
use_agentic_retrieval = True if overrides.get("use_agentic_retrieval") else False
220220
original_user_query = messages[-1]["content"]
221221

@@ -243,7 +243,7 @@ async def run_until_final_call(
243243
)
244244

245245
chat_coroutine = cast(
246-
Union[Awaitable[ChatCompletion], Awaitable[AsyncStream[ChatCompletionChunk]]],
246+
Awaitable[ChatCompletion] | Awaitable[AsyncStream[ChatCompletionChunk]],
247247
self.create_chat_completion(
248248
self.chatgpt_deployment,
249249
self.chatgpt_model,

0 commit comments

Comments
 (0)