Coverage for tests/test_introspection.py: 16%

88 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-03-14 10:19 -0700

1# This file is part of utils. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22import sys 

23import unittest 

24from collections import Counter 

25 

26# Classes and functions to use in tests. 

27import lsst.utils 

28from lsst.utils import doImport 

29from lsst.utils._packaging import getPackageDir 

30from lsst.utils.introspection import ( 

31 find_outside_stacklevel, 

32 get_caller_name, 

33 get_class_of, 

34 get_full_type_name, 

35 get_instance_of, 

36) 

37 

38 

39class GetCallerNameTestCase(unittest.TestCase): 

40 """Test get_caller_name. 

41 

42 Warning: due to the different ways this can be run 

43 (e.g. directly or py.test), the module name can be one of two different 

44 things. 

45 """ 

46 

47 def test_free_function(self): 

48 def test_func(): 

49 return get_caller_name(1) 

50 

51 result = test_func() 

52 self.assertEqual(result, f"{__name__}.test_func") 

53 

54 def test_instance_method(self): 

55 class TestClass: 

56 def run(self): 

57 return get_caller_name(1) 

58 

59 tc = TestClass() 

60 result = tc.run() 

61 self.assertEqual(result, f"{__name__}.TestClass.run") 

62 

63 def test_class_method(self): 

64 class TestClass: 

65 @classmethod 

66 def run(cls): 

67 return get_caller_name(1) 

68 

69 tc = TestClass() 

70 result = tc.run() 

71 self.assertEqual(result, f"{__name__}.TestClass.run") 

72 

73 def test_skip(self): 

74 def test_func(stacklevel): 

75 return get_caller_name(stacklevel) 

76 

77 result = test_func(2) 

78 self.assertEqual(result, f"{__name__}.GetCallerNameTestCase.test_skip") 

79 

80 result = test_func(2000000) # use a large number to avoid details of how the test is run 

81 self.assertEqual(result, "") 

82 

83 

84class TestInstropection(unittest.TestCase): 

85 """Tests for lsst.utils.introspection.""" 

86 

87 def testTypeNames(self): 

88 # Check types and also an object 

89 tests = [ 

90 (getPackageDir, "lsst.utils.getPackageDir"), # underscore filtered out 

91 (int, "int"), 

92 (0, "int"), 

93 ("", "str"), 

94 (doImport, "lsst.utils.doImport.doImport"), # no underscore 

95 (Counter, "collections.Counter"), 

96 (Counter(), "collections.Counter"), 

97 (lsst.utils, "lsst.utils"), 

98 ] 

99 

100 for item, typeName in tests: 

101 self.assertEqual(get_full_type_name(item), typeName) 

102 

103 def testUnderscores(self): 

104 # Underscores are filtered out unless they can't be, either 

105 # because __init__.py did not import it or there is a clash with 

106 # the non-underscore version. 

107 for test_name in ( 

108 "import_test.two._four.simple.Simple", 

109 "import_test.two._four.clash.Simple", 

110 "import_test.two.clash.Simple", 

111 ): 

112 test_cls = get_class_of(test_name) 

113 self.assertTrue(test_cls.true()) 

114 full = get_full_type_name(test_cls) 

115 self.assertEqual(full, test_name) 

116 

117 def testGetClassOf(self): 

118 tests = [(doImport, "lsst.utils.doImport"), (Counter, "collections.Counter")] 

119 

120 for test in tests: 

121 ref_type = test[0] 

122 for t in test: 

123 c = get_class_of(t) 

124 self.assertIs(c, ref_type) 

125 

126 def testGetInstanceOf(self): 

127 c = get_instance_of("collections.Counter", "abcdeab") 

128 self.assertIsInstance(c, Counter) 

129 self.assertEqual(c["a"], 2) 

130 with self.assertRaises(TypeError) as cm: 

131 get_instance_of(lsst.utils) 

132 self.assertIn("lsst.utils", str(cm.exception)) 

133 

134 def test_stacklevel(self): 

135 level = find_outside_stacklevel("lsst.utils") 

136 self.assertEqual(level, 1) 

137 

138 info = {} 

139 level = find_outside_stacklevel("lsst.utils", stack_info=info) 

140 self.assertIn("test_introspection.py", info["filename"]) 

141 

142 c = doImport("import_test.two.three.success.Container") 

143 with self.assertWarns(Warning) as cm: 

144 level = c.level() 

145 self.assertTrue(cm.filename.endswith("test_introspection.py")) 

146 self.assertEqual(level, 2) 

147 with self.assertWarns(Warning) as cm: 

148 level = c.indirect_level() 

149 self.assertTrue(cm.filename.endswith("test_introspection.py")) 

150 self.assertEqual(level, 3) 

151 

152 # Test with additional options. 

153 with self.assertWarns(Warning) as cm: 

154 level = c.indirect_level(allow_methods={"indirect_level"}) 

155 self.assertEqual(level, 2) 

156 self.assertTrue(cm.filename.endswith("success.py")) 

157 

158 # Adjust test on python 3.10. 

159 allow_methods = {"import_test.two.three.success.Container.level"} 

160 stacklevel = 1 

161 if sys.version_info < (3, 11, 0): 

162 # python 3.10 does not support "." syntax and will filter it out. 

163 allow_methods.add("indirect_level") 

164 stacklevel = 2 

165 with self.assertWarns(FutureWarning) as cm: 

166 level = c.indirect_level(allow_methods=allow_methods) 

167 self.assertEqual(level, stacklevel) 

168 self.assertTrue(cm.filename.endswith("success.py")) 

169 

170 

171if __name__ == "__main__": 

172 unittest.main()