Decimal object warning without using Decimal objects

576 views
Skip to first unread message

phix...@gmail.com

unread,
Jun 22, 2017, 9:18:31 PM6/22/17
to sqlalchemy
Hello,

I get the warning message "SAWarning: Dialect sqlite+pysqlite does *not* support Decimal objects natively, and SQLAlchemy must convert from floating point", without me using a Decimal object anywhere, which was highly confusing.

This occurs when evaluating a hybrid property multiplying an sqltypes.Integer column with a python float literal (0.5), which I would expect to result in a float.
It seems to be caused by two weird looking definitions in sqltypes.py:

class Numeric(_DateAffinity, TypeEngine): ...
def __init__(self, ..., asdecimal=True): ...

_type_map = {...
float: Numeric(), ...

Is there a reason, why floats are treated as decimals (instead of binary floating points?) by default, and a way to prevent this from happening when defining the following property?

@
my_hybrid_property.expression
def my_hybrid_property(cls):
return cls.my_integer_column * 0.5

Greetings,
Phillip Kuhrt

mike bayer

unread,
Jun 22, 2017, 11:25:41 PM6/22/17
to sqlal...@googlegroups.com


On 06/22/2017 09:18 PM, phix...@gmail.com wrote:
> Hello,
>
> I get the warning message "SAWarning: Dialect sqlite+pysqlite does *not*
> support Decimal objects natively, and SQLAlchemy must convert from
> floating point", without me using a Decimal object anywhere, which was
> highly confusing.
>
> This occurs when evaluating a hybrid property multiplying an
> sqltypes.Integer column with a python float literal (0.5), which I would
> expect to result in a float.
> It seems to be caused by two weird looking definitions in sqltypes.py:
>
> class Numeric(_DateAffinity, TypeEngine): ...
> def __init__(self, ..., asdecimal=True): ...
>
>
> _type_map = {...
> float: Numeric(), ...
>
>
> Is there a reason, why floats are treated as decimals (instead of binary
> floating points?) by default,

looks like ancient history, where there was only "float: Numeric()", and
then somewhere around the 0.5 series we started also accommodating
Python Decimal objects, so "Decimal: Numeric()" was added, and I didn't
trace it out completely but it's likely that the strict "asdecimal" /
"asfloat" separation evolved later than that. Earlier, it was just
whatever the driver did.

So the float coercion there never changed.


It should be a Float() so
https://bitbucket.org/zzzeek/sqlalchemy/issues/4017/float-should-coerce-to-float
is added.

and a way to prevent this from happening
> when defining the following property?
>
>
> @my_hybrid_property.expression
> def my_hybrid_property(cls):
> return cls.my_integer_column * 0.5

you can set the type explicitly:

from sqlalchemy import literal, Float
return my_col * literal(0.5, Float())



>
>
> Greetings,
>
> Phillip Kuhrt
>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> ---
> You received this message because you are subscribed to the Google
> Groups "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to sqlalchemy+...@googlegroups.com
> <mailto:sqlalchemy+...@googlegroups.com>.
> To post to this group, send email to sqlal...@googlegroups.com
> <mailto:sqlal...@googlegroups.com>.
> Visit this group at https://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.

phix...@gmail.com

unread,
Jun 24, 2017, 9:44:08 PM6/24/17
to sqlalchemy


Am Freitag, 23. Juni 2017 05:25:41 UTC+2 schrieb Mike Bayer:


return my_col * literal(0.5, Float())

Hello Mike,
 
thank you for the quick response.

This doesn't seem to fully work when querying the value using session.query(MyObject.my_hybrid_expression).scalar(). From the callstack, the Numeric (possibly representing the result of the multiplication) now seems to be initialized by type_api.py:to_instance, which calls the Numeric constructor without passing any **kwargs, so it again defaults to asdecimal=True. This method is called by sqltypes.py:_DateAffinity.Comparator._adapt_expression, which finds MyFloat._type_affinity == Numeric.

Greetings,
Phillip

Mike Bayer

unread,
Jun 25, 2017, 12:11:36 PM6/25/17
to sqlal...@googlegroups.com


On 06/24/2017 09:44 PM, phix...@gmail.com wrote:
>
>
> Am Freitag, 23. Juni 2017 05:25:41 UTC+2 schrieb Mike Bayer:
>
>
>
> return my_col * literal(0.5, Float())
>
>
> Hello Mike,
> thank you for the quick response.
>
> This doesn't seem to fully work when querying the value using
> session.query(MyObject.my_hybrid_expression).scalar().

https://bitbucket.org/zzzeek/sqlalchemy/issues/4018/maintaining-float-type-affinity
is added as a proposal to use something more specific than type affinity
for this match however as always, these are all convenience behaviors
that are tunable when they fail to produce the desired effect; you can
control the exact type of any expression using type_coerce() or cast():

from sqlalchemy import Float, Integer, literal, Numeric, type_coerce
expr = literal(5, Integer) * literal(4.5, Float)

expr = type_coerce(expr, Numeric(asdecimal=False))

assert expr.type.asdecimal is False



From the
> callstack, the Numeric (possibly representing the result of the
> multiplication) now seems to be initialized by type_api.py:to_instance,
> which calls the Numeric constructor without passing any **kwargs, so it
> again defaults to asdecimal=True. This method is called by
> sqltypes.py:_DateAffinity.Comparator._adapt_expression, which finds
> MyFloat._type_affinity == Numeric.
>
> Greetings,
> Phillip
>
Reply all
Reply to author
Forward
0 new messages