# Copyright 2016 Quora, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import qcore from qcore.asserts import assert_eq, assert_ne, assert_unordered_list_eq, AssertRaises import inspect class NonCython(object): pass class InheritFromCython(qcore.caching.LazyConstant): pass def test_is_cython_class(): if qcore.caching.__file__.endswith(".so"): assert qcore.inspection.is_cython_class(qcore.caching.LazyConstant) assert not qcore.inspection.is_cython_class(NonCython) assert not qcore.inspection.is_cython_class(InheritFromCython) class A(object): pass class B(A): pass class C(B): pass class MetaA(type): pass class MetaB(MetaA): pass def test_get_subclass_tree(): assert_unordered_list_eq([C, B], qcore.inspection.get_subclass_tree(A)) assert_eq([C], qcore.inspection.get_subclass_tree(B)) assert_eq([], qcore.inspection.get_subclass_tree(C)) assert_eq([MetaB], qcore.inspection.get_subclass_tree(MetaA)) assert_eq([], qcore.inspection.get_subclass_tree(MetaB)) def fun_with_args(a, b, c, d="e", **f): pass def test_getargspec(): empty = inspect.ArgSpec(args=[], varargs=None, keywords=None, defaults=None) assert_eq(empty, qcore.inspection.getargspec(test_get_subclass_tree)) assert_eq(empty, qcore.inspection.getargspec(qcore.inspection.lazy_stack)) emptymethod = inspect.ArgSpec( args=["self"], varargs=None, keywords=None, defaults=None ) assert_eq(emptymethod, qcore.inspection.getargspec(X.myinstancemethod)) assert_eq(emptymethod, qcore.inspection.getargspec(X().myinstancemethod)) emptyclsmethod = inspect.ArgSpec( args=["cls"], varargs=None, keywords=None, defaults=None ) assert_eq(emptyclsmethod, qcore.inspection.getargspec(X.myclassmethod)) spec = inspect.ArgSpec( args=["a", "b", "c", "d"], varargs=None, keywords="f", defaults=("e",) ) assert_eq(spec, qcore.inspection.getargspec(fun_with_args)) try: exec( """ def fun_with_annotations(a: int, b: str, *args) -> None: pass def fun_with_kwonly_args(a=1, *, b, c=3): pass """ ) except SyntaxError: pass else: def test_getargspec_py3_only(): spec = inspect.ArgSpec( args=["a", "b"], varargs="args", keywords=None, defaults=None ) assert_eq(spec, qcore.inspection.getargspec(fun_with_annotations)) with AssertRaises(ValueError): qcore.inspection.getargspec(fun_with_kwonly_args) class X(object): @classmethod def myclassmethod(cls): pass def myinstancemethod(self): pass class OldStyle: @classmethod def myclassmethod(cls): pass def myinstancemethod(self): pass class BoolConversionFails(object): def method(self): pass def __nonzero__(self): raise TypeError("Cannot convert %s to bool" % self) __bool__ = __nonzero__ def test_is_classmethod(): assert not qcore.inspection.is_classmethod(X) assert not qcore.inspection.is_classmethod(X()) assert not qcore.inspection.is_classmethod(OldStyle) assert not qcore.inspection.is_classmethod(OldStyle()) assert not qcore.inspection.is_classmethod("x") assert not qcore.inspection.is_classmethod(qcore) assert not qcore.inspection.is_classmethod(qcore.inspection.is_classmethod) assert qcore.inspection.is_classmethod(X.myclassmethod) assert qcore.inspection.is_classmethod(X().myclassmethod) assert qcore.inspection.is_classmethod(OldStyle.myclassmethod) assert qcore.inspection.is_classmethod(OldStyle().myclassmethod) assert not qcore.inspection.is_classmethod(X.myinstancemethod) assert not qcore.inspection.is_classmethod(X().myinstancemethod) assert not qcore.inspection.is_classmethod(OldStyle.myinstancemethod) assert not qcore.inspection.is_classmethod(OldStyle().myinstancemethod) # this throws an error if you do "not im_self" assert not qcore.inspection.is_classmethod(BoolConversionFails().method) def test_get_function_call_str(): class TestObject(object): """A test object containing no __str__ implementation.""" def __str__(self): raise NotImplementedError() def __repr__(self): return "test" def test_function(): pass function_str_kv = qcore.inspection.get_function_call_str( test_function, (1, 2, 3), {"k": "v"} ) function_str_dummy = qcore.inspection.get_function_call_str( test_function, (TestObject(),), {} ) assert_eq("test_inspection.test_function(1,2,3,k=v)", function_str_kv) assert_eq("test_inspection.test_function(test)", function_str_dummy) def test_get_function_call_repr(): def dummy_function(): pass function_repr_kv = qcore.inspection.get_function_call_repr( dummy_function, ("x",), {"k": "v"} ) function_repr_kr = qcore.inspection.get_function_call_repr( dummy_function, ("x",), {"k": "r"} ) assert_eq("test_inspection.dummy_function('x',k='v')", function_repr_kv) assert_ne(function_repr_kv, function_repr_kr)