Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Add free-threaded CPython 3.13t to CI #12550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
Loading
from

Conversation

andfoy
Copy link

@andfoy andfoy commented Apr 22, 2025

Description

See #12513

This PR enables free-threaded CPython 3.13t as a supported target in the CI tests, as outlined in the discussion, this PR only tests sqlalchemy without any kind of parallelism, i.e., using pytest-run-parallel. Most of the changes are related to test skips when garbage collection timing or memory usage collection takes place, since the free-threaded distribution behaves differently from the GIL-enabled one on these aspects, otherwise, all tests are passing locally.

Checklist

This pull request is:

  • A documentation / typographical / small typing error fix
    • Good to go, no issue or tests are needed
  • A short code fix
    • please include the issue number, and create an issue if none exists, which
      must include a complete example of the issue. one line code fixes without an
      issue and demonstration will not be accepted.
    • Please include: Fixes: #<issue number> in the commit message
    • please include tests. one line code fixes without tests will not be accepted.
  • A new feature implementation
    • please include the issue number, and create an issue if none exists, which must
      include a complete example of how the feature would look.
    • Please include: Fixes: #<issue number> in the commit message
    • please include tests.

Have a nice day!

Copy link
Member

@zzzeek zzzeek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unclear if the tests run here? we've noted there's a lot not working with current python sqlite3 so how are you running the tests ?

@@ -1585,7 +1586,7 @@ def predictable_gc(self):
gc.collect() is called, as well as clean out unreferenced subclasses.

