]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Use a symbol to mark function that are in case-sensitive mode.
authorAdrien Berchet <adrien.berchet@gmail.com>
Thu, 11 Apr 2019 16:46:10 +0000 (18:46 +0200)
committerAdrien Berchet <adrien.berchet@gmail.com>
Thu, 11 Apr 2019 16:46:10 +0000 (18:46 +0200)
lib/sqlalchemy/sql/functions.py
test/sql/test_deprecations.py
test/sql/test_functions.py

index f5720408bc8e319f97902f6adaf35e77a0ab16ba..32c9149ad73202e32a96560d87a45c060405d732 100644 (file)
@@ -8,8 +8,6 @@
 """SQL function API, factories, and built-in functions.
 
 """
-import warnings
-
 from . import annotation
 from . import operators
 from . import schema
@@ -35,7 +33,6 @@ from .selectable import Alias
 from .selectable import FromClause
 from .selectable import Select
 from .visitors import VisitableType
-from .. import exc as sa_exc
 from .. import util
 
 
@@ -43,6 +40,10 @@ _registry = util.defaultdict(dict)
 _case_sensitive_registry = util.defaultdict(
     lambda: util.defaultdict(dict)
 )
+_CASE_SENSITIVE = util.symbol(
+    name="case_sensitive_function",
+    doc="Symbol to mark the functions that are switched into case-sensitive "
+    "mode.")
 
 
 def register_function(identifier, fn, package="_default"):
@@ -60,18 +61,26 @@ def register_function(identifier, fn, package="_default"):
     identifier = identifier.lower()
 
     # Check if a function with the same lowercase identifier is registered.
-    if identifier in reg:
+    if identifier in reg and reg[identifier] is not _CASE_SENSITIVE:
         if raw_identifier in case_sensitive_reg[identifier]:
-            warnings.warn(
+            util.warn(
                 "The GenericFunction '{}' is already registered and "
-                "is going to be overriden.".format(identifier),
-                sa_exc.SAWarning)
+                "is going to be overriden.".format(identifier))
             reg[identifier] = fn
         else:
             # If a function with the same lowercase identifier is registered,
             # then these 2 functions are considered as case-sensitive.
             # Note: This case should raise an error in a later release.
-            reg.pop(identifier)
+            util.warn_deprecated(
+                "GenericFunction '{}' is already registered with "
+                "different letter case, so the previously registered function "
+                "'{}' is switched into case-sensitive mode. "
+                "GenericFunction objects will be fully case-insensitive in a "
+                "future release.".format(
+                    raw_identifier,
+                    list(case_sensitive_reg[identifier].keys())[0],
+                ))
+            reg[identifier] = _CASE_SENSITIVE
 
     # Check if a function with different letter case identifier is registered.
     elif identifier in case_sensitive_reg:
