Skip to content
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

Support cursor.execute(psycopg2.sql.Composable) #1029

Merged
merged 1 commit into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion django-stubs/db/backends/postgresql/base.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ from typing import Any, Dict, Tuple, Type

from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.utils import CursorDebugWrapper as BaseCursorDebugWrapper
from django.db.backends.utils import _ExecuteQuery

from .client import DatabaseClient
from .creation import DatabaseCreation
Expand Down Expand Up @@ -37,5 +38,5 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def pg_version(self) -> int: ...

class CursorDebugWrapper(BaseCursorDebugWrapper):
def copy_expert(self, sql: str, file: IOBase, *args: Any): ...
def copy_expert(self, sql: _ExecuteQuery, file: IOBase, *args: Any): ...
def copy_to(self, file: IOBase, table: str, *args: Any, **kwargs: Any): ...
28 changes: 25 additions & 3 deletions django-stubs/db/backends/utils.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,21 @@ import types
from contextlib import contextmanager
from decimal import Decimal
from logging import Logger
from typing import Any, Dict, Generator, Iterator, List, Mapping, Optional, Sequence, Tuple, Type, Union, overload
from typing import (
Any,
Dict,
Generator,
Iterator,
List,
Mapping,
Optional,
Protocol,
Sequence,
Tuple,
Type,
Union,
overload,
)
from uuid import UUID

if sys.version_info < (3, 8):
Expand All @@ -14,6 +28,14 @@ else:

logger: Logger

# Protocol matching psycopg2.sql.Composable, to avoid depending psycopg2
class _Composable(Protocol):
Copy link
Member

@sobolevn sobolevn Jun 28, 2022

Choose a reason for hiding this comment

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

Can we depend on types-psycopg2? What do you think?
Refs #114

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A risk in that approach is that typeshed packages are obsoleted when the corresponding upstream packages add type annotations. In fact, psycopg3 (psycopg on PyPI) already has upstream type annotations, and we’ll want to support its version of Composable, which satisfies the same protocol.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, thanks!

def as_string(self, context: Any) -> str: ...
def __add__(self, other: _Composable) -> _Composable: ...
def __mul__(self, n: int) -> _Composable: ...

_ExecuteQuery = Union[str, _Composable]

# Python types that can be adapted to SQL.
_SQLType = Union[
None, bool, int, float, Decimal, str, bytes, datetime.date, datetime.datetime, UUID, Tuple[Any, ...], List[Any]
Expand All @@ -37,8 +59,8 @@ class CursorWrapper:
def callproc(
self, procname: str, params: Optional[Sequence[Any]] = ..., kparams: Optional[Dict[str, int]] = ...
) -> Any: ...
def execute(self, sql: str, params: _ExecuteParameters = ...) -> Any: ...
def executemany(self, sql: str, param_list: Sequence[_ExecuteParameters]) -> Any: ...
def execute(self, sql: _ExecuteQuery, params: _ExecuteParameters = ...) -> Any: ...
def executemany(self, sql: _ExecuteQuery, param_list: Sequence[_ExecuteParameters]) -> Any: ...

class CursorDebugWrapper(CursorWrapper):
cursor: Any
Expand Down
10 changes: 10 additions & 0 deletions tests/typecheck/db/test_connection.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
with connection.cursor() as cursor:
reveal_type(cursor) # N: Revealed type is "django.db.backends.utils.CursorWrapper"
cursor.execute("SELECT %s", [123])


- case: raw_connection_psycopg2_composable
andersk marked this conversation as resolved.
Show resolved Hide resolved
main: |
from django.db import connection
from psycopg2.sql import SQL, Identifier
with connection.cursor() as cursor:
cursor.execute(SQL("INSERT INTO {} VALUES (%s)").format(Identifier("my_table")), [123])


- case: raw_connections
main: |
from django.db import connections
Expand Down