Coverage for tests/test_introspection.py: 16%
88 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-06 03:35 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-06 03:35 -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/>.
22import sys
23import unittest
24from collections import Counter
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)
39class GetCallerNameTestCase(unittest.TestCase):
40 """Test get_caller_name.
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 """
47 def test_free_function(self):
48 def test_func():
49 return get_caller_name(1)
51 result = test_func()
52 self.assertEqual(result, f"{__name__}.test_func")
54 def test_instance_method(self):
55 class TestClass:
56 def run(self):
57 return get_caller_name(1)
59 tc = TestClass()
60 result = tc.run()
61 self.assertEqual(result, f"{__name__}.TestClass.run")
63 def test_class_method(self):
64 class TestClass:
65 @classmethod
66 def run(cls):
67 return get_caller_name(1)
69 tc = TestClass()
70 result = tc.run()
71 self.assertEqual(result, f"{__name__}.TestClass.run")
73 def test_skip(self):
74 def test_func(stacklevel):
75 return get_caller_name(stacklevel)
77 result = test_func(2)
78 self.assertEqual(result, f"{__name__}.GetCallerNameTestCase.test_skip")
80 result = test_func(2000000) # use a large number to avoid details of how the test is run
81 self.assertEqual(result, "")
84class TestInstropection(unittest.TestCase):
85 """Tests for lsst.utils.introspection."""
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 ]
100 for item, typeName in tests:
101 self.assertEqual(get_full_type_name(item), typeName)
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)
117 def testGetClassOf(self):
118 tests = [(doImport, "lsst.utils.doImport"), (Counter, "collections.Counter")]
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)
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))
134 def test_stacklevel(self):
135 level = find_outside_stacklevel("lsst.utils")
136 self.assertEqual(level, 1)
138 info = {}
139 level = find_outside_stacklevel("lsst.utils", stack_info=info)
140 self.assertIn("test_introspection.py", info["filename"])
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)
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"))
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"))
171if __name__ == "__main__":
172 unittest.main()