1# Test packages (dotted-name import)
2
3import sys
4import os
5import tempfile
6import textwrap
7import unittest
8
9
10# Helpers to create and destroy hierarchies.
11
12def cleanout(root):
13    names = os.listdir(root)
14    for name in names:
15        fullname = os.path.join(root, name)
16        if os.path.isdir(fullname) and not os.path.islink(fullname):
17            cleanout(fullname)
18        else:
19            os.remove(fullname)
20    os.rmdir(root)
21
22def fixdir(lst):
23    if "__builtins__" in lst:
24        lst.remove("__builtins__")
25    if "__initializing__" in lst:
26        lst.remove("__initializing__")
27    return lst
28
29
30# XXX Things to test
31#
32# import package without __init__
33# import package with __init__
34# __init__ importing submodule
35# __init__ importing global module
36# __init__ defining variables
37# submodule importing other submodule
38# submodule importing global module
39# submodule import submodule via global name
40# from package import submodule
41# from package import subpackage
42# from package import variable (defined in __init__)
43# from package import * (defined in __init__)
44
45
46class TestPkg(unittest.TestCase):
47
48    def setUp(self):
49        self.root = None
50        self.pkgname = None
51        self.syspath = list(sys.path)
52        self.modules_to_cleanup = set()  # Populated by mkhier().
53
54    def tearDown(self):
55        sys.path[:] = self.syspath
56        for modulename in self.modules_to_cleanup:
57            if modulename in sys.modules:
58                del sys.modules[modulename]
59        if self.root: # Only clean if the test was actually run
60            cleanout(self.root)
61
62        # delete all modules concerning the tested hierarchy
63        if self.pkgname:
64            modules = [name for name in sys.modules
65                       if self.pkgname in name.split('.')]
66            for name in modules:
67                del sys.modules[name]
68
69    def run_code(self, code):
70        exec(textwrap.dedent(code), globals(), {"self": self})
71
72    def mkhier(self, descr):
73        root = tempfile.mkdtemp()
74        sys.path.insert(0, root)
75        if not os.path.isdir(root):
76            os.mkdir(root)
77        for name, contents in descr:
78            comps = name.split()
79            self.modules_to_cleanup.add('.'.join(comps))
80            fullname = root
81            for c in comps:
82                fullname = os.path.join(fullname, c)
83            if contents is None:
84                os.mkdir(fullname)
85            else:
86                with open(fullname, "w") as f:
87                    f.write(contents)
88                    if not contents.endswith('\n'):
89                        f.write('\n')
90        self.root = root
91        # package name is the name of the first item
92        self.pkgname = descr[0][0]
93
94    def test_1(self):
95        hier = [("t1", None), ("t1 __init__.py", "")]
96        self.mkhier(hier)
97        import t1
98
99    def test_2(self):
100        hier = [
101         ("t2", None),
102         ("t2 __init__.py", "'doc for t2'"),
103         ("t2 sub", None),
104         ("t2 sub __init__.py", ""),
105         ("t2 sub subsub", None),
106         ("t2 sub subsub __init__.py", "spam = 1"),
107        ]
108        self.mkhier(hier)
109
110        import t2.sub
111        import t2.sub.subsub
112        self.assertEqual(t2.__name__, "t2")
113        self.assertEqual(t2.sub.__name__, "t2.sub")
114        self.assertEqual(t2.sub.subsub.__name__, "t2.sub.subsub")
115
116        # This exec crap is needed because Py3k forbids 'import *' outside
117        # of module-scope and __import__() is insufficient for what we need.
118        s = """
119            import t2
120            from t2 import *
121            self.assertEqual(dir(), ['self', 'sub', 't2'])
122            """
123        self.run_code(s)
124
125        from t2 import sub
126        from t2.sub import subsub
127        from t2.sub.subsub import spam
128        self.assertEqual(sub.__name__, "t2.sub")
129        self.assertEqual(subsub.__name__, "t2.sub.subsub")
130        self.assertEqual(sub.subsub.__name__, "t2.sub.subsub")
131        for name in ['spam', 'sub', 'subsub', 't2']:
132            self.assertTrue(locals()["name"], "Failed to import %s" % name)
133
134        import t2.sub
135        import t2.sub.subsub
136        self.assertEqual(t2.__name__, "t2")
137        self.assertEqual(t2.sub.__name__, "t2.sub")
138        self.assertEqual(t2.sub.subsub.__name__, "t2.sub.subsub")
139
140        s = """
141            from t2 import *
142            self.assertEqual(dir(), ['self', 'sub'])
143            """
144        self.run_code(s)
145
146    def test_3(self):
147        hier = [
148                ("t3", None),
149                ("t3 __init__.py", ""),
150                ("t3 sub", None),
151                ("t3 sub __init__.py", ""),
152                ("t3 sub subsub", None),
153                ("t3 sub subsub __init__.py", "spam = 1"),
154               ]
155        self.mkhier(hier)
156
157        import t3.sub.subsub
158        self.assertEqual(t3.__name__, "t3")
159        self.assertEqual(t3.sub.__name__, "t3.sub")
160        self.assertEqual(t3.sub.subsub.__name__, "t3.sub.subsub")
161
162    def test_4(self):
163        hier = [
164        ("t4.py", "raise RuntimeError('Shouldnt load t4.py')"),
165        ("t4", None),
166        ("t4 __init__.py", ""),
167        ("t4 sub.py", "raise RuntimeError('Shouldnt load sub.py')"),
168        ("t4 sub", None),
169        ("t4 sub __init__.py", ""),
170        ("t4 sub subsub.py",
171         "raise RuntimeError('Shouldnt load subsub.py')"),
172        ("t4 sub subsub", None),
173        ("t4 sub subsub __init__.py", "spam = 1"),
174               ]
175        self.mkhier(hier)
176
177        s = """
178            from t4.sub.subsub import *
179            self.assertEqual(spam, 1)
180            """
181        self.run_code(s)
182
183    def test_5(self):
184        hier = [
185        ("t5", None),
186        ("t5 __init__.py", "import t5.foo"),
187        ("t5 string.py", "spam = 1"),
188        ("t5 foo.py",
189         "from . import string; assert string.spam == 1"),
190         ]
191        self.mkhier(hier)
192
193        import t5
194        s = """
195            from t5 import *
196            self.assertEqual(dir(), ['foo', 'self', 'string', 't5'])
197            """
198        self.run_code(s)
199
200        import t5
201        self.assertEqual(fixdir(dir(t5)),
202                         ['__cached__', '__doc__', '__file__', '__loader__',
203                          '__name__', '__package__', '__path__', '__spec__',
204                          'foo', 'string', 't5'])
205        self.assertEqual(fixdir(dir(t5.foo)),
206                         ['__cached__', '__doc__', '__file__', '__loader__',
207                          '__name__', '__package__', '__spec__', 'string'])
208        self.assertEqual(fixdir(dir(t5.string)),
209                         ['__cached__', '__doc__', '__file__', '__loader__',
210                          '__name__', '__package__', '__spec__', 'spam'])
211
212    def test_6(self):
213        hier = [
214                ("t6", None),
215                ("t6 __init__.py",
216                 "__all__ = ['spam', 'ham', 'eggs']"),
217                ("t6 spam.py", ""),
218                ("t6 ham.py", ""),
219                ("t6 eggs.py", ""),
220               ]
221        self.mkhier(hier)
222
223        import t6
224        self.assertEqual(fixdir(dir(t6)),
225                         ['__all__', '__cached__', '__doc__', '__file__',
226                          '__loader__', '__name__', '__package__', '__path__',
227                          '__spec__'])
228        s = """
229            import t6
230            from t6 import *
231            self.assertEqual(fixdir(dir(t6)),
232                             ['__all__', '__cached__', '__doc__', '__file__',
233                              '__loader__', '__name__', '__package__',
234                              '__path__', '__spec__', 'eggs', 'ham', 'spam'])
235            self.assertEqual(dir(), ['eggs', 'ham', 'self', 'spam', 't6'])
236            """
237        self.run_code(s)
238
239    def test_7(self):
240        hier = [
241                ("t7.py", ""),
242                ("t7", None),
243                ("t7 __init__.py", ""),
244                ("t7 sub.py",
245                 "raise RuntimeError('Shouldnt load sub.py')"),
246                ("t7 sub", None),
247                ("t7 sub __init__.py", ""),
248                ("t7 sub .py",
249                 "raise RuntimeError('Shouldnt load subsub.py')"),
250                ("t7 sub subsub", None),
251                ("t7 sub subsub __init__.py",
252                 "spam = 1"),
253               ]
254        self.mkhier(hier)
255
256
257        t7, sub, subsub = None, None, None
258        import t7 as tas
259        self.assertEqual(fixdir(dir(tas)),
260                         ['__cached__', '__doc__', '__file__', '__loader__',
261                          '__name__', '__package__', '__path__', '__spec__'])
262        self.assertFalse(t7)
263        from t7 import sub as subpar
264        self.assertEqual(fixdir(dir(subpar)),
265                         ['__cached__', '__doc__', '__file__', '__loader__',
266                          '__name__', '__package__', '__path__', '__spec__'])
267        self.assertFalse(t7)
268        self.assertFalse(sub)
269        from t7.sub import subsub as subsubsub
270        self.assertEqual(fixdir(dir(subsubsub)),
271                         ['__cached__', '__doc__', '__file__', '__loader__',
272                          '__name__', '__package__', '__path__', '__spec__',
273                          'spam'])
274        self.assertFalse(t7)
275        self.assertFalse(sub)
276        self.assertFalse(subsub)
277        from t7.sub.subsub import spam as ham
278        self.assertEqual(ham, 1)
279        self.assertFalse(t7)
280        self.assertFalse(sub)
281        self.assertFalse(subsub)
282
283    @unittest.skipIf(sys.flags.optimize >= 2,
284                     "Docstrings are omitted with -O2 and above")
285    def test_8(self):
286        hier = [
287                ("t8", None),
288                ("t8 __init__"+os.extsep+"py", "'doc for t8'"),
289               ]
290        self.mkhier(hier)
291
292        import t8
293        self.assertEqual(t8.__doc__, "doc for t8")
294
295if __name__ == "__main__":
296    unittest.main()
297