--- /dev/null
+.. change::
+ :tags: bug, typing
+ :tickets: 9391
+
+ Added missing init overload to :class:`_sql.Numeric` to allow
+ type checkers to properly resolve the type var given the
+ ``asdecimal`` parameter.
from __future__ import annotations
import datetime
+import decimal
from typing import Any
from typing import cast
from typing import Dict
from .selectable import FromClause
from .selectable import Select
from .selectable import TableValuedAlias
-from .sqltypes import _N
from .sqltypes import TableValueType
from .type_api import TypeEngine
from .visitors import InternalTraversal
...
@property
- def cume_dist(self) -> Type[cume_dist[Any]]:
+ def cume_dist(self) -> Type[cume_dist]:
...
@property
...
@property
- def percent_rank(self) -> Type[percent_rank[Any]]:
+ def percent_rank(self) -> Type[percent_rank]:
...
@property
inherit_cache = True
-class percent_rank(GenericFunction[_N]):
+class percent_rank(GenericFunction[decimal.Decimal]):
"""Implement the ``percent_rank`` hypothetical-set aggregate function.
This function must be used with the :meth:`.FunctionElement.within_group`
"""
- type: sqltypes.Numeric[_N] = sqltypes.Numeric()
+ type: sqltypes.Numeric[decimal.Decimal] = sqltypes.Numeric()
inherit_cache = True
-class cume_dist(GenericFunction[_N]):
+class cume_dist(GenericFunction[decimal.Decimal]):
"""Implement the ``cume_dist`` hypothetical-set aggregate function.
This function must be used with the :meth:`.FunctionElement.within_group`
"""
- type: sqltypes.Numeric[_N] = sqltypes.Numeric()
+ type: sqltypes.Numeric[decimal.Decimal] = sqltypes.Numeric()
inherit_cache = True
_default_decimal_return_scale = 10
+ @overload
+ def __init__(
+ self: Numeric[decimal.Decimal],
+ precision: Optional[int] = ...,
+ scale: Optional[int] = ...,
+ decimal_return_scale: Optional[int] = ...,
+ asdecimal: Literal[True] = ...,
+ ):
+ ...
+
+ @overload
+ def __init__(
+ self: Numeric[float],
+ precision: Optional[int] = ...,
+ scale: Optional[int] = ...,
+ decimal_return_scale: Optional[int] = ...,
+ asdecimal: Literal[False] = ...,
+ ):
+ ...
+
def __init__(
self,
precision: Optional[int] = None,
stmt4 = select(func.cume_dist())
-# EXPECTED_RE_TYPE: .*Select\[Any\]
+# EXPECTED_RE_TYPE: .*Select\[Tuple\[.*Decimal\]\]
reveal_type(stmt4)
stmt14 = select(func.percent_rank())
-# EXPECTED_RE_TYPE: .*Select\[Any\]
+# EXPECTED_RE_TYPE: .*Select\[Tuple\[.*Decimal\]\]
reveal_type(stmt14)
--- /dev/null
+from sqlalchemy import Float
+from sqlalchemy import Numeric
+
+# EXPECTED_TYPE: Float[float]
+reveal_type(Float())
+# EXPECTED_TYPE: Float[Decimal]
+reveal_type(Float(asdecimal=True))
+
+# EXPECTED_TYPE: Numeric[Decimal]
+reveal_type(Numeric())
+# EXPECTED_TYPE: Numeric[float]
+reveal_type(Numeric(asdecimal=False))
from __future__ import annotations
-from decimal import Decimal
import inspect
import re
from tempfile import NamedTemporaryFile
import textwrap
-from typing import Any
from sqlalchemy.sql.functions import _registry
from sqlalchemy.types import TypeEngine
fn_class.type, TypeEngine
):
python_type = fn_class.type.python_type
-
- # TODO: numeric types don't seem to be coming out
- # at the moment, because Numeric is typed generically
- # in that it can return Decimal or float. We would need
- # to further break out Numeric / Float into types
- # that type out as returning an exact Decimal or float
- if python_type is Decimal:
- python_type = Any
- python_expr = f"{python_type.__name__}"
- else:
- python_expr = rf"Tuple\[.*{python_type.__name__}\]"
+ python_expr = rf"Tuple\[.*{python_type.__name__}\]"
argspec = inspect.getfullargspec(fn_class)
args = ", ".join(
'column("x")' for elem in argspec.args[1:]