"""
return self.cpython
return self.cpython and not bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would make a new requirement (that is, a function inside this file) called gil_enabled and use that where needed

@@ -54,6 +56,11 @@
from ..orm import _fixtures


pytestmark = pytest.mark.skipif(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed, use gil_enabled req

@@ -1051,6 +1058,12 @@ def test_many_discarded_relationships(self):
"""a use case that really isn't supported, nonetheless we can
guard against memleaks here so why not"""

import sysconfig
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed, use gil_enabled req on test_many_discarded_relationships

e.g. for this one and the rest:

@testing.requires.gil_enabled
def test_many_discarded_relationships()

import sysconfig
import pytest

if bool(sysconfig.get_config_var("Py_GIL_DISABLED")):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed, use gil_enabled req

import sysconfig
import pytest

if bool(sysconfig.get_config_var("Py_GIL_DISABLED")):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed, use gil_enabled req

import sysconfig
import pytest

if bool(sysconfig.get_config_var("Py_GIL_DISABLED")):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed, use gil_enabled req

import sysconfig
import pytest

if bool(sysconfig.get_config_var("Py_GIL_DISABLED")):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed, use gil_enabled req

@andfoy
Copy link
Author

andfoy commented Apr 22, 2025

Thanks for the review @zzzeek, I've been running the tests (without any parallelism) using tox, sqlite issues arise when tests are run in parallel

@@ -134,6 +135,7 @@ jobs:
- "3.11"
- "3.12"
- "3.13"
- "3.13t"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think it's needed here

@property
def gil_enabled(self):
"""Test should run if the Python distribution is GIL-enabled."""
return bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the canonical way to check for this is with sys._is_gil_enabled().

@zzzeek zzzeek requested a review from sqla-tester May 24, 2025 14:31
Copy link
Collaborator

@sqla-tester sqla-tester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 7600297 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

New Gerrit review created for change 7600297: https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/5896

@zzzeek
Copy link
Member

zzzeek commented May 24, 2025

Random q

pytest-run-parallel, I'm enabling by using --parallel-threads=4 (confirming I'm using the correct plugin here)

If I just run some of our simplest suites, like the test/sql/test_query suite:

pytest test/sql/test_query.py  --parallel-threads=4

"60 tests were not run in parallel because of use of thread-unsafe functionality".

It can list out the tests that were parallel or not parallel, but it tells me nothing about what "thread unsafe functionality" is involved.

Maybe that's because of sqlite? OK so I tried running them against the psycopg2 driver instead, which is definitely threadsafe:

pytest test/sql/test_query.py  --parallel-threads=4 --db postgresql

same exact message:

"60 tests were not run in parallel because of use of thread-unsafe functionality, "

Shouldn't there be a way for the plugin to list out what functionality is thread-unsafe?

@zzzeek
Copy link
Member

zzzeek commented May 24, 2025

per https://github.com/Quansight-Labs/pytest-run-parallel ". If a test was not run in a thread pool because pytest-run-parallel detected use of thread-unsafe functionality, the reason will be printed as well."

 PYTEST_RUN_PARALLEL_VERBOSE=1 pytest test/sql/test_query.py  --parallel-threads=4 --db postgresql -v

full output is below, I see no reasons? am I doing something wrong?

$ PYTEST_RUN_PARALLEL_VERBOSE=1 pytest test/sql/test_query.py  --parallel-threads=4 --db postgresql -v
========================================================================================= sqlalchemy installation =========================================================================================
SQLAlchemy 2.1.0b1 (user site loaded)
Path: /home/classic/dev/sqlalchemy/lib/sqlalchemy/__init__.py
compiled extension not enabled; Modules sqlalchemy.util._collections_cy, sqlalchemy.util._immutabledict_cy, sqlalchemy.engine._processors_cy, sqlalchemy.engine._row_cy, sqlalchemy.engine._util_cy, sqlalchemy.sql._util_cy are not compiled
=========================================================================================== test session starts ===========================================================================================
platform linux -- Python 3.13.3, pytest-8.3.5, pluggy-1.5.0 -- /home/classic/.venv3/bin/python3
cachedir: .pytest_cache
rootdir: /home/classic/dev/sqlalchemy
configfile: pyproject.toml
plugins: xdist-3.6.1, anyio-4.9.0, random-0.2, run-parallel-0.4.2
collected 65 items                                                                                                                                                                                        
Collected 5 items to run in parallel

test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_composite_alias PASSED                                                                                                          [  1%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_except_style1 PASSED                                                                                                            [  3%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_except_style2 PASSED                                                                                                            [  4%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_except_style3 PASSED                                                                                                            [  6%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_except_style4 PASSED                                                                                                            [  7%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_intersect PASSED                                                                                                                [  9%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_intersect_unions PASSED                                                                                                         [ 10%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_intersect_unions_2 PASSED                                                                                                       [ 12%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_intersect_unions_3 PASSED                                                                                                       [ 13%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union PASSED                                                                                                                    [ 15%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union_all PASSED                                                                                                                [ 16%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union_all_lightweight PASSED                                                                                                    [ 18%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union_ordered PASSED                                                                                                            [ 20%]
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union_ordered_alias PASSED                                                                                                      [ 21%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_join_x1 PASSED                                                                                                                      [ 23%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_join_x2 PASSED                                                                                                                      [ 24%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_mixed PASSED                                                                                                                        [ 26%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_mixed_where PASSED                                                                                                                  [ 27%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t1 PASSED                                                                                                        [ 29%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t1t2 PASSED                                                                                                      [ 30%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t1t2t3 PASSED                                                                                                    [ 32%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t1t3 PASSED                                                                                                      [ 33%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t2 PASSED                                                                                                        [ 35%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t3 PASSED                                                                                                        [ 36%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_x1 PASSED                                                                                                                 [ 38%]
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_x2 PASSED                                                                                                                 [ 40%]
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_distinct_limit PASSED                                                                                                       [ 41%]
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_distinct_limit_offset PASSED                                                                                                [ 43%]
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_distinct_offset PASSED                                                                                                      [ 44%]
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_limit PASSED                                                                                                                [ 46%]
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_limit_offset PASSED                                                                                                         [ 47%]
test/sql/test_query.py::OperatorTest_postgresql+psycopg2_16_8::test_modulo PASSED                                                                                                                   [ 49%]
test/sql/test_query.py::OperatorTest_postgresql+psycopg2_16_8::test_over PASSED                                                                                                                     [ 50%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_bind_in PASSED                                                                                                                     [ 52%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_bindparam_detection PASSED                                                                                                         [ 53%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_compiled_execute PASSED                                                                                                            [ 55%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_compiled_insert_execute PASSED                                                                                                     [ 56%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_empty_in_filtering_static PASSED                                                                                                   [ 58%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in PASSED                                                                                                                [ 60%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_composite PASSED                                                                                                      [ 61%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_dont_alter_compiled PASSED                                                                                            [ 63%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_multiple PASSED                                                                                                       [ 64%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_repeated PASSED                                                                                                       [ 66%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_special_chars PASSED                                                                                                  [ 67%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_ilike PASSED                                                                                                                       [ 69%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_in_filtering PASSED                                                                                                                [ 70%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_like_ops[<lambda>-result0] PASSED                                                                                                  [ 72%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_like_ops[<lambda>-result1] PASSED                                                                                                  [ 73%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_like_ops[<lambda>-result2] PASSED                                                                                                  [ 75%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_like_ops[<lambda>-result3] PASSED                                                                                                  [ 76%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_literal_in PASSED                                                                                                                  [ 78%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_or_and_as_columns PASSED                                                                                                           [ 80%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_order_by PASSED                                                                                                                    [ 81%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_order_by_label PASSED                                                                                                              [ 83%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_order_by_label_compound PASSED                                                                                                     [ 84%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_order_by_nulls PASSED                                                                                                              [ 86%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_percents_in_text PASSED                                                                                                            [ 87%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_repeated_bindparams PASSED                                                                                                         [ 89%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_select_from_bindparam PASSED                                                                                                       [ 90%]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_select_tuple PASSED                                                                                                                [ 92%]
test/sql/test_query.py::RequiredBindTest::test_insert PARALLEL PASSED                                                                                                                               [ 93%]
test/sql/test_query.py::RequiredBindTest::test_required_flag PARALLEL PASSED                                                                                                                        [ 95%]
test/sql/test_query.py::RequiredBindTest::test_select_columns PARALLEL PASSED                                                                                                                       [ 96%]
test/sql/test_query.py::RequiredBindTest::test_select_where PARALLEL PASSED                                                                                                                         [ 98%]
test/sql/test_query.py::RequiredBindTest::test_text PARALLEL PASSED                                                                                                                                 [100%]

*************************************************************************************** pytest-run-parallel report ****************************************************************************************
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_composite_alias
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_except_style1
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_except_style2
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_except_style3
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_except_style4
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_intersect
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_intersect_unions
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_intersect_unions_2
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_intersect_unions_3
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union_all
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union_all_lightweight
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union_ordered
test/sql/test_query.py::CompoundTest_postgresql+psycopg2_16_8::test_union_ordered_alias
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_join_x1
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_join_x2
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_mixed
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_mixed_where
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t1
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t1t2
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t1t2t3
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t1t3
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t2
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_where_x2_t3
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_x1
test/sql/test_query.py::JoinTest_postgresql+psycopg2_16_8::test_outerjoin_x2
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_distinct_limit
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_distinct_limit_offset
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_distinct_offset
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_limit
test/sql/test_query.py::LimitTest_postgresql+psycopg2_16_8::test_select_limit_offset
test/sql/test_query.py::OperatorTest_postgresql+psycopg2_16_8::test_modulo
test/sql/test_query.py::OperatorTest_postgresql+psycopg2_16_8::test_over
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_bind_in
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_bindparam_detection
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_compiled_execute
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_compiled_insert_execute
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_empty_in_filtering_static
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_composite
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_dont_alter_compiled
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_multiple
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_repeated
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_expanding_in_special_chars
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_ilike
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_in_filtering
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_like_ops[<lambda>-result0]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_like_ops[<lambda>-result1]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_like_ops[<lambda>-result2]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_like_ops[<lambda>-result3]
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_literal_in
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_or_and_as_columns
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_order_by
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_order_by_label
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_order_by_label_compound
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_order_by_nulls
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_percents_in_text
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_repeated_bindparams
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_select_from_bindparam
test/sql/test_query.py::QueryTest_postgresql+psycopg2_16_8::test_select_tuple
=========================================================================================== 65 passed in 0.78s ===============================================================================

@andfoy
Copy link
Author

andfoy commented May 26, 2025

@zzzeek thanks for the follow-up, I might need to take a look to the test, maybe there's a function that is declared as thread-unsafe and it's not being reported?

@zzzeek
Copy link
Member

zzzeek commented May 26, 2025

@zzzeek thanks for the follow-up, I might need to take a look to the test, maybe there's a function that is declared as thread-unsafe and it's not being reported?

I think I was running the tests just on main. The pytest parallel plugin ran the tests, made the decision to not parallelize a bunch, and did not express any messaging why it made this decision, in contradiction to what the docs say. So this seems like a bug in pytest parallel. If the tool is going to make implicit decisions, it should log the origin of these decisions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.