Merge pull request #34198 from akhilnarang/python3.14

build: support python3.14
This commit is contained in:
Akhil Narang 2025-11-14 18:57:40 +05:30 committed by GitHub
commit 0be60be5dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 41 additions and 23 deletions

View file

@ -9,7 +9,7 @@ on:
python-version:
required: false
type: string
default: '3.13'
default: '3.14'
node-version:
required: false
type: number

View file

@ -5,7 +5,7 @@ on:
python-version:
required: false
type: string
default: '3.13.0'
default: '3.14.0'
jobs:
typecheck:

View file

@ -9,7 +9,7 @@ on:
python-version:
required: false
type: string
default: '3.13'
default: '3.14'
node-version:
required: false
type: number

View file

@ -25,7 +25,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
python-version: "3.14"
- name: Run script to update POT file
run: |

View file

@ -41,7 +41,7 @@ jobs:
- name: 'Setup Environment'
uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
- uses: actions/checkout@v5
- name: Validate Docs
@ -60,7 +60,7 @@ jobs:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
cache: pip
- name: Download Semgrep rules
@ -78,7 +78,7 @@ jobs:
steps:
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
- uses: actions/checkout@v5
@ -106,6 +106,6 @@ jobs:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
cache: pip
- uses: pre-commit/action@v3.0.1

View file

@ -26,7 +26,7 @@ jobs:
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
- name: Set up bench and build assets
run: |
npm install -g yarn

View file

@ -19,7 +19,7 @@ jobs:
node-version: 22
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
- name: Set up bench and build assets
run: |
npm install -g yarn

View file

@ -74,7 +74,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
- name: Setup Node
uses: actions/setup-node@v6

View file

@ -176,6 +176,13 @@ class TestRQJob(IntegrationTestCase):
if frappe.conf.use_mysqlclient:
# TEMP: Add extra allowance for running two connectors, this should be rolled back before v16
LAST_MEASURED_USAGE += 2
# Observed higher usage on 3.14. Temporarily raising the limit
from sys import version_info
if version_info >= (3, 14):
LAST_MEASURED_USAGE += 5
self.assertLessEqual(rss, LAST_MEASURED_USAGE * 1.05, msg)
def test_clear_failed_jobs(self):

View file

@ -144,6 +144,8 @@ class TestConnectedApp(IntegrationTestCase):
doc = frappe.get_doc("OAuth Authorization Code", code.name)
doc.delete()
frappe.db.commit()
delete_if_exists("user")
delete_if_exists("oauth_client")

View file

@ -193,14 +193,16 @@ class TestPerformance(IntegrationTestCase):
"""
query = "select * from tabUser"
expected_refcount = 1 if sys.version_info >= (3, 14) else 2
for kwargs in ({}, {"as_dict": True}, {"as_list": True}):
result = frappe.db.sql(query, **kwargs)
self.assertEqual(sys.getrefcount(result), 2) # Note: This always returns +1
self.assertEqual(sys.getrefcount(result), expected_refcount) # Note: This always returns +1
self.assertFalse(gc.get_referrers(result))
def test_no_cyclic_references(self):
doc = frappe.get_doc("User", "Administrator")
self.assertEqual(sys.getrefcount(doc), 2) # Note: This always returns +1
expected_refcount = 1 if sys.version_info >= (3, 14) else 2
self.assertEqual(sys.getrefcount(doc), expected_refcount) # Note: This always returns +1
def test_get_doc_cache_calls(self):
frappe.get_doc("User", "Administrator")

View file

@ -2,6 +2,7 @@ import os
import random
import signal
import socket
import sys
import time
from collections import defaultdict
from collections.abc import Callable
@ -373,6 +374,7 @@ class FrappeWorker(Worker):
def start_frappe_scheduler(self):
from frappe.utils.scheduler import start_scheduler
# TODO: switch to multiprocessing.Process() after further investigating of fork -> forkserver
Thread(target=start_scheduler, daemon=True).start()
@ -418,7 +420,6 @@ def start_worker_pool(
WARNING: This feature is considered "EXPERIMENTAL".
"""
_start_sentry()
# If gc.freeze is done then importing modules before forking allows us to share the memory
@ -447,9 +448,15 @@ def start_worker_pool(
logging_level = "WARNING"
# TODO: Make this true by default eventually. It's limited to RQ WorkerPool
no_fork = sbool(os.environ.get("FRAPPE_BACKGROUND_WORKERS_NOFORK", False))
if sbool(os.environ.get("FRAPPE_BACKGROUND_WORKERS_NOFORK", False)):
worker_klass = FrappeWorkerNoFork
else:
if sys.version_info >= (3, 14):
import multiprocessing
multiprocessing.set_start_method("fork", force=True)
worker_klass = FrappeWorker
worker_klass = FrappeWorkerNoFork if no_fork else FrappeWorker
pool = WorkerPool(
queues=queues,
connection=redis_connection,

View file

@ -4,7 +4,7 @@ authors = [
{ name = "Frappe Technologies Pvt Ltd", email = "developers@frappe.io"}
]
description = "Metadata driven, full-stack low code web framework"
requires-python = ">=3.10,<3.14"
requires-python = ">=3.10,<3.15"
readme = "README.md"
dynamic = ["version"]
dependencies = [
@ -21,11 +21,11 @@ dependencies = [
# do NOT add loose requirements on PyMySQL versions.
"PyMySQL==1.1.1",
"pypdf~=6.1.3",
"PyPika @ git+https://github.com/frappe/pypika@093984977ce157d35e048c51d9ff55a1f0f44570",
"PyPika @ git+https://github.com/frappe/pypika@2c50e6142b2d61d2d243e466fdd5dc03b3d918f2",
"mysqlclient==2.2.7",
"PyQRCode~=1.2.1",
"PyYAML~=6.0.2",
"RestrictedPython~=8.0",
"RestrictedPython~=8.1",
"WeasyPrint==66.0",
"pydyf==0.11.0",
"Werkzeug==3.1.3",
@ -35,7 +35,7 @@ dependencies = [
"bleach[css]~=6.2.0",
"chardet~=5.2.0",
"croniter~=6.0.0",
"cryptography~=45.0.4",
"cryptography~=46.0.2",
"cssutils~=2.11.1",
"email-reply-parser~=0.5.12",
"gunicorn @ git+https://github.com/frappe/gunicorn@bb554053bb87218120d76ab6676af7015680e8b6",
@ -47,15 +47,15 @@ dependencies = [
"num2words~=0.5.14",
"oauthlib~=3.2.2",
"openpyxl~=3.1.5",
"orjson~=3.10.18",
"orjson~=3.11.3",
"passlib~=1.7.4",
"pdfkit~=1.0.0",
"phonenumbers~=9.0.7",
"premailer~=3.10.0",
"psutil~=7.0.0",
"psycopg2-binary~=2.9.1",
"pyOpenSSL~=25.1.0",
"pydantic~=2.11.7",
"pyOpenSSL~=25.3.0",
"pydantic~=2.12.0",
"pyotp~=2.9.0",
"python-dateutil~=2.9.0",
"pytz==2025.2",