From e7fcb190cc0a6bf5631cee7dad78ff8c0490403d Mon Sep 17 00:00:00 2001 From: Michael Merickel <michael@merickel.org> Date: Mon, 10 Jul 2017 07:08:52 +0200 Subject: [PATCH] Merge pull request #3124 from mmerickel/circular-import --- pyramid/config/util.py | 64 +------ pyramid/tests/test_util.py | 194 +++++++++++++++++++++ pyramid/util.py | 52 +++++ CHANGES.txt | 4 pyramid/tests/test_config/test_util.py | 194 --------------------- pyramid/viewderivers.py | 15 6 files changed, 268 insertions(+), 255 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a5e1035..0108cef 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,10 @@ decorators. See https://github.com/Pylons/pyramid/pull/3121 and https://github.com/Pylons/pyramid/pull/3123 +- Fix a circular import which made it impossible to import + ``pyramid.viewderivers`` before ``pyramid.config``. + See https://github.com/Pylons/pyramid/pull/3124 + 1.9 (2017-06-26) ================ diff --git a/pyramid/config/util.py b/pyramid/config/util.py index 63f06ff..b899592 100644 --- a/pyramid/config/util.py +++ b/pyramid/config/util.py @@ -1,13 +1,10 @@ from hashlib import md5 -import inspect from pyramid.compat import ( bytes_, - getargspec, is_nonstr_iter ) -from pyramid.compat import im_func from pyramid.exceptions import ConfigurationError from pyramid.registry import predvalseq @@ -15,13 +12,21 @@ TopologicalSorter, action_method, ActionInfo, + takes_one_arg, ) + +from pyramid.viewderivers import ( + MAX_ORDER, + DEFAULT_PHASH, +) action_method = action_method # support bw compat imports ActionInfo = ActionInfo # support bw compat imports -MAX_ORDER = 1 << 30 -DEFAULT_PHASH = md5().hexdigest() +MAX_ORDER = MAX_ORDER # support bw compat imports +DEFAULT_PHASH = DEFAULT_PHASH # support bw compat imports + +takes_one_arg = takes_one_arg # support bw compat imports class not_(object): """ @@ -189,52 +194,3 @@ score = score | bit order = (MAX_ORDER - score) / (len(preds) + 1) return order, preds, phash.hexdigest() - -def takes_one_arg(callee, attr=None, argname=None): - ismethod = False - if attr is None: - attr = '__call__' - if inspect.isroutine(callee): - fn = callee - elif inspect.isclass(callee): - try: - fn = callee.__init__ - except AttributeError: - return False - ismethod = hasattr(fn, '__call__') - else: - try: - fn = getattr(callee, attr) - except AttributeError: - return False - - try: - argspec = getargspec(fn) - except TypeError: - return False - - args = argspec[0] - - if hasattr(fn, im_func) or ismethod: - # it's an instance method (or unbound method on py2) - if not args: - return False - args = args[1:] - - if not args: - return False - - if len(args) == 1: - return True - - if argname: - - defaults = argspec[3] - if defaults is None: - defaults = () - - if args[0] == argname: - if len(args) - len(defaults) == 1: - return True - - return False diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py index bb86a1f..f1a3df5 100644 --- a/pyramid/tests/test_config/test_util.py +++ b/pyramid/tests/test_config/test_util.py @@ -391,200 +391,6 @@ self.assertEqual(predicates[1](None, request), True) self.assertEqual(predicates[2](None, request), True) - -class Test_takes_one_arg(unittest.TestCase): - def _callFUT(self, view, attr=None, argname=None): - from pyramid.config.util import takes_one_arg - return takes_one_arg(view, attr=attr, argname=argname) - - def test_requestonly_newstyle_class_no_init(self): - class foo(object): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_requestonly_newstyle_class_init_toomanyargs(self): - class foo(object): - def __init__(self, context, request): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_requestonly_newstyle_class_init_onearg_named_request(self): - class foo(object): - def __init__(self, request): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_newstyle_class_init_onearg_named_somethingelse(self): - class foo(object): - def __init__(self, req): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_newstyle_class_init_defaultargs_firstname_not_request(self): - class foo(object): - def __init__(self, context, request=None): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_newstyle_class_init_defaultargs_firstname_request(self): - class foo(object): - def __init__(self, request, foo=1, bar=2): - """ """ - self.assertTrue(self._callFUT(foo, argname='request')) - - def test_newstyle_class_init_firstname_request_with_secondname(self): - class foo(object): - def __init__(self, request, two): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_newstyle_class_init_noargs(self): - class foo(object): - def __init__(): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_oldstyle_class_no_init(self): - class foo: - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_oldstyle_class_init_toomanyargs(self): - class foo: - def __init__(self, context, request): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_oldstyle_class_init_onearg_named_request(self): - class foo: - def __init__(self, request): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_oldstyle_class_init_onearg_named_somethingelse(self): - class foo: - def __init__(self, req): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_oldstyle_class_init_defaultargs_firstname_not_request(self): - class foo: - def __init__(self, context, request=None): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_oldstyle_class_init_defaultargs_firstname_request(self): - class foo: - def __init__(self, request, foo=1, bar=2): - """ """ - self.assertTrue(self._callFUT(foo, argname='request'), True) - - def test_oldstyle_class_init_noargs(self): - class foo: - def __init__(): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_function_toomanyargs(self): - def foo(context, request): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_function_with_attr_false(self): - def bar(context, request): - """ """ - def foo(context, request): - """ """ - foo.bar = bar - self.assertFalse(self._callFUT(foo, 'bar')) - - def test_function_with_attr_true(self): - def bar(context, request): - """ """ - def foo(request): - """ """ - foo.bar = bar - self.assertTrue(self._callFUT(foo, 'bar')) - - def test_function_onearg_named_request(self): - def foo(request): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_function_onearg_named_somethingelse(self): - def foo(req): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_function_defaultargs_firstname_not_request(self): - def foo(context, request=None): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_function_defaultargs_firstname_request(self): - def foo(request, foo=1, bar=2): - """ """ - self.assertTrue(self._callFUT(foo, argname='request')) - - def test_function_noargs(self): - def foo(): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_instance_toomanyargs(self): - class Foo: - def __call__(self, context, request): - """ """ - foo = Foo() - self.assertFalse(self._callFUT(foo)) - - def test_instance_defaultargs_onearg_named_request(self): - class Foo: - def __call__(self, request): - """ """ - foo = Foo() - self.assertTrue(self._callFUT(foo)) - - def test_instance_defaultargs_onearg_named_somethingelse(self): - class Foo: - def __call__(self, req): - """ """ - foo = Foo() - self.assertTrue(self._callFUT(foo)) - - def test_instance_defaultargs_firstname_not_request(self): - class Foo: - def __call__(self, context, request=None): - """ """ - foo = Foo() - self.assertFalse(self._callFUT(foo)) - - def test_instance_defaultargs_firstname_request(self): - class Foo: - def __call__(self, request, foo=1, bar=2): - """ """ - foo = Foo() - self.assertTrue(self._callFUT(foo, argname='request'), True) - - def test_instance_nocall(self): - class Foo: pass - foo = Foo() - self.assertFalse(self._callFUT(foo)) - - def test_method_onearg_named_request(self): - class Foo: - def method(self, request): - """ """ - foo = Foo() - self.assertTrue(self._callFUT(foo.method)) - - def test_function_annotations(self): - def foo(bar): - """ """ - # avoid SyntaxErrors in python2, this if effectively nop - getattr(foo, '__annotations__', {}).update({'bar': 'baz'}) - self.assertTrue(self._callFUT(foo)) - class TestNotted(unittest.TestCase): def _makeOne(self, predicate): from pyramid.config.util import Notted diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py index d64f0a7..6059c68 100644 --- a/pyramid/tests/test_util.py +++ b/pyramid/tests/test_util.py @@ -855,6 +855,200 @@ self.assertTrue('foo' not in obj.__dict__) +class Test_takes_one_arg(unittest.TestCase): + def _callFUT(self, view, attr=None, argname=None): + from pyramid.config.util import takes_one_arg + return takes_one_arg(view, attr=attr, argname=argname) + + def test_requestonly_newstyle_class_no_init(self): + class foo(object): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_requestonly_newstyle_class_init_toomanyargs(self): + class foo(object): + def __init__(self, context, request): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_requestonly_newstyle_class_init_onearg_named_request(self): + class foo(object): + def __init__(self, request): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_newstyle_class_init_onearg_named_somethingelse(self): + class foo(object): + def __init__(self, req): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_newstyle_class_init_defaultargs_firstname_not_request(self): + class foo(object): + def __init__(self, context, request=None): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_newstyle_class_init_defaultargs_firstname_request(self): + class foo(object): + def __init__(self, request, foo=1, bar=2): + """ """ + self.assertTrue(self._callFUT(foo, argname='request')) + + def test_newstyle_class_init_firstname_request_with_secondname(self): + class foo(object): + def __init__(self, request, two): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_newstyle_class_init_noargs(self): + class foo(object): + def __init__(): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_no_init(self): + class foo: + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_init_toomanyargs(self): + class foo: + def __init__(self, context, request): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_init_onearg_named_request(self): + class foo: + def __init__(self, request): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_oldstyle_class_init_onearg_named_somethingelse(self): + class foo: + def __init__(self, req): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_oldstyle_class_init_defaultargs_firstname_not_request(self): + class foo: + def __init__(self, context, request=None): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_init_defaultargs_firstname_request(self): + class foo: + def __init__(self, request, foo=1, bar=2): + """ """ + self.assertTrue(self._callFUT(foo, argname='request'), True) + + def test_oldstyle_class_init_noargs(self): + class foo: + def __init__(): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_function_toomanyargs(self): + def foo(context, request): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_function_with_attr_false(self): + def bar(context, request): + """ """ + def foo(context, request): + """ """ + foo.bar = bar + self.assertFalse(self._callFUT(foo, 'bar')) + + def test_function_with_attr_true(self): + def bar(context, request): + """ """ + def foo(request): + """ """ + foo.bar = bar + self.assertTrue(self._callFUT(foo, 'bar')) + + def test_function_onearg_named_request(self): + def foo(request): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_function_onearg_named_somethingelse(self): + def foo(req): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_function_defaultargs_firstname_not_request(self): + def foo(context, request=None): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_function_defaultargs_firstname_request(self): + def foo(request, foo=1, bar=2): + """ """ + self.assertTrue(self._callFUT(foo, argname='request')) + + def test_function_noargs(self): + def foo(): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_instance_toomanyargs(self): + class Foo: + def __call__(self, context, request): + """ """ + foo = Foo() + self.assertFalse(self._callFUT(foo)) + + def test_instance_defaultargs_onearg_named_request(self): + class Foo: + def __call__(self, request): + """ """ + foo = Foo() + self.assertTrue(self._callFUT(foo)) + + def test_instance_defaultargs_onearg_named_somethingelse(self): + class Foo: + def __call__(self, req): + """ """ + foo = Foo() + self.assertTrue(self._callFUT(foo)) + + def test_instance_defaultargs_firstname_not_request(self): + class Foo: + def __call__(self, context, request=None): + """ """ + foo = Foo() + self.assertFalse(self._callFUT(foo)) + + def test_instance_defaultargs_firstname_request(self): + class Foo: + def __call__(self, request, foo=1, bar=2): + """ """ + foo = Foo() + self.assertTrue(self._callFUT(foo, argname='request'), True) + + def test_instance_nocall(self): + class Foo: pass + foo = Foo() + self.assertFalse(self._callFUT(foo)) + + def test_method_onearg_named_request(self): + class Foo: + def method(self, request): + """ """ + foo = Foo() + self.assertTrue(self._callFUT(foo.method)) + + def test_function_annotations(self): + def foo(bar): + """ """ + # avoid SyntaxErrors in python2, this if effectively nop + getattr(foo, '__annotations__', {}).update({'bar': 'baz'}) + self.assertTrue(self._callFUT(foo)) + + def dummyfunc(): pass diff --git a/pyramid/util.py b/pyramid/util.py index 2827884..97e761c 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -17,6 +17,8 @@ ) from pyramid.compat import ( + getargspec, + im_func, is_nonstr_iter, integer_types, string_types, @@ -645,3 +647,53 @@ return (pattern[0] == "." and (host.endswith(pattern) or host == pattern[1:]) or pattern == host) + + +def takes_one_arg(callee, attr=None, argname=None): + ismethod = False + if attr is None: + attr = '__call__' + if inspect.isroutine(callee): + fn = callee + elif inspect.isclass(callee): + try: + fn = callee.__init__ + except AttributeError: + return False + ismethod = hasattr(fn, '__call__') + else: + try: + fn = getattr(callee, attr) + except AttributeError: + return False + + try: + argspec = getargspec(fn) + except TypeError: + return False + + args = argspec[0] + + if hasattr(fn, im_func) or ismethod: + # it's an instance method (or unbound method on py2) + if not args: + return False + args = args[1:] + + if not args: + return False + + if len(args) == 1: + return True + + if argname: + + defaults = argspec[3] + if defaults is None: + defaults = () + + if args[0] == argname: + if len(args) - len(defaults) == 1: + return True + + return False diff --git a/pyramid/viewderivers.py b/pyramid/viewderivers.py index d2869b1..e048d47 100644 --- a/pyramid/viewderivers.py +++ b/pyramid/viewderivers.py @@ -1,3 +1,4 @@ +from hashlib import md5 import inspect from zope.interface import ( @@ -28,22 +29,22 @@ is_unbound_method, ) -from pyramid.config.util import ( - DEFAULT_PHASH, - MAX_ORDER, - takes_one_arg, - ) - from pyramid.exceptions import ( ConfigurationError, PredicateMismatch, ) from pyramid.httpexceptions import HTTPForbidden -from pyramid.util import object_description +from pyramid.util import ( + object_description, + takes_one_arg, + ) from pyramid.view import render_view_to_response from pyramid import renderers +MAX_ORDER = 1 << 30 +DEFAULT_PHASH = md5().hexdigest() + def view_description(view): try: return view.__text__ -- Gitblit v1.9.3