nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: djrobstep/sqlakeyset
Pull request #83 (2023-04-08 20:27:10)

Unserialize returns tuple instead of list.
Returning a list is incompatible with this type check in paging.py:
`if place is not None and not isinstance(place, tuple)`

acarapetis: Just a heads-up: The intended public API is `sqlakeyset.results.unserialize_bookmark` (or just passing the bookmark string to t`select_page` directly), which converts the output of `Serial.unserialize_values` to a tuple. 

I still think this is a good change - it makes sense to be consistent about using tuples for keysets. Thanks for your contribution.

Patch:
diff --git a/sqlakeyset/serial/serial.py b/sqlakeyset/serial/serial.py
index 9600bdf..0b90b1b 100644
--- a/sqlakeyset/serial/serial.py
+++ b/sqlakeyset/serial/serial.py
@@ -143,10 +143,10 @@ class Serial(object):
             return ""
         return self.join(self.serialize_value(_) for _ in values)
 
-    def unserialize_values(self, s: str) -> Optional[List]:
+    def unserialize_values(self, s: str) -> Optional[Tuple]:
         if s == "":
             return None
-        return [self.unserialize_value(_) for _ in self.split(s)]
+        return tuple(self.unserialize_value(_) for _ in self.split(s))
 
     def serialize_value(self, x) -> str:
         try:


Test patch:
diff --git a/tests/test_ser.py b/tests/test_ser.py
index 8f539f3..eec8dc3 100644
--- a/tests/test_ser.py
+++ b/tests/test_ser.py
@@ -161,7 +161,7 @@ def test_serial_row():
 
 def test_unserial_row():
     def twoway(x):
-        assert s.unserialize_values(s.serialize_values(x)) == x
+        assert s.unserialize_values(s.serialize_values(x)) == tuple(x)
 
     twoway([None, True])
     twoway(["hello", "world", 13])
Repository: djrobstep/sqlakeyset
Pull request #111 (2024-08-21 00:00:11)

Automatically .unique() sa2 ORM joinedload results
I'm hoping this will work for #110. Works for the simpler case I've added to the tests, anyway.


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 021960e..7e35075 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -106,7 +106,7 @@ workflows:
           matrix:
             parameters:
               python: ["3.7", "3.8", "3.9", "3.10", "3.11"]
-              sqlalchemy: ["1.3.24", "1.4.49", "2.0.0", "2.0.23"]
+              sqlalchemy: ["1.3.24", "1.4.49", "2.0.0", "2.0.31"]
       - publish:
           requires:
             - build
diff --git a/sqlakeyset/paging.py b/sqlakeyset/paging.py
index f36e36f..8bb4e3d 100644
--- a/sqlakeyset/paging.py
+++ b/sqlakeyset/paging.py
@@ -295,6 +295,11 @@ def core_get_page(
         dialect=get_bind(q=selectable, s=s).dialect,
     )
     selected = s.execute(sel.select)
+    # NOTE: This feels quite brittle, but is the best way I could think of to detect
+    # results that need .unique() called on them.
+    state = getattr(selected, "_unique_filter_state", None)
+    if isinstance(state, tuple) and len(state) > 0 and state[0] is None:
+        selected = selected.unique()
     keys = list(selected.keys())
     N = len(keys) - len(sel.extra_columns)
     keys = keys[:N]


Test patch:
diff --git a/tests/test_paging.py b/tests/test_paging.py
index 89485b0..9a5753a 100644
--- a/tests/test_paging.py
+++ b/tests/test_paging.py
@@ -95,14 +95,14 @@ def check_paging_orm(q):
             assert gathered == unpaged
 
 
-def check_paging_core(selectable, s):
+def check_paging_core(selectable, s, unique=False):
     item_counts = range(1, 12)
 
     if isinstance(s, Session):
         result = s.execute(selectable)
     else:
         result = Session(bind=s).execute(selectable)
-    unpaged = result.fetchall()
+    unpaged = result.unique().fetchall() if unique else result.fetchall()
 
     for backwards in [False, True]:
         for per_page in item_counts:
diff --git a/tests/test_paging_20_style.py b/tests/test_paging_20_style.py
index b86d93b..93fbea4 100644
--- a/tests/test_paging_20_style.py
+++ b/tests/test_paging_20_style.py
@@ -2,7 +2,7 @@
 import pytest
 from packaging import version
 from sqlalchemy import desc, func, select
-from sqlalchemy.orm import aliased
+from sqlalchemy.orm import aliased, joinedload, selectinload
 from conftest import (
     ECHO,
     SQLA_VERSION,
@@ -186,3 +186,15 @@ def test_new_orm_query_using_connection(dburl):
     with S(dburl, echo=ECHO) as s:
         q = select(Book).order_by(Book.id, Book.name)
         check_paging_core(q, s.connection())
+
+
+def test_new_orm_selectinload(dburl):
+    with S(dburl, echo=ECHO) as s:
+        q = select(Author).options(selectinload(Author.books)).order_by(Author.name, Author.id)
+        check_paging_core(q, s)
+
+
+def test_new_orm_joinedload(dburl):
+    with S(dburl, echo=ECHO) as s:
+        q = select(Author).options(joinedload(Author.books)).order_by(Author.name, Author.id)
+        check_paging_core(q, s, unique=True)