attribute, provided the name is the same as that
of the entity mapped column.
+ - Enhanced the instrumentation in the ORM to support
+ Py3K's new argument style of "required kw arguments",
+ i.e. fn(a, b, *, c, d), fn(a, b, *args, c, d).
+ Argument signatures of mapped object's __init__
+ method will be preserved, including required kw rules.
+ [ticket:2237]
+
- Fixed bug in unit of work whereby detection of
"cycles" among classes in highly interlinked patterns
would not produce a deterministic
# Py3K
#func_defaults = getattr(original__init__, '__defaults__', None)
+ #func_kw_defaults = getattr(original__init__, '__kwdefaults__', None)
# Py2K
func = getattr(original__init__, 'im_func', original__init__)
func_defaults = getattr(func, 'func_defaults', None)
__init__.__doc__ = original__init__.__doc__
if func_defaults:
__init__.func_defaults = func_defaults
+ # Py3K
+ #if func_kw_defaults:
+ # __init__.__kwdefaults__ = func_kw_defaults
return __init__
else:
from urlparse import parse_qsl
+if py3k:
+ from inspect import getfullargspec as inspect_getfullargspec
+else:
+ from inspect import getargspec as inspect_getfullargspec
+
if py3k:
# they're bringing it back in 3.2. brilliant !
def callable(fn):
import sys
import types
import warnings
-from compat import update_wrapper, set_types, threading
+from compat import update_wrapper, set_types, threading, callable, inspect_getfullargspec, py3k
from sqlalchemy import exc
def _unique_symbols(used, *bases):
def decorate(fn):
if not inspect.isfunction(fn):
raise Exception("not a decoratable function")
- spec = inspect.getargspec(fn)
+ spec = inspect_getfullargspec(fn)
names = tuple(spec[0]) + spec[1:3] + (fn.func_name,)
targ_name, fn_name = _unique_symbols(names, 'target', 'fn')
'apply_pos': '(self, a, b, c, **d)'}
"""
- spec = callable(fn) and inspect.getargspec(fn) or fn
+ if callable(fn):
+ spec = inspect_getfullargspec(fn)
+ else:
+ # we accept an existing argspec...
+ spec = fn
args = inspect.formatargspec(*spec)
if spec[0]:
self_arg = spec[0][0]
self_arg = '%s[0]' % spec[1]
else:
self_arg = None
- apply_pos = inspect.formatargspec(spec[0], spec[1], spec[2])
- defaulted_vals = spec[3] is not None and spec[0][0-len(spec[3]):] or ()
- apply_kw = inspect.formatargspec(spec[0], spec[1], spec[2], defaulted_vals,
+
+ if py3k:
+ apply_pos = inspect.formatargspec(spec[0], spec[1], spec[2], None, spec[4])
+ num_defaults = 0
+ if spec[3]:
+ num_defaults += len(spec[3])
+ if spec[4]:
+ num_defaults += len(spec[4])
+ name_args = spec[0] + spec[4]
+ else:
+ apply_pos = inspect.formatargspec(spec[0], spec[1], spec[2])
+ num_defaults = 0
+ if spec[3]:
+ num_defaults += len(spec[3])
+ name_args = spec[0]
+
+ if num_defaults:
+ defaulted_vals = name_args[0-num_defaults:]
+ else:
+ defaulted_vals = ()
+
+ apply_kw = inspect.formatargspec(name_args, spec[1], spec[2], defaulted_vals,
formatvalue=lambda x: '=' + x)
if grouped:
return dict(args=args, self_arg=self_arg,
)
)
+def python3(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(
+ lambda: sys.version_info < (3,),
+ "Python version 3.xx is required."
+ )
+ )
+
def python26(fn):
return _chain_decorators_on(
fn,
from test.lib.schema import Column
from test.lib.testing import eq_, ne_
from test.lib.util import decorator
-from test.lib import fixtures
+from test.lib import fixtures, testing
@decorator
def modifies_instrumentation_finders(fn, *args, **kw):
class T(object): pass
assert_raises(KeyError, mapper, T, t)
+class Py3KFunctionInstTest(fixtures.ORMTest):
+ __requires__ = ("python3", )
+
+ # Py3K
+ #def _kw_only_fixture(self):
+ # class A(object):
+ # def __init__(self, a, *, b, c):
+ # self.a = a
+ # self.b = b
+ # self.c = c
+ # return self._instrument(A)
+ #
+ #def _kw_plus_posn_fixture(self):
+ # class A(object):
+ # def __init__(self, a, *args, b, c):
+ # self.a = a
+ # self.b = b
+ # self.c = c
+ # return self._instrument(A)
+ #
+ #def _kw_opt_fixture(self):
+ # class A(object):
+ # def __init__(self, a, *, b, c="c"):
+ # self.a = a
+ # self.b = b
+ # self.c = c
+ # return self._instrument(A)
+
+ def _instrument(self, cls):
+ manager = instrumentation.register_class(cls)
+ canary = []
+ def check(target, args, kwargs):
+ canary.append((args, kwargs))
+ event.listen(manager, "init", check)
+ return cls, canary
+
+ def test_kw_only_args(self):
+ cls, canary = self._kw_only_fixture()
+
+ a = cls("a", b="b", c="c")
+ eq_(canary, [(('a', ), {'b':'b','c':'c'})])
+
+ def test_kw_plus_posn_args(self):
+ cls, canary = self._kw_plus_posn_fixture()
+
+ a = cls("a", 1, 2, 3, b="b", c="c")
+ eq_(canary, [(('a', 1, 2, 3), {'b':'b','c':'c'})])
+
+ def test_kw_only_args_plus_opt(self):
+ cls, canary = self._kw_opt_fixture()
+
+ a = cls("a", b="b")
+ eq_(canary, [(('a', ), {'b':'b','c':'c'})])
+
+ canary[:] = []
+ a = cls("a", b="b", c="d")
+ eq_(canary, [(('a', ), {'b':'b','c':'d'})])
+
+ def test_kw_only_sig(self):
+ cls, canary = self._kw_only_fixture()
+ assert_raises(
+ TypeError,
+ cls, "a", "b", "c"
+ )
+
+ def test_kw_plus_opt_sig(self):
+ cls, canary = self._kw_only_fixture()
+ assert_raises(
+ TypeError,
+ cls, "a", "b", "c"
+ )
+
+ assert_raises(
+ TypeError,
+ cls, "a", "b", c="c"
+ )
class MiscTest(fixtures.ORMTest):
"""Seems basic, but not directly covered elsewhere!"""