"""SQL function API, factories, and built-in functions.
"""
-import warnings
-
from . import annotation
from . import operators
from . import schema
from .selectable import FromClause
from .selectable import Select
from .visitors import VisitableType
-from .. import exc as sa_exc
from .. import util
_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"):
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:
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:
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)
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):
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
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)
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'
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'
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)
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):