diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index 508ba98e..4a311db0 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,5 +13,5 @@
# limitations under the License.
docker:
image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
- digest: sha256:25de45b58e52021d3a24a6273964371a97a4efeefe6ad3845a64e697c63b6447
-# created: 2025-04-14T14:34:43.260858345Z
+ digest: sha256:543e209e7c1c1ffe720eb4db1a3f045a75099304fb19aa11a47dc717b8aae2a9
+# created: 2025-10-09T14:48:42.914384887Z
diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml
index d9de7a03..c3e26318 100644
--- a/.github/sync-repo-settings.yaml
+++ b/.github/sync-repo-settings.yaml
@@ -11,7 +11,6 @@ branchProtectionRules:
- 'OwlBot Post Processor'
- 'Kokoro'
- 'Samples - Lint'
- - 'Samples - Python 3.8'
- 'Samples - Python 3.9'
- 'Samples - Python 3.10'
- 'Samples - Python 3.11'
diff --git a/.kokoro/samples/python3.14/common.cfg b/.kokoro/samples/python3.14/common.cfg
new file mode 100644
index 00000000..cc6e7ee0
--- /dev/null
+++ b/.kokoro/samples/python3.14/common.cfg
@@ -0,0 +1,40 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+# Build logs will be here
+action {
+ define_artifacts {
+ regex: "**/*sponge_log.xml"
+ }
+}
+
+# Specify which tests to run
+env_vars: {
+ key: "RUN_TESTS_SESSION"
+ value: "py-3.14"
+}
+
+# Declare build specific Cloud project.
+env_vars: {
+ key: "BUILD_SPECIFIC_GCLOUD_PROJECT"
+ value: "python-docs-samples-tests-314"
+}
+
+env_vars: {
+ key: "TRAMPOLINE_BUILD_FILE"
+ value: "github/python-bigquery-sqlalchemy/.kokoro/test-samples.sh"
+}
+
+# Configure the docker image for kokoro-trampoline.
+env_vars: {
+ key: "TRAMPOLINE_IMAGE"
+ value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker"
+}
+
+# Download secrets for samples
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples"
+
+# Download trampoline resources.
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
+
+# Use the trampoline script to run in docker.
+build_file: "python-bigquery-sqlalchemy/.kokoro/trampoline_v2.sh"
diff --git a/.kokoro/samples/python3.14/continuous.cfg b/.kokoro/samples/python3.14/continuous.cfg
new file mode 100644
index 00000000..a1c8d975
--- /dev/null
+++ b/.kokoro/samples/python3.14/continuous.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
\ No newline at end of file
diff --git a/.kokoro/samples/python3.14/periodic-head.cfg b/.kokoro/samples/python3.14/periodic-head.cfg
new file mode 100644
index 00000000..abf3481d
--- /dev/null
+++ b/.kokoro/samples/python3.14/periodic-head.cfg
@@ -0,0 +1,11 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
+
+env_vars: {
+ key: "TRAMPOLINE_BUILD_FILE"
+ value: "github/python-bigquery-sqlalchemy/.kokoro/test-samples-against-head.sh"
+}
diff --git a/.kokoro/samples/python3.14/periodic.cfg b/.kokoro/samples/python3.14/periodic.cfg
new file mode 100644
index 00000000..71cd1e59
--- /dev/null
+++ b/.kokoro/samples/python3.14/periodic.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "False"
+}
diff --git a/.kokoro/samples/python3.14/presubmit.cfg b/.kokoro/samples/python3.14/presubmit.cfg
new file mode 100644
index 00000000..a1c8d975
--- /dev/null
+++ b/.kokoro/samples/python3.14/presubmit.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 72311aa6..83a5bc63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,14 @@ Older versions of this project were distributed as [pybigquery][0].
[2]: https://pypi.org/project/pybigquery/#history
+## [1.16.0](https://github.com/googleapis/python-bigquery-sqlalchemy/compare/v1.15.0...v1.16.0) (2025-11-05)
+
+
+### Features
+
+* Add support for Python 3.14 ([#1278](https://github.com/googleapis/python-bigquery-sqlalchemy/issues/1278)) ([c09a009](https://github.com/googleapis/python-bigquery-sqlalchemy/commit/c09a00976a70194abb2561753071e3e7b3659d5c))
+* Remove python 3.8 support ([#1215](https://github.com/googleapis/python-bigquery-sqlalchemy/issues/1215)) ([632d6ef](https://github.com/googleapis/python-bigquery-sqlalchemy/commit/632d6ef024bdd1dcb4206e2b111063f79f29b7ba))
+
## [1.15.0](https://github.com/googleapis/python-bigquery-sqlalchemy/compare/v1.14.1...v1.15.0) (2025-06-18)
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 2f03f9d0..3d141082 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -22,7 +22,7 @@ In order to add a feature:
documentation.
- The feature must work fully on the following CPython versions:
- 3.8, 3.9, 3.10, 3.11, 3.12, and 3.13 on both UNIX and Windows.
+ 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows.
- The feature must not add unnecessary dependencies (where
"unnecessary" is of course subjective, but new dependencies should
@@ -72,7 +72,7 @@ We use `nox `__ to instrument our tests.
- To run a single unit test::
- $ nox -s unit-3.13 -- -k
+ $ nox -s unit-3.14 -- -k
.. note::
@@ -143,12 +143,12 @@ Running System Tests
$ nox -s system
# Run a single system test
- $ nox -s system-3.13 -- -k
+ $ nox -s system-3.14 -- -k
.. note::
- System tests are only configured to run under Python 3.8, 3.12, and 3.13.
+ System tests are only configured to run under Python 3.9, 3.12, and 3.14.
For expediency, we do not run them in older versions of Python 3.
This alone will not run the tests. You'll need to change some local
@@ -195,11 +195,11 @@ configure them just like the System Tests.
# Run all tests in a folder
$ cd samples/snippets
- $ nox -s py-3.8
+ $ nox -s py-3.9
# Run a single sample test
$ cd samples/snippets
- $ nox -s py-3.8 -- -k
+ $ nox -s py-3.9 -- -k
********************************************
Note About ``README`` as it pertains to PyPI
@@ -221,19 +221,19 @@ Supported Python Versions
We support:
-- `Python 3.8`_
- `Python 3.9`_
- `Python 3.10`_
- `Python 3.11`_
- `Python 3.12`_
- `Python 3.13`_
+- `Python 3.14`_
-.. _Python 3.8: https://docs.python.org/3.8/
.. _Python 3.9: https://docs.python.org/3.9/
.. _Python 3.10: https://docs.python.org/3.10/
.. _Python 3.11: https://docs.python.org/3.11/
.. _Python 3.12: https://docs.python.org/3.12/
.. _Python 3.13: https://docs.python.org/3.13/
+.. _Python 3.14: https://docs.python.org/3.14/
Supported versions can be found in our ``noxfile.py`` `config`_.
@@ -241,7 +241,7 @@ Supported versions can be found in our ``noxfile.py`` `config`_.
.. _config: https://github.com/googleapis/python-bigquery-sqlalchemy/blob/main/noxfile.py
-We also explicitly decided to support Python 3 beginning with version 3.8.
+We also explicitly decided to support Python 3 beginning with version 3.9.
Reasons for this include:
- Encouraging use of newest versions of Python 3
diff --git a/README.rst b/README.rst
index 2a64c0c7..be964f0c 100644
--- a/README.rst
+++ b/README.rst
@@ -51,7 +51,7 @@ dependencies.
Supported Python Versions
^^^^^^^^^^^^^^^^^^^^^^^^^
-Python >= 3.8, <3.14
+Python >= 3.9, <3.14
Unsupported Python Versions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/noxfile.py b/noxfile.py
index b6e34730..d4fd895b 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -18,10 +18,12 @@
from __future__ import absolute_import
+from functools import wraps
import os
import pathlib
import re
import shutil
+import time
from typing import Dict, List
import warnings
@@ -39,9 +41,9 @@
"setup.py",
]
-DEFAULT_PYTHON_VERSION = "3.8"
+DEFAULT_PYTHON_VERSION = "3.14"
-UNIT_TEST_PYTHON_VERSIONS: List[str] = ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
+UNIT_TEST_PYTHON_VERSIONS: List[str] = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
UNIT_TEST_STANDARD_DEPENDENCIES = [
"mock",
"asyncmock",
@@ -56,11 +58,6 @@
"tests",
]
UNIT_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {
- "3.8": [
- "tests",
- "alembic",
- "bqstorage",
- ],
"3.11": [
"tests",
"geography",
@@ -76,9 +73,14 @@
"geography",
"bqstorage",
],
+ "3.14": [
+ "tests",
+ "geography",
+ "bqstorage",
+ ],
}
-SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.8", "3.12", "3.13"]
+SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.9", "3.12", "3.13", "3.14"]
SYSTEM_TEST_STANDARD_DEPENDENCIES: List[str] = [
"mock",
"pytest",
@@ -91,17 +93,17 @@
"tests",
]
SYSTEM_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {
- "3.8": [
+ "3.12": [
"tests",
- "alembic",
+ "geography",
"bqstorage",
],
- "3.12": [
+ "3.13": [
"tests",
"geography",
"bqstorage",
],
- "3.13": [
+ "3.14": [
"tests",
"geography",
"bqstorage",
@@ -110,6 +112,27 @@
CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
+
+def _calculate_duration(func):
+ """This decorator prints the execution time for the decorated function."""
+
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ start = time.monotonic()
+ result = func(*args, **kwargs)
+ end = time.monotonic()
+ total_seconds = round(end - start)
+ hours = total_seconds // 3600 # Integer division to get hours
+ remaining_seconds = total_seconds % 3600 # Modulo to find remaining seconds
+ minutes = remaining_seconds // 60
+ seconds = remaining_seconds % 60
+ human_time = f"{hours:}:{minutes:0>2}:{seconds:0>2}"
+ print(f"Session ran in {total_seconds} seconds ({human_time})")
+ return result
+
+ return wrapper
+
+
nox.options.sessions = [
"unit",
"system",
@@ -128,6 +151,7 @@
@nox.session(python=DEFAULT_PYTHON_VERSION)
+@_calculate_duration
def lint(session):
"""Run linters.
@@ -144,6 +168,7 @@ def lint(session):
@nox.session(python=DEFAULT_PYTHON_VERSION)
+@_calculate_duration
def blacken(session):
"""Run black. Format code to uniform standard."""
session.install(BLACK_VERSION)
@@ -154,6 +179,7 @@ def blacken(session):
@nox.session(python=DEFAULT_PYTHON_VERSION)
+@_calculate_duration
def format(session):
"""
Run isort to sort imports. Then run black
@@ -174,9 +200,10 @@ def format(session):
@nox.session(python=DEFAULT_PYTHON_VERSION)
+@_calculate_duration
def lint_setup_py(session):
"""Verify that setup.py is valid (including RST check)."""
- session.install("docutils", "pygments")
+ session.install("docutils", "pygments", "setuptools")
session.run("python", "setup.py", "check", "--restructuredtext", "--strict")
@@ -213,10 +240,16 @@ def install_unittest_dependencies(session, *constraints):
"protobuf_implementation",
["python", "upb", "cpp"],
)
+@_calculate_duration
def unit(session, protobuf_implementation, install_extras=True):
# Install all test dependencies, then install this package in-place.
- if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"):
+ if protobuf_implementation == "cpp" and session.python in (
+ "3.11",
+ "3.12",
+ "3.13",
+ "3.14",
+ ):
session.skip("cpp implementation is not supported in python 3.11+")
constraints_path = str(
@@ -224,7 +257,7 @@ def unit(session, protobuf_implementation, install_extras=True):
)
install_unittest_dependencies(session, "-c", constraints_path)
- if install_extras and session.python in ["3.11", "3.12", "3.13"]:
+ if install_extras and session.python in ["3.11", "3.12", "3.13", "3.14"]:
install_target = ".[geography,alembic,tests,bqstorage]"
elif install_extras:
install_target = ".[all]"
@@ -288,6 +321,7 @@ def install_systemtest_dependencies(session, *constraints):
@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS)
+@_calculate_duration
def system(session):
"""Run the system test suite."""
constraints_path = str(
@@ -331,6 +365,7 @@ def system(session):
@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS)
+@_calculate_duration
def system_noextras(session):
"""Run the system test suite."""
constraints_path = str(
@@ -376,6 +411,7 @@ def system_noextras(session):
@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS[-1])
+@_calculate_duration
def compliance(session):
"""Run the SQLAlchemy dialect-compliance system tests"""
constraints_path = str(
@@ -398,10 +434,8 @@ def compliance(session):
"-c",
constraints_path,
)
- if session.python == "3.8":
- extras = "[tests,alembic]"
- elif session.python in ["3.12", "3.13"]:
- extras = "[tests,geography]"
+ if session.python in ["3.12", "3.13", "3.14"]:
+ extras = "[tests,geography,alembic]"
else:
extras = "[tests]"
session.install("-e", f".{extras}", "-c", constraints_path)
@@ -430,6 +464,7 @@ def compliance(session):
@nox.session(python=DEFAULT_PYTHON_VERSION)
+@_calculate_duration
def cover(session):
"""Run the final coverage report.
@@ -443,6 +478,7 @@ def cover(session):
@nox.session(python="3.10")
+@_calculate_duration
def docs(session):
"""Build the docs for this library."""
@@ -480,6 +516,7 @@ def docs(session):
@nox.session(python="3.10")
+@_calculate_duration
def docfx(session):
"""Build the docfx yaml files for this library."""
@@ -527,15 +564,21 @@ def docfx(session):
)
-@nox.session(python="3.13")
+@nox.session(python=DEFAULT_PYTHON_VERSION)
@nox.parametrize(
"protobuf_implementation",
["python", "upb", "cpp"],
)
+@_calculate_duration
def prerelease_deps(session, protobuf_implementation):
"""Run all tests with prerelease versions of dependencies installed."""
- if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"):
+ if protobuf_implementation == "cpp" and session.python in (
+ "3.11",
+ "3.12",
+ "3.13",
+ "3.14",
+ ):
session.skip("cpp implementation is not supported in python 3.11+")
# Install all dependencies
diff --git a/owlbot.py b/owlbot.py
index 5173dce0..e5e1d8b4 100644
--- a/owlbot.py
+++ b/owlbot.py
@@ -30,14 +30,15 @@
# ----------------------------------------------------------------------------
extras = ["tests"]
extras_by_python = {
- "3.8": ["tests", "alembic", "bqstorage"],
+ "3.9": ["tests", "alembic", "bqstorage"],
"3.11": ["tests", "geography", "bqstorage"],
"3.12": ["tests", "geography", "bqstorage"],
"3.13": ["tests", "geography", "bqstorage"],
+ "3.14": ["tests", "geography", "bqstorage"],
}
templated_files = common.py_library(
- unit_test_python_versions=["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"],
- system_test_python_versions=["3.8", "3.12", "3.13"],
+ unit_test_python_versions=["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"],
+ system_test_python_versions=["3.9", "3.12", "3.13", "3.14"],
cov_level=100,
unit_test_extras=extras,
unit_test_extras_by_python=extras_by_python,
@@ -53,6 +54,7 @@
# exclude gh actions as credentials are needed for tests
".github/workflows",
"README.rst",
+ "renovate.json",
],
)
diff --git a/renovate.json b/renovate.json
index c7875c46..ccdeeb0a 100644
--- a/renovate.json
+++ b/renovate.json
@@ -8,5 +8,12 @@
"ignorePaths": [".pre-commit-config.yaml", ".kokoro/requirements.txt", "setup.py", ".github/workflows/unittest.yml"],
"pip_requirements": {
"fileMatch": ["requirements-test.txt", "samples/[\\S/]*constraints.txt", "samples/[\\S/]*constraints-test.txt"]
- }
+ },
+ "packageRules": [
+ {
+ "matchFileNames": ["requirements.txt"],
+ "matchStrings": ["geoalchemy2(.*); python_version == '3.9'"],
+ "allowedVersions": ">= 0.17.1, < 0.18.0"
+ }
+ ]
}
diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py
index e5885544..6c2546ed 100644
--- a/samples/snippets/noxfile.py
+++ b/samples/snippets/noxfile.py
@@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]:
# DO NOT EDIT - automatically generated.
# All versions used to test samples.
-ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
+ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
# Any default versions that should be ignored.
IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"]
diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt
index 82b196c3..b878043a 100644
--- a/samples/snippets/requirements-test.txt
+++ b/samples/snippets/requirements-test.txt
@@ -1,20 +1,17 @@
-attrs==25.3.0
+attrs==25.4.0
click===8.1.8; python_version <= '3.9'
-click==8.2.1; python_version >= '3.10'
-google-auth==2.40.3
+click==8.3.0; python_version >= '3.10'
+google-auth==2.41.1
google-cloud-testutils==1.6.4
iniconfig==2.1.0
packaging==25.0
-pluggy===1.5.0; python_version == '3.8'
pluggy==1.6.0; python_version >= '3.9'
py==1.11.0
pyasn1==0.6.1
pyasn1-modules==0.4.2
-pyparsing===3.1.4; python_version == '3.8'
-pyparsing==3.2.3; python_version >= '3.9'
+pyparsing==3.2.5; python_version >= '3.9'
pytest===6.2.5
rsa==4.9.1
six==1.17.0
toml==0.10.2
-typing-extensions===4.13.0; python_version == '3.8'
-typing-extensions==4.14.0; python_version >= '3.9'
+typing-extensions==4.15.0; python_version >= '3.9'
diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 3ee07a2d..2b448f07 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -1,47 +1,35 @@
-alembic===1.14.0; python_version == '3.8'
-alembic==1.16.2; python_version >= '3.9'
-certifi==2025.6.15
-charset-normalizer==3.4.2
-geoalchemy2==0.17.1
-google-api-core[grpc]==2.25.1
-google-auth==2.40.3
-google-cloud-bigquery===3.30.0; python_version == '3.8'
-google-cloud-bigquery==3.34.0; python_version >= '3.9'
+alembic==1.16.5; python_version >= '3.9'
+certifi==2025.10.5
+charset-normalizer==3.4.3
+geoalchemy2===0.17.1; python_version == '3.9'
+geoalchemy2==0.18.0; python_version >= '3.10'
+google-api-core[grpc]==2.25.2
+google-auth==2.41.1
+google-cloud-bigquery==3.38.0; python_version >= '3.9'
google-cloud-core==2.4.3
-google-crc32c===1.5.0; python_version == '3.8'
google-crc32c==1.7.1; python_version >= '3.9'
google-resumable-media==2.7.2
googleapis-common-protos==1.70.0
-greenlet===3.1.1; python_version == '3.8'
-greenlet==3.2.3; python_version >= '3.9'
-grpcio===1.68.0; python_version == '3.8'
-grpcio==1.73.0; python_version >= '3.9'
-grpcio-status===1.68.0; python_version == '3.8'
-grpcio-status==1.73.0; python_version >= '3.9'
+greenlet==3.2.4; python_version >= '3.9'
+grpcio==1.75.1; python_version >= '3.9'
+grpcio-status==1.75.1; python_version >= '3.9'
idna==3.10
-importlib-resources===6.4.5; python_version == '3.8'
importlib-resources==6.5.2; python_version >= '3.9'
-mako===1.3.5; python_version == '3.8'
mako==1.3.10; python_version >= '3.9'
-markupsafe===2.1.5; python_version == '3.8'
-markupsafe==3.0.2; python_version >= '3.9'
+markupsafe==3.0.3; python_version >= '3.9'
packaging==25.0
proto-plus==1.26.1
-protobuf===5.28.3; python_version == '3.8'
-protobuf==6.31.1; python_version >= '3.9'
+protobuf==6.32.1; python_version >= '3.9'
pyasn1==0.6.1
pyasn1-modules==0.4.2
-pyparsing===3.1.4; python_version == '3.8'
-pyparsing==3.2.3; python_version >= '3.9'
+pyparsing==3.2.5; python_version >= '3.9'
python-dateutil==2.9.0.post0
pytz==2025.2
-requests==2.32.4
+requests==2.32.5
rsa==4.9.1
shapely===2.0.7; python_version <= '3.9'
-shapely==2.1.1; python_version >= '3.10'
+shapely==2.1.2; python_version >= '3.10'
six==1.17.0
sqlalchemy===1.4.27
-typing-extensions===4.13.0; python_version == '3.8'
-typing-extensions==4.14.0; python_version >= '3.9'
-urllib3===2.2.3; python_version == '3.8'
+typing-extensions==4.15.0; python_version >= '3.9'
urllib3==2.5.0; python_version >= '3.9'
diff --git a/setup.py b/setup.py
index 6d6c814a..fbc319bd 100644
--- a/setup.py
+++ b/setup.py
@@ -61,8 +61,9 @@ def readme():
# grpc.Channel.close() method isn't added until 1.32.0.
# https://github.com/grpc/grpc/pull/15254
"grpcio >= 1.47.0, < 2.0.0",
- "grpcio >= 1.49.1, < 2.0.0; python_version>='3.11'",
- "pyarrow >= 3.0.0",
+ "grpcio >= 1.49.1, < 2.0.0; python_version =='3.11'",
+ "grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'",
+ "pyarrow >= 5.0.0",
],
}
@@ -105,6 +106,7 @@ def readme():
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"Operating System :: OS Independent",
"Topic :: Database :: Front-Ends",
],
@@ -120,7 +122,7 @@ def readme():
"sqlalchemy>=1.4.16,<3.0.0",
],
extras_require=extras,
- python_requires=">=3.8, <3.14",
+ python_requires=">=3.8, <3.15",
tests_require=["packaging", "pytz"],
entry_points={
"sqlalchemy.dialects": ["bigquery = sqlalchemy_bigquery:BigQueryDialect"]
diff --git a/sqlalchemy_bigquery/__init__.py b/sqlalchemy_bigquery/__init__.py
index 1e506125..373c4861 100644
--- a/sqlalchemy_bigquery/__init__.py
+++ b/sqlalchemy_bigquery/__init__.py
@@ -48,14 +48,17 @@
from . import _versions_helpers
sys_major, sys_minor, sys_micro = _versions_helpers.extract_runtime_version()
-if sys_major == 3 and sys_minor in (7, 8):
+
+# Now that support for Python 3.7 and 3.8 has been removed, we don't expect the
+# following check to succeed. The warning is only included for robustness.
+if sys_major == 3 and sys_minor in (7, 8): # pragma: NO COVER
warnings.warn(
- "The python-bigquery library will stop supporting Python 3.7 "
- "and Python 3.8 in a future major release expected in Q4 2024. "
+ "The python-bigquery-sqlalchemy library no longer supports Python 3.7 "
+ "and Python 3.8. "
f"Your Python version is {sys_major}.{sys_minor}.{sys_micro}. We "
"recommend that you update soon to ensure ongoing support. For "
"more details, see: [Google Cloud Client Libraries Supported Python Versions policy](https://cloud.google.com/python/docs/supported-python-versions)",
- PendingDeprecationWarning,
+ FutureWarning,
)
diff --git a/sqlalchemy_bigquery/version.py b/sqlalchemy_bigquery/version.py
index 86059295..e76f81a9 100644
--- a/sqlalchemy_bigquery/version.py
+++ b/sqlalchemy_bigquery/version.py
@@ -17,4 +17,4 @@
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-__version__ = "1.15.0"
+__version__ = "1.16.0"
diff --git a/testing/constraints-3.14.txt b/testing/constraints-3.14.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt
deleted file mode 100644
index 667a747d..00000000
--- a/testing/constraints-3.8.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-# This constraints file is used to check that lower bounds
-# are correct in setup.py
-# List *all* library dependencies and extras in this file.
-# Pin the version to the lower bound.
-#
-# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev",
-sqlalchemy==1.4.16
-google-auth==1.25.0
-google-cloud-bigquery==3.3.6
-google-cloud-bigquery-storage==2.0.0
-google-api-core==1.31.5
-grpcio==1.47.0
-pyarrow==3.0.0
diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt
index e69de29b..a9840fe3 100644
--- a/testing/constraints-3.9.txt
+++ b/testing/constraints-3.9.txt
@@ -0,0 +1,13 @@
+# This constraints file is used to check that lower bounds
+# are correct in setup.py
+# List *all* library dependencies and extras in this file.
+# Pin the version to the lower bound.
+#
+# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev",
+sqlalchemy==1.4.16
+google-auth==1.25.0
+google-cloud-bigquery==3.3.6
+google-cloud-bigquery-storage==2.0.0
+google-api-core==1.31.5
+grpcio==1.47.0
+numpy==1.26.4
diff --git a/tests/sqlalchemy_dialect_compliance/test_dialect_compliance.py b/tests/sqlalchemy_dialect_compliance/test_dialect_compliance.py
index ff14db9a..6ac2ffa0 100644
--- a/tests/sqlalchemy_dialect_compliance/test_dialect_compliance.py
+++ b/tests/sqlalchemy_dialect_compliance/test_dialect_compliance.py
@@ -55,8 +55,9 @@
)
from sqlalchemy.testing.suite.test_reflection import (
- BizarroCharacterFKResolutionTest,
+ BizarroCharacterTest,
ComponentReflectionTest,
+ ComponentReflectionTestExtra,
HasTableTest,
)
@@ -615,6 +616,7 @@ def test_no_results_for_non_returning_insert(cls):
pass
+del ComponentReflectionTestExtra # Multiple tests re: CHECK CONSTRAINTS, etc which
del ComponentReflectionTest # Multiple tests re: CHECK CONSTRAINTS, etc which
# BQ does not support
# class ComponentReflectionTest(_ComponentReflectionTest):
@@ -629,7 +631,7 @@ def test_no_results_for_non_returning_insert(cls):
# pass
del ArrayTest # only appears to apply to postgresql
-del BizarroCharacterFKResolutionTest
+del BizarroCharacterTest
del HasTableTest.test_has_table_cache # TODO confirm whether BQ has table caching
del DistinctOnTest # expects unquoted table names.
del HasIndexTest # BQ doesn't do the indexes that SQLA is loooking for.