@@ -80,16 +89,17 @@ def register_function(identifier, fn, package="_default"):
             raw_identifier not in case_sensitive_reg[identifier]
         ):
             util.warn_deprecated(
-                "GenericFunction(s) {} are already registered with "
-                "different letter cases and might interact with {}.".format(
+                "GenericFunction(s) '{}' are already registered with "
+                "different letter cases and might interact with '{}'. "
+                "GenericFunction objects will be fully case-insensitive in a "
+                "future release.".format(
                     list(case_sensitive_reg[identifier].keys()),
                     raw_identifier))
 
         else:
-            warnings.warn(
+            util.warn(
                 "The GenericFunction '{}' is already registered and "
-                "is going to be overriden.".format(identifier),
-                sa_exc.SAWarning)
+                "is going to be overriden.".format(raw_identifier))
 
     # Register by default
     else:
@@ -487,11 +497,11 @@ class _FunctionGenerator(object):
             package = None
 
         if package is not None:
-            reg = _registry[package]
-            case_sensitive_reg = _case_sensitive_registry[package]
-            func = reg.get(fname.lower())
-            if func is None and fname.lower() in case_sensitive_reg:
-                func = case_sensitive_reg[fname.lower()].get(fname)
+            func = _registry[package].get(fname.lower())
+            if func is _CASE_SENSITIVE:
+                case_sensitive_reg = _case_sensitive_registry[package]
+                func = case_sensitive_reg.get(fname.lower()).get(fname)
+
             if func is not None:
                 return func(*c, **o)
 
index b1a61d9c9df0d840652a9b1681380a147c75c0b6..05698c78a80f026d62b95c22d663d1d1eaab3f02 100644 (file)
@@ -36,7 +36,6 @@ from sqlalchemy.testing import fixtures
 from sqlalchemy.testing import in_
 from sqlalchemy.testing import mock
 from sqlalchemy.testing import not_in_
-from sqlalchemy.testing.assertions import expect_warnings
 
 
 class DeprecationWarningsTest(fixtures.TestBase):
@@ -174,7 +173,14 @@ class DeprecationWarningsTest(fixtures.TestBase):
         in_("myfunc", cs_reg)
         eq_(set(cs_reg['myfunc'].keys()), set(['MYFUNC']))
 
-        with testing.expect_deprecated():
+        with testing.expect_deprecated(
+            "GenericFunction 'MyFunc' is already registered with"
+            " different letter case, so the previously registered function "
+            "'MYFUNC' is switched into case-sensitive mode. "
+            "GenericFunction objects will be fully case-insensitive in a "
+            "future release.",
+            regex=False
+        ):
             class MyFunc(GenericFunction):
                 type = Integer
 
@@ -185,7 +191,7 @@ class DeprecationWarningsTest(fixtures.TestBase):
         with pytest.raises(AssertionError):
             assert isinstance(func.myfunc().type, Integer)
 
-        not_in_("myfunc", reg)
+        eq_(reg["myfunc"], functions._CASE_SENSITIVE)
         not_in_("MYFUNC", reg)
         not_in_("MyFunc", reg)
         in_("myfunc", cs_reg)
@@ -210,7 +216,14 @@ class DeprecationWarningsTest(fixtures.TestBase):
         in_("replaceable_func", cs_reg)
         eq_(set(cs_reg['replaceable_func'].keys()), set(['REPLACEABLE_FUNC']))
 
-        with testing.expect_deprecated():
+        with testing.expect_deprecated(
+            "GenericFunction 'Replaceable_Func' is already registered with"
+            " different letter case, so the previously registered function "
+            "'REPLACEABLE_FUNC' is switched into case-sensitive mode. "
+            "GenericFunction objects will be fully case-insensitive in a "
+            "future release.",
+            regex=False
+        ):
             class Replaceable_Func(GenericFunction):
                 type = DateTime
                 identifier = 'Replaceable_Func'
@@ -220,24 +233,38 @@ class DeprecationWarningsTest(fixtures.TestBase):
         assert isinstance(func.RePlAcEaBlE_fUnC().type, NullType)
         assert isinstance(func.replaceable_func().type, NullType)
 
-        not_in_("replaceable_func", reg)
+        eq_(reg["replaceable_func"], functions._CASE_SENSITIVE)
         not_in_("REPLACEABLE_FUNC", reg)
         not_in_("Replaceable_Func", reg)
         in_("replaceable_func", cs_reg)
         eq_(set(cs_reg['replaceable_func'].keys()),
             set(['REPLACEABLE_FUNC', 'Replaceable_Func']))
 
-        with expect_warnings():
+        with testing.expect_warnings(
+            "The GenericFunction 'REPLACEABLE_FUNC' is already registered and "
+            "is going to be overriden.",
+            regex=False
+        ):
             class replaceable_func_override(GenericFunction):
                 type = DateTime
                 identifier = 'REPLACEABLE_FUNC'
 
-        with testing.expect_deprecated():
+        with testing.expect_deprecated(
+            "GenericFunction(s) '['REPLACEABLE_FUNC', 'Replaceable_Func']' "
+            "are already registered with different letter cases and might "
+            "interact with 'replaceable_func'. GenericFunction objects will "
+            "be fully case-insensitive in a future release.",
+            regex=False
+        ):
             class replaceable_func_lowercase(GenericFunction):
                 type = String
                 identifier = 'replaceable_func'
 
-        with expect_warnings():
+        with testing.expect_warnings(
+            "The GenericFunction 'Replaceable_Func' is already registered and "
+            "is going to be overriden.",
+            regex=False
+        ):
             class Replaceable_Func_override(GenericFunction):
                 type = Integer
                 identifier = 'Replaceable_Func'
@@ -247,7 +274,7 @@ class DeprecationWarningsTest(fixtures.TestBase):
         assert isinstance(func.RePlAcEaBlE_fUnC().type, NullType)
         assert isinstance(func.replaceable_func().type, String)
 
-        not_in_("replaceable_func", reg)
+        eq_(reg["replaceable_func"], functions._CASE_SENSITIVE)
         not_in_("REPLACEABLE_FUNC", reg)
         not_in_("Replaceable_Func", reg)
         in_("replaceable_func", cs_reg)
index ae2bdd47ac42b08548b8968c4631ea8a9adc94a3..9f8072721261b105431dda1dac479afa7b370c8e 100644 (file)
@@ -241,22 +241,26 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
 
     def test_replace_function(self):
 
-        class replacable_func(GenericFunction):
+        class replaceable_func(GenericFunction):
             type = Integer
-            identifier = 'replacable_func'
-
-        assert isinstance(func.Replacable_Func().type, Integer)
-        assert isinstance(func.RePlAcaBlE_fUnC().type, Integer)
-        assert isinstance(func.replacable_func().type, Integer)
-
-        with expect_warnings():
-            class replacable_func_override(GenericFunction):
+            identifier = 'replaceable_func'
+
+        assert isinstance(func.Replaceable_Func().type, Integer)
+        assert isinstance(func.RePlAcEaBlE_fUnC().type, Integer)
+        assert isinstance(func.replaceable_func().type, Integer)
+
+        with expect_warnings(
+            "The GenericFunction 'replaceable_func' is already registered and "
+            "is going to be overriden.",
+            regex=False
+        ):
+            class replaceable_func_override(GenericFunction):
                 type = DateTime
-                identifier = 'replacable_func'
+                identifier = 'replaceable_func'
 
-        assert isinstance(func.Replacable_Func().type, DateTime)
-        assert isinstance(func.RePlAcaBlE_fUnC().type, DateTime)
-        assert isinstance(func.replacable_func().type, DateTime)
+        assert isinstance(func.Replaceable_Func().type, DateTime)
+        assert isinstance(func.RePlAcEaBlE_fUnC().type, DateTime)
+        assert isinstance(func.replaceable_func().type, DateTime)
 
     def test_custom_w_custom_name(self):
         class myfunc(GenericFunction):