Merge "Coerce float Python type to Float; ensure Python float coming back"

This commit is contained in:
mike bayer
2017-06-26 11:56:38 -04:00
committed by Gerrit Code Review
5 changed files with 75 additions and 1 deletions
+14
View File
@@ -13,6 +13,20 @@
.. changelog::
:version: 1.2.0b1
.. change:: 4017
:tags: bug, sql
:tickets: 4017
Added some extra strictness to the handling of Python "float" values
passed to SQL statements. A "float" value will be associated with the
:class:`.Float` datatype and not the Decimal-coercing :class:`.Numeric`
datatype as was the case before, eliminating a confusing warning
emitted on SQLite as well as unecessary coercion to Decimal.
.. seealso::
:ref:`change_floats_12`
.. change:: 3058
:tags: feature, orm
:tickets: 3058
+25
View File
@@ -764,6 +764,31 @@ Where the value of the parameter "x_1" is ``'total/%score'``.
:ticket:`2694`
.. _change_floats_12:
Stronger typing added to "float" datatypes
------------------------------------------
A series of changes allow for use of the :class:`.Float` datatype to more
strongly link itself to Python floating point values, instead of the more
generic :class:`.Numeric`. The changes are mostly related to ensuring
that Python floating point values are not erroneously coerced to
``Decimal()``, and are coerced to ``float`` if needed, on the result side,
if the application is working with plain floats.
* A plain Python "float" value passed to a SQL expression will now be
pulled into a literal parameter with the type :class:`.Float`; previously,
the type was :class:`.Numeric`, with the default "asdecimal=True" flag, which
meant the result type would coerce to ``Decimal()``. In particular,
this would emit a confusing warning on SQLite::
float_value = connection.scalar(
select([literal(4.56)]) # the "BindParameter" will now be
# Float, not Numeric(asdecimal=True)
)
:ticket:`4017`
Key Behavioral Changes - ORM
============================
+1 -1
View File
@@ -2604,7 +2604,7 @@ MATCHTYPE = MatchType()
_type_map = {
int: Integer(),
float: Numeric(),
float: Float(),
bool: BOOLEANTYPE,
decimal.Decimal: Numeric(),
dt.date: Date(),
@@ -431,6 +431,24 @@ class NumericTest(_LiteralRoundTripFixture, fixtures.TestBase):
filter_=lambda n: n is not None and round(n, 5) or None
)
@testing.fails_on("mysql", "until we do #4020")
def test_float_coerce_round_trip(self):
expr = 15.7563
val = testing.db.scalar(
select([literal(expr)])
)
eq_(val, expr)
# TODO: this one still breaks on MySQL
# def test_decimal_coerce_round_trip(self):
# expr = decimal.Decimal("15.7563")
#
# val = testing.db.scalar(
# select([literal(expr)])
# )
# eq_(val, expr)
@testing.requires.precision_numerics_general
def test_precision_decimal(self):
numbers = set([
+17
View File
@@ -2025,6 +2025,23 @@ class ExpressionTest(
expr = column('foo', CHAR) == "asdf"
eq_(expr.right.type.__class__, CHAR)
def test_actual_literal_adapters(self):
for data, expected in [
(5, Integer),
(2.65, Float),
(True, Boolean),
(decimal.Decimal("2.65"), Numeric),
(datetime.date(2015, 7, 20), Date),
(datetime.time(10, 15, 20), Time),
(datetime.datetime(2015, 7, 20, 10, 15, 20), DateTime),
(datetime.timedelta(seconds=5), Interval),
(None, types.NullType)
]:
is_(
literal(data).type.__class__,
expected
)
def test_typedec_operator_adapt(self):
expr = test_table.c.bvalue + "hi"