tests: Improve master_test.ScanCodeImportsTest coverage
This covers existing behaviours of `mitogen.master.scan_code_imports()` some of which are relied on, some not, but regardless weren't tested. Notably - Explicit relative imports return level > 0 - Imports inside `class` and `def` are excluded - Imports inside other blocks are included - Python 3.x prunes impossible if/else branches (previously unknown) It also - Decouples the test results from the implementation details of the unit test. - Fixes a missing import - Fixes at least one Python 2.4 incompatibility (use of with block)pull/1328/head
parent
1386529493
commit
2fd88298ae
@ -0,0 +1,11 @@
|
||||
# pyright: reportMissingImports=false
|
||||
# ruff: noqa: E401 E702 F401 F403
|
||||
|
||||
import a
|
||||
import a.b
|
||||
import c as d
|
||||
import e, e.f as g \
|
||||
, h; import i
|
||||
|
||||
from j import k, l, m as n
|
||||
from o import *
|
||||
@ -0,0 +1,9 @@
|
||||
# pyright: reportMissingImports=false
|
||||
# ruff: noqa: E401 E702 F401 F403
|
||||
|
||||
from . import a
|
||||
from .b import c, d as e
|
||||
from ... import (
|
||||
f,
|
||||
j as k,
|
||||
)
|
||||
@ -0,0 +1,13 @@
|
||||
# pyright: reportMissingImports=false
|
||||
# ruff: noqa: E401 E702 F401 F403
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import a
|
||||
import a.b
|
||||
import c as d
|
||||
import e, e.f as g \
|
||||
, h; import i
|
||||
|
||||
from j import k, l, m as n
|
||||
from o import *
|
||||
@ -0,0 +1,7 @@
|
||||
class C:
|
||||
import in_class
|
||||
from in_class import x as y
|
||||
|
||||
def m(self):
|
||||
import in_method
|
||||
from in_method import x as y, z
|
||||
@ -0,0 +1,3 @@
|
||||
def f():
|
||||
import in_func
|
||||
from in_func import x as y, z
|
||||
@ -0,0 +1,16 @@
|
||||
import sys
|
||||
|
||||
|
||||
if True:
|
||||
import in_if_always_true
|
||||
from in_if_always_true import x as y, z
|
||||
else:
|
||||
import in_else_never_true
|
||||
from in_else_never_true import x as y, z
|
||||
|
||||
if sys.version >= (3, 0):
|
||||
import in_if_py3
|
||||
from in_if_py3 import x as y, z
|
||||
else:
|
||||
import in_else_py2
|
||||
from in_else_py2 import x as y, z
|
||||
@ -0,0 +1,9 @@
|
||||
try:
|
||||
import in_try
|
||||
from in_try import x as y, z
|
||||
except ImportError:
|
||||
import in_except_importerror
|
||||
from in_except_importerror import x as y, z
|
||||
except Exception:
|
||||
import in_except_exception
|
||||
from in_except_exception import x as y, z
|
||||
@ -1,25 +1,129 @@
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import testlib
|
||||
import mitogen.master
|
||||
|
||||
|
||||
def testmod_compile(path):
|
||||
path = os.path.join(testlib.MODS_DIR, path)
|
||||
f = open(path, 'rb')
|
||||
co = compile(f.read(), path, 'exec')
|
||||
f.close()
|
||||
return co
|
||||
|
||||
|
||||
class ScanCodeImportsTest(testlib.TestCase):
|
||||
func = staticmethod(mitogen.master.scan_code_imports)
|
||||
|
||||
if mitogen.core.PY3:
|
||||
level = 0
|
||||
else:
|
||||
level = -1
|
||||
@unittest.skipIf(sys.version_info < (3, 0), "Py is 2.x, would be relative")
|
||||
def test_default_absolute(self):
|
||||
co = testmod_compile('scanning/defaults.py')
|
||||
expected = [
|
||||
(0, 'a', ()), (0, 'a.b', ()), (0, 'c', ()),
|
||||
(0, 'e', ()), (0, 'e.f', ()), (0, 'h', ()),
|
||||
(0, 'i', ()),
|
||||
(0, 'j', ('k', 'l', 'm')),
|
||||
(0, 'o', ('*',)),
|
||||
]
|
||||
self.assertEqual(list(self.func(co)), expected)
|
||||
|
||||
@unittest.skipIf(sys.version_info >= (3, 0), "Py is 3.x, would be absolute")
|
||||
def test_default_relative(self):
|
||||
co = testmod_compile('scanning/defaults.py')
|
||||
expected = [
|
||||
(-1, 'a', ()), (-1, 'a.b', ()), (-1, 'c', ()),
|
||||
(-1, 'e', ()), (-1, 'e.f', ()), (-1, 'h', ()),
|
||||
(-1, 'i', ()),
|
||||
(-1, 'j', ('k', 'l', 'm')),
|
||||
(-1, 'o', ('*',)),
|
||||
]
|
||||
self.assertEqual(list(self.func(co)), expected)
|
||||
|
||||
@unittest.skipIf(sys.version_info < (2, 5), "Py is 2.4, no absolute_import")
|
||||
def test_explicit_absolute(self):
|
||||
co = testmod_compile('scanning/has_absolute_import.py')
|
||||
expected = [
|
||||
(0, '__future__', ('absolute_import',)),
|
||||
|
||||
(0, 'a', ()), (0, 'a.b', ()), (0, 'c', ()),
|
||||
(0, 'e', ()), (0, 'e.f', ()), (0, 'h', ()),
|
||||
(0, 'i', ()),
|
||||
(0, 'j', ('k', 'l', 'm')),
|
||||
(0, 'o', ('*',)),
|
||||
]
|
||||
self.assertEqual(list(self.func(co)), expected)
|
||||
|
||||
@unittest.skipIf(sys.version_info < (2, 5), "Py is 2.4, no `from . import x`")
|
||||
def test_explicit_relative(self):
|
||||
co = testmod_compile('scanning/explicit_relative.py')
|
||||
expected = [
|
||||
(1, '', ('a',)),
|
||||
(1, 'b', ('c', 'd')),
|
||||
(3, '', ('f', 'j')),
|
||||
]
|
||||
self.assertEqual(list(self.func(co)), expected)
|
||||
|
||||
def test_scoped_class(self):
|
||||
# Imports in `class` or `def` are ignored, a bad heuristc to detect
|
||||
# lazy imports and skip sending the pre-emptively.
|
||||
# See
|
||||
# - https://github.com/mitogen-hq/mitogen/issues/682
|
||||
# - https://github.com/mitogen-hq/mitogen/issues/1325#issuecomment-3170482014
|
||||
co = testmod_compile('scanning/scoped_class.py')
|
||||
self.assertEqual(list(self.func(co)), [])
|
||||
|
||||
pass
|
||||
|
||||
SIMPLE_EXPECT = [
|
||||
(level, 'inspect', ()),
|
||||
(level, 'testlib', ()),
|
||||
(level, 'mitogen.master', ()),
|
||||
def test_scoped_function(self):
|
||||
co = testmod_compile('scanning/scoped_function.py')
|
||||
self.assertEqual(list(self.func(co)), [])
|
||||
|
||||
@unittest.skipIf(sys.version_info >= (3, 0), "Python is 3.x, which prunes")
|
||||
def test_scoped_if_else_unpruned(self):
|
||||
co = testmod_compile('scanning/scoped_if_else.py')
|
||||
level = (-1, 0)[int(sys.version_info >= (3, 0))]
|
||||
expected = [
|
||||
(level, 'sys', ()),
|
||||
(level, 'in_if_always_true', ()),
|
||||
(level, 'in_if_always_true', ('x', 'z')),
|
||||
# Python 2.x does no pruning
|
||||
(level, 'in_else_never_true', ()),
|
||||
(level, 'in_else_never_true', ('x', 'z')),
|
||||
(level, 'in_if_py3', ()),
|
||||
(level, 'in_if_py3', ('x', 'z')),
|
||||
(level, 'in_else_py2', ()),
|
||||
(level, 'in_else_py2', ('x', 'z')),
|
||||
]
|
||||
self.assertEqual(list(self.func(co)), expected)
|
||||
|
||||
@unittest.skipIf(sys.version_info < (3, 0), "Python is 2.x, which doesn't prune")
|
||||
def test_scoped_if_else_pruned(self):
|
||||
co = testmod_compile('scanning/scoped_if_else.py')
|
||||
level = (-1, 0)[int(sys.version_info >= (3, 0))]
|
||||
expected = [
|
||||
(level, 'sys', ()),
|
||||
(level, 'in_if_always_true', ()),
|
||||
(level, 'in_if_always_true', ('x', 'z')),
|
||||
# Python 3.x prunes some impossible branches ...
|
||||
(level, 'in_if_py3', ()),
|
||||
(level, 'in_if_py3', ('x', 'z')),
|
||||
# ... but not sys.version_info ones
|
||||
(level, 'in_else_py2', ()),
|
||||
(level, 'in_else_py2', ('x', 'z')),
|
||||
]
|
||||
self.assertEqual(list(self.func(co)), expected)
|
||||
|
||||
def test_simple(self):
|
||||
source_path = inspect.getsourcefile(ScanCodeImportsTest)
|
||||
with open(source_path) as f:
|
||||
co = compile(f.read(), source_path, 'exec')
|
||||
self.assertEqual(list(self.func(co)), self.SIMPLE_EXPECT)
|
||||
def test_scoped_try_except(self):
|
||||
co = testmod_compile('scanning/scoped_try_except.py')
|
||||
level = (-1, 0)[int(sys.version_info >= (3, 0))]
|
||||
expected = [
|
||||
(level, 'in_try', ()),
|
||||
(level, 'in_try', ('x', 'z')),
|
||||
(level, 'in_except_importerror', ()),
|
||||
(level, 'in_except_importerror', ('x', 'z')),
|
||||
(level, 'in_except_exception', ()),
|
||||
(level, 'in_except_exception', ('x', 'z')),
|
||||
]
|
||||
self.assertEqual(list(self.func(co)), expected)
|
||||
|
||||
Loading…
Reference in New Issue