]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
it got slower
authorabebus <anamaev263@gmail.com>
Wed, 15 Oct 2025 22:31:18 +0000 (01:31 +0300)
committerabebus <anamaev263@gmail.com>
Wed, 15 Oct 2025 22:31:18 +0000 (01:31 +0300)
lib/sqlalchemy/engine/_row_cy.py
lib/sqlalchemy/util/cython.py

index 87cf5bfa39ce94a139f2478c4d6076a5939b111b..e550710821f5c6e39341db032fd751d2c37544d3 100644 (file)
@@ -41,9 +41,28 @@ def _is_compiled() -> bool:
 # END GENERATED CYTHON IMPORT
 
 
+if not cython.compiled:
+
+    def PyTuple_New(n):  # type: ignore
+        # Actually produces list if not compiled
+        return [None] * n
+
+    def PyTuple_SET_ITEM(tup, idx, item):  # type: ignore
+        tup[idx] = item
+
+    PySequence_Fast_GET_SIZE = len
+    Py_INCREF = cython._no_op
+else:
+    from cython.cimports.cpython import PyTuple_New
+    from cython.cimports.cpython import Py_INCREF
+    from cython.cimports.cpython import PyTuple_SET_ITEM
+    from cython.cimports.cpython import PySequence_Fast_GET_SIZE
+
+
 @cython.cclass
 class BaseRow:
-    __slots__ = ("_parent", "_data", "_key_to_index")
+    if not cython.compiled:
+        __slots__ = ("_parent", "_data", "_key_to_index")
 
     if cython.compiled:
         _parent: ResultMetaData = cython.declare(object, visibility="readonly")
@@ -61,12 +80,15 @@ class BaseRow:
     ) -> None:
         """Row objects are constructed by CursorResult objects."""
 
-        data_tuple: Tuple[Any, ...] = (
-            _apply_processors(processors, data)
-            if processors is not None
-            else tuple(data)
+        self._set_attrs(
+            parent,
+            key_to_index,
+            (
+                _apply_processors(processors, data)
+                if processors is not None
+                else tuple(data)
+            ),
         )
-        self._set_attrs(parent, key_to_index, data_tuple)
 
     @cython.cfunc
     @cython.inline
@@ -112,7 +134,7 @@ class BaseRow:
     def __hash__(self) -> int:
         return hash(self._data)
 
-    if not TYPE_CHECKING:
+    if not TYPE_CHECKING or cython.compiled:
 
         def __getitem__(self, key: Any) -> Any:
             return self._data[key]
@@ -121,13 +143,13 @@ class BaseRow:
         return self._get_by_key_impl(key, False)
 
     @cython.cfunc
+    @cython.inline
     def _get_by_key_impl(self, key: _KeyType, attr_err: cython.bint) -> object:
         index: Optional[int] = self._key_to_index.get(key)
         if index is not None:
             return self._data[index]
         self._parent._key_not_found(key, attr_err)
 
-    @cython.annotation_typing(False)
     def __getattr__(self, name: str) -> Any:
         return self._get_by_key_impl(name, True)
 
@@ -135,27 +157,50 @@ class BaseRow:
         return self._data
 
 
-@cython.inline
-@cython.cfunc
-def _apply_processors(
-    proc: _ProcessorsType, data: Sequence[Any]
-) -> Tuple[Any, ...]:
-    res: List[Any] = list(data)
-    proc_size: cython.Py_ssize_t = len(proc)
-    # TODO: would be nice to do this only on the fist row
-    assert len(res) == proc_size
-    for i in range(proc_size):
-        p = proc[i]
-        if p is not None:
-            res[i] = p(res[i])
-    return tuple(res)
+if cython.compiled:
+
+    @cython.inline
+    @cython.cfunc
+    @cython.wraparound(False)
+    @cython.boundscheck(False)
+    @cython.returns(tuple)
+    @cython.locals(res=tuple, proc_size=cython.Py_ssize_t, p=object)
+    def _apply_processors(proc: object, data: object) -> Tuple[Any, ...]:
+        proc_size = PySequence_Fast_GET_SIZE(proc)
+        # TODO: would be nice to do this only on the fist row
+        assert PySequence_Fast_GET_SIZE(data) == proc_size
+        res = PyTuple_New(proc_size)
+        for i in range(proc_size):
+            p = proc[i]
+            if p is not None:
+                PyTuple_SET_ITEM(res, i, Py_INCREF(p(data[i])))
+            else:
+                PyTuple_SET_ITEM(res, i, Py_INCREF(data[i]))
+        return res
+
+else:
+
+    def _apply_processors(
+        proc: _ProcessorsType, data: Sequence[Any]
+    ) -> Tuple[Any, ...]:
+        res: List[Any] = list(data)
+        proc_size = len(proc)
+        # TODO: would be nice to do this only on the fist row
+        assert len(res) == proc_size
+        for i in range(proc_size):
+            p = proc[i]
+            if p is not None:
+                res[i] = p(res[i])
+        return tuple(res)
 
 
 # This reconstructor is necessary so that pickles with the Cy extension or
 # without use the same Binary format.
 # Turn off annotation typing so the compiled version accepts the python
 # class too.
-@cython.annotation_typing(False)
+# @cython.annotation_typing(False)
+@cython.inline
+@cython.cfunc
 def rowproxy_reconstructor(
     cls: Type[BaseRow], state: Dict[str, Any]
 ) -> BaseRow:
index 0d796313d84fa1a12828d3e4afa32f80f7eec993..56ba0bf7b1b4af4825806d09fb9c91bfac60b9e3 100644 (file)
@@ -30,6 +30,7 @@ uint = int
 float = float  # noqa: A001
 double = float
 void = Any
+NULL = None
 
 
 # functions
@@ -59,3 +60,14 @@ def exceptval(value: Any = None, *, check: bool = False) -> _NO_OP[_T]:
 
 def cast(type_: Type[_T], value: Any, *, typecheck: bool = False) -> _T:
     return value  # type: ignore[no-any-return]
+
+
+def returns(_: type) -> _NO_OP[_T]:
+    return _no_op
+
+
+def locals(**kwargs: Any) -> _NO_OP[_T]:  # noqa: A001
+    return _no_op
+
+
+boundscheck = wraparound = annotation_typing