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
Alex Willmer 4 months ago
parent 1386529493
commit 2fd88298ae

@ -23,6 +23,7 @@ In progress (unreleased)
* :gh:issue:`1329` CI: Refactor and de-duplicate Github Actions workflow
* :gh:issue:`1315` CI: macOS: Increase failed logins limit of test users
* :gh:issue:`1325` tests: Improve ``master_test.ScanCodeImportsTest`` coverage
v0.3.26 (2025-08-04)

@ -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
SIMPLE_EXPECT = [
(level, 'inspect', ()),
(level, 'testlib', ()),
(level, 'mitogen.master', ()),
]
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)
@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
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_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…
Cancel
Save