From 64f469139eb9c65d973dda9030d71462914e6a19 Mon Sep 17 00:00:00 2001 From: Pratik Date: Mon, 27 Oct 2025 10:36:06 +0530 Subject: [PATCH 1/3] refactor(packaging): Use SQLAlchemy event for Filename registry (Fixes #576) --- warehouse/forklift/legacy.py | 4 ---- warehouse/packaging/models.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/warehouse/forklift/legacy.py b/warehouse/forklift/legacy.py index a21ccab42195..4fecd804f01e 100644 --- a/warehouse/forklift/legacy.py +++ b/warehouse/forklift/legacy.py @@ -1530,10 +1530,6 @@ def file_upload(request): f"Invalid attestations supplied during upload: {e}", ) - # TODO: This should be handled by some sort of database trigger or a - # SQLAlchemy hook or the like instead of doing it inline in this - # view. - request.db.add(Filename(filename=filename)) # Store the information about the file in the database. file_ = File( diff --git a/warehouse/packaging/models.py b/warehouse/packaging/models.py index 3f1b209e0722..4657831ef741 100644 --- a/warehouse/packaging/models.py +++ b/warehouse/packaging/models.py @@ -30,6 +30,7 @@ orm, select, sql, + event, ) from sqlalchemy.dialects.postgresql import ( ARRAY, @@ -1207,3 +1208,17 @@ class AlternateRepository(db.Model): name: Mapped[str] url: Mapped[str] description: Mapped[str] + +@event.listens_for(File, "after_insert") +def add_filename_to_registry(mapper, connection, target): + """ + Listen for a new File object being inserted and add its filename to the + Filename registry. + """ + # Use a direct connection-level insert, + # This avoids using session.add() during the flush process. + # The 'target' is the File object that was just inserted. + connection.execute( + Filename.__table__.insert(), + {"filename": target.filename} + ) \ No newline at end of file From 00e2ade581686131b30430ae584ba950037d6347 Mon Sep 17 00:00:00 2001 From: Pratik Date: Mon, 27 Oct 2025 10:38:24 +0530 Subject: [PATCH 2/3] refactor(packaging): Use SQLAlchemy event for Filename registry (Fixes #576) --- warehouse/packaging/models.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/warehouse/packaging/models.py b/warehouse/packaging/models.py index 4657831ef741..e2893c98185e 100644 --- a/warehouse/packaging/models.py +++ b/warehouse/packaging/models.py @@ -1212,12 +1212,17 @@ class AlternateRepository(db.Model): @event.listens_for(File, "after_insert") def add_filename_to_registry(mapper, connection, target): """ - Listen for a new File object being inserted and add its filename to the - Filename registry. + Log the new filename to the Filename (file_registry) table. + + This event listener is triggered *after* a new `File` object is + successfully inserted into the database. + + We use a direct connection-level insert (`connection.execute()`) + instead of `session.add(Filename(...))` to avoid an `SAWarning`. + Modifying the session *during* the flush process (which is when + this hook runs) is not a supported operation. This method + bypasses the session's unit-of-work tracking and is safe here. """ - # Use a direct connection-level insert, - # This avoids using session.add() during the flush process. - # The 'target' is the File object that was just inserted. connection.execute( Filename.__table__.insert(), {"filename": target.filename} From 7853a8cca266e261d7b3e9cfaef43040b2cbeb54 Mon Sep 17 00:00:00 2001 From: Pratik Date: Mon, 27 Oct 2025 10:43:19 +0530 Subject: [PATCH 3/3] refactor(packaging): Use SQLAlchemy event for Filename registry (Fixes #576) --- warehouse/forklift/legacy.py | 1 - warehouse/packaging/models.py | 14 ++++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/warehouse/forklift/legacy.py b/warehouse/forklift/legacy.py index 4fecd804f01e..7c02a7bd1257 100644 --- a/warehouse/forklift/legacy.py +++ b/warehouse/forklift/legacy.py @@ -1530,7 +1530,6 @@ def file_upload(request): f"Invalid attestations supplied during upload: {e}", ) - # Store the information about the file in the database. file_ = File( release=release, diff --git a/warehouse/packaging/models.py b/warehouse/packaging/models.py index e2893c98185e..989860fda907 100644 --- a/warehouse/packaging/models.py +++ b/warehouse/packaging/models.py @@ -25,12 +25,12 @@ Text, UniqueConstraint, cast, + event, func, or_, orm, select, sql, - event, ) from sqlalchemy.dialects.postgresql import ( ARRAY, @@ -1209,6 +1209,7 @@ class AlternateRepository(db.Model): url: Mapped[str] description: Mapped[str] + @event.listens_for(File, "after_insert") def add_filename_to_registry(mapper, connection, target): """ @@ -1218,12 +1219,9 @@ def add_filename_to_registry(mapper, connection, target): successfully inserted into the database. We use a direct connection-level insert (`connection.execute()`) - instead of `session.add(Filename(...))` to avoid an `SAWarning`. - Modifying the session *during* the flush process (which is when - this hook runs) is not a supported operation. This method + instead of `session.add(Filename(...))` to avoid an `SAWarning`. + Modifying the session *during* the flush process (which is when + this hook runs) is not a supported operation. This method bypasses the session's unit-of-work tracking and is safe here. """ - connection.execute( - Filename.__table__.insert(), - {"filename": target.filename} - ) \ No newline at end of file + connection.execute(Filename.__table__.insert(), {"filename": target.filename})