Repository: pyinstaller/pyinstaller

Issue:
unittest automatically triggered during building
## Description of the issue
`unittest` is mysteriously triggered during building, though it does not affect the (successful) building process.

This behavior is quite weird since neither `unittest` nor `laonlp` is actually imported or used in the minimal script. To reproduce this issue, besides `openpyxl`, `LaoNLP` should also be installed (via pip), since the triggered tests belong to `LaoNLP`.

### Context information (for bug reports)

* Output of `pyinstaller --version`: ```6.0.0```
* Version of Python: 3.10.13
* Platform: Windows 11 x64
* How you installed Python: python.org/downloads
* Did you also try this on another platform? Does it work there?

* try the latest development version, using the following command:

```shell
pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip
```

* follow *all* the instructions in our "If Things Go Wrong" Guide
  (https://github.com/pyinstaller/pyinstaller/wiki/If-Things-Go-Wrong) and

### Make sure [everything is packaged correctly](https://github.com/pyinstaller/pyinstaller/wiki/How-to-Report-Bugs#make-sure-everything-is-packaged-correctly)

  * [x] start with clean installation
  * [x] use the latest development version
  * [x] Run your frozen program **from a command window (shell)** — instead of double-clicking on it
  * [x] Package your program in **--onedir mode**
  * [x] Package **without UPX**, say: use the option `--noupx` or set `upx=False` in your .spec-file
  * [x] Repackage you application in **verbose/debug mode**. For this, pass the option `--debug` to `pyi-makespec` or `pyinstaller` or use `EXE(..., debug=1, ...)` in your .spec file.

### A minimal example program which shows the error
```
import openpyxl
```

### Stacktrace / full error message
```
F:\>pyinstaller --clean -y test.py
295 INFO: PyInstaller: 6.0.0
295 INFO: Python: 3.10.13
302 INFO: Platform: Windows-10-10.0.22621-SP0
303 INFO: wrote F:\test.spec
304 INFO: Removing temporary files and cleaning cache in C:\Users\BLKSerene\AppData\Local\pyinstaller
310 INFO: Extending PYTHONPATH with paths
['F:\\']
556 INFO: checking Analysis
556 INFO: Building Analysis because Analysis-00.toc is non existent
556 INFO: Initializing module dependency graph...
558 INFO: Caching module graph hooks...
571 INFO: Analyzing base_library.zip ...
1440 INFO: Loading module hook 'hook-heapq.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
1510 INFO: Loading module hook 'hook-encodings.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
2778 INFO: Loading module hook 'hook-pickle.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
3812 INFO: Caching module dependency graph...
3917 INFO: Running Analysis Analysis-00.toc
3918 INFO: Looking for Python shared library...
3922 INFO: Using Python shared library: D:\Python\python310.dll
3923 INFO: Analyzing F:\test.py
3924 INFO: Loading module hook 'hook-openpyxl.py' from 'D:\\Python\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
4023 INFO: Loading module hook 'hook-numpy.py' from 'D:\\Python\\Lib\\site-packages\\numpy\\_pyinstaller'...
4481 INFO: Loading module hook 'hook-difflib.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
4627 INFO: Loading module hook 'hook-multiprocessing.util.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
4785 INFO: Loading module hook 'hook-xml.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
5251 INFO: Loading module hook 'hook-platform.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
5284 INFO: Loading module hook 'hook-sysconfig.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
6614 INFO: Loading module hook 'hook-lxml.py' from 'D:\\Python\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
7070 INFO: Loading module hook 'hook-xml.etree.cElementTree.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
7325 INFO: Loading module hook 'hook-PIL.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
7395 INFO: Loading module hook 'hook-PIL.Image.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
8004 INFO: Loading module hook 'hook-packaging.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
8046 INFO: Loading module hook 'hook-PIL.ImageFilter.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
8445 INFO: Processing module hooks...
8445 INFO: Loading module hook 'hook-lxml.etree.py' from 'D:\\Python\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
8718 INFO: Loading module hook 'hook-charset_normalizer.py' from 'D:\\Python\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
8858 INFO: Loading module hook 'hook-lxml.isoschematron.py' from 'D:\\Python\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
8951 INFO: Loading module hook 'hook-pkg_resources.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
10068 INFO: Loading module hook 'hook-PIL.SpiderImagePlugin.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
10482 INFO: Loading module hook 'hook-platformdirs.py' from 'D:\\Python\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
10622 INFO: Loading module hook 'hook-lxml.objectify.py' from 'D:\\Python\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
10647 INFO: Looking for ctypes DLLs
10660 INFO: Analyzing run-time hooks ...
10665 INFO: Including run-time hook 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py'
10667 INFO: Including run-time hook 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgres.py'
10673 INFO: Including run-time hook 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py'
10675 INFO: Including run-time hook 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py'
10691 INFO: Looking for dynamic libraries
D:\Python\lib\site-packages\PyInstaller\building\build_main.py:194: UserWarning: The numpy.array_api submodule is still experimental. See NEP 47.
  __import__(package)
..........E
======================================================================
ERROR: tests.test_word_vector (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: tests.test_word_vector
Traceback (most recent call last):
  File "D:\Python\lib\site-packages\laonlp\word_vector\word2vec.py", line 20, in <module>
    import gensim
ModuleNotFoundError: No module named 'gensim'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\Python\lib\unittest\loader.py", line 436, in _find_test_path
    module = self._get_module_from_name(name)
  File "D:\Python\lib\unittest\loader.py", line 377, in _get_module_from_name
    __import__(name)
  File "D:\Python\lib\site-packages\tests\test_word_vector.py", line 4, in <module>
    from laonlp.word_vector import Word2Vec
  File "D:\Python\lib\site-packages\laonlp\word_vector\__init__.py", line 17, in <module>
    from laonlp.word_vector.word2vec import Word2Vec
  File "D:\Python\lib\site-packages\laonlp\word_vector\word2vec.py", line 22, in <module>
    raise ModuleNotFoundError('Word vector functionalities require gensim which is not currently installed. Please try installing the package via "pip install gensim".')
ModuleNotFoundError: Word vector functionalities require gensim which is not currently installed. Please try installing the package via "pip install gensim".


----------------------------------------------------------------------
Ran 11 tests in 0.379s

FAILED (errors=1)
17033 INFO: Extra DLL search directories (AddDllDirectory): ['D:\\Python\\lib\\site-packages\\numpy.libs']
17033 INFO: Extra DLL search directories (PATH): []
17451 INFO: Warnings written to F:\build\test\warn-test.txt
17544 INFO: Graph cross-reference written to F:\build\test\xref-test.html
17574 INFO: checking PYZ
17574 INFO: Building PYZ because PYZ-00.toc is non existent
17574 INFO: Building PYZ (ZlibArchive) F:\build\test\PYZ-00.pyz
18387 INFO: Building PYZ (ZlibArchive) F:\build\test\PYZ-00.pyz completed successfully.
18404 INFO: checking PKG
18405 INFO: Building PKG because PKG-00.toc is non existent
18405 INFO: Building PKG (CArchive) test.pkg
18421 INFO: Building PKG (CArchive) test.pkg completed successfully.
18423 INFO: Bootloader D:\Python\lib\site-packages\PyInstaller\bootloader\Windows-64bit-intel\run.exe
18423 INFO: checking EXE
18424 INFO: Building EXE because EXE-00.toc is non existent
18424 INFO: Building EXE from EXE-00.toc
18424 INFO: Copying bootloader EXE to F:\build\test\test.exe
18484 INFO: Copying icon to EXE
18542 INFO: Copying 0 resources to EXE
18542 INFO: Embedding manifest in EXE
18599 INFO: Appending PKG archive to EXE
18606 INFO: Fixing EXE headers
18980 INFO: Building EXE from EXE-00.toc completed successfully.
18981 INFO: checking COLLECT
18981 INFO: Building COLLECT because COLLECT-00.toc is non existent
18982 INFO: Removing dir F:\dist\test
18998 INFO: Building COLLECT COLLECT-00.toc
19224 INFO: Building COLLECT COLLECT-00.toc completed successfully.
```


Discussion:
Can't reproduce this in a clean environment with just PyInstaller, LaoNLP and openpyxl installed but I do notice that LaoNLP have accidentally packaged their test suite.
```console
(env) /e/n/p/8010> pip download LaoNLP
(env) /e/n/p/8010> unzip -l LaoNLP-1.1.2-py3-none-any.whl 
Archive:  LaoNLP-1.1.2-py3-none-any.whl
  Length      Date    Time    Name
---------  ---------- -----   ----
     1680  2023-09-23 19:04   laonlp/__init__.py
     1356  2023-09-23 19:04   laonlp/corpus/LICENSE-lao-eng-dictionary
   205310  2023-09-23 19:04   laonlp/corpus/Lao-Dictionary.txt
      940  2023-09-23 19:04   laonlp/corpus/__init__.py
      737  2023-09-23 19:04   laonlp/corpus/core.py
  2857765  2023-09-23 19:04   laonlp/corpus/lao-eng-dictionary.csv
      379  2023-09-23 19:04   laonlp/corpus/lao-wannaphong.txt
     2353  2023-09-23 19:04   laonlp/corpus/lao_words.py
   409234  2023-09-23 19:04   laonlp/corpus/lo_spellcheck_dict.txt
     1486  2023-09-23 19:04   laonlp/corpus/mopt_dict.py
  3743688  2023-09-23 19:04   laonlp/corpus/ptagger_SeqLabeling_corpus.json
  5077288  2023-09-23 19:04   laonlp/corpus/ptagger_Yunshan-Cup_corpus.json
     1783  2023-09-23 19:04   laonlp/corpus/stopwords_lao.txt
   270905  2023-09-23 19:04   laonlp/corpus/wiktionary-20210720.txt
      675  2023-09-23 19:04   laonlp/tag/__init__.py
     1956  2023-09-23 19:04   laonlp/tag/pos_tag.py
     1308  2023-09-23 19:04   laonlp/tokenize/__init__.py
     1061  2023-09-23 19:04   laonlp/translate/__init__.py
     1085  2023-09-23 19:04   laonlp/translate/mopt_dict.py
     8469  2023-09-23 19:04   laonlp/transliterate/__init__.py
      864  2023-09-23 19:04   laonlp/util/__init__.py
     1750  2023-09-23 19:04   laonlp/util/digitconv.py
      925  2023-09-23 19:04   laonlp/util/lao.py
      686  2023-09-23 19:04   laonlp/word_vector/__init__.py
     3229  2023-09-23 19:04   laonlp/word_vector/word2vec.py
      226  2023-09-23 19:04   tests/__init__.py
      561  2023-09-23 19:04   tests/test_tag.py
      597  2023-09-23 19:04   tests/test_tokenize.py
      231  2023-09-23 19:04   tests/test_translate.py
      549  2023-09-23 19:04   tests/test_transliterate.py
      609  2023-09-23 19:04   tests/test_util.py
      374  2023-09-23 19:04   tests/test_word_vector.py
    11357  2023-09-23 19:04   LaoNLP-1.1.2.dist-info/LICENSE
     3926  2023-09-23 19:04   LaoNLP-1.1.2.dist-info/METADATA
       92  2023-09-23 19:04   LaoNLP-1.1.2.dist-info/WHEEL
       13  2023-09-23 19:04   LaoNLP-1.1.2.dist-info/top_level.txt
     3090  2023-09-23 19:04   LaoNLP-1.1.2.dist-info/RECORD
---------                     -------
 12618537                     37 files
```
That still shouldn't have affected PyInstaller though...
I set up a clean environment with only the latest version of `PyInstaller`, `openpyxl`, and `LaoNLP` installed via pip (`tzdata` is also required on Windows), and could confirm that this issue happens.

PyInstaller: 6.1.0
OS: Windows 11 22H2 x64
Python: 3.10.13

`pip list`:
```
Package                   Version
------------------------- ---------
altgraph                  0.17.4
certifi                   2023.7.22
charset-normalizer        3.3.0
et-xmlfile                1.1.0
idna                      3.4
LaoNLP                    1.1.2
openpyxl                  3.1.2
packaging                 23.2
pefile                    2023.2.7
pip                       23.2.1
pyinstaller               6.1.0
pyinstaller-hooks-contrib 2023.10
pythainlp                 4.0.2
pywin32-ctypes            0.2.2
requests                  2.31.0
setuptools                68.2.2
tzdata                    2023.3
urllib3                   2.0.6
```

test.py:
```
import openpyxl
```

`pyinstaller --clean -y test.py`:
```
300 INFO: PyInstaller: 6.1.0
300 INFO: Python: 3.10.13
309 INFO: Platform: Windows-10-10.0.22621-SP0
309 INFO: wrote F:\test\test.spec
311 INFO: Removing temporary files and cleaning cache in C:\Users\BLKSerene\AppData\Local\pyinstaller
315 INFO: Extending PYTHONPATH with paths
['F:\\test']
504 INFO: checking Analysis
504 INFO: Building Analysis because Analysis-00.toc is non existent
504 INFO: Initializing module dependency graph...
505 INFO: Caching module graph hooks...
521 INFO: Analyzing base_library.zip ...
1341 INFO: Loading module hook 'hook-heapq.py' from 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks'...
1424 INFO: Loading module hook 'hook-encodings.py' from 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks'...
2764 INFO: Loading module hook 'hook-pickle.py' from 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks'...
4004 INFO: Caching module dependency graph...
4129 INFO: Running Analysis Analysis-00.toc
4129 INFO: Looking for Python shared library...
4135 INFO: Using Python shared library: D:\Python\python310.dll
4135 INFO: Analyzing F:\test\test.py
4137 INFO: Loading module hook 'hook-openpyxl.py' from 'F:\\test\\env\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
4386 INFO: Loading module hook 'hook-xml.py' from 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks'...
4405 INFO: Loading module hook 'hook-xml.etree.cElementTree.py' from 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks'...
5180 INFO: Loading module hook 'hook-difflib.py' from 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks'...
5341 INFO: Loading module hook 'hook-multiprocessing.util.py' from 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks'...
6032 INFO: Processing module hooks...
6276 INFO: Looking for ctypes DLLs
6279 INFO: Analyzing run-time hooks ...
6283 INFO: Including run-time hook 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py'
6284 INFO: Including run-time hook 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py'
6287 INFO: Including run-time hook 'F:\\test\\env\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py'
6296 INFO: Looking for dynamic libraries
..........E
======================================================================
ERROR: tests.test_word_vector (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: tests.test_word_vector
Traceback (most recent call last):
  File "F:\test\env\lib\site-packages\laonlp\word_vector\word2vec.py", line 20, in <module>
    import gensim
ModuleNotFoundError: No module named 'gensim'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\Python\lib\unittest\loader.py", line 436, in _find_test_path
    module = self._get_module_from_name(name)
  File "D:\Python\lib\unittest\loader.py", line 377, in _get_module_from_name
    __import__(name)
  File "F:\test\env\lib\site-packages\tests\test_word_vector.py", line 4, in <module>
    from laonlp.word_vector import Word2Vec
  File "F:\test\env\lib\site-packages\laonlp\word_vector\__init__.py", line 17, in <module>
    from laonlp.word_vector.word2vec import Word2Vec
  File "F:\test\env\lib\site-packages\laonlp\word_vector\word2vec.py", line 22, in <module>
    raise ModuleNotFoundError('Word vector functionalities require gensim which is not currently installed. Please try installing the package via "pip install gensim".')
ModuleNotFoundError: Word vector functionalities require gensim which is not currently installed. Please try installing the package via "pip install gensim".


----------------------------------------------------------------------
Ran 11 tests in 0.437s

FAILED (errors=1)
12063 INFO: Extra DLL search directories (AddDllDirectory): []
12063 INFO: Extra DLL search directories (PATH): []
12275 INFO: Warnings written to F:\test\build\test\warn-test.txt
12330 INFO: Graph cross-reference written to F:\test\build\test\xref-test.html
12354 INFO: checking PYZ
12355 INFO: Building PYZ because PYZ-00.toc is non existent
12355 INFO: Building PYZ (ZlibArchive) F:\test\build\test\PYZ-00.pyz
12847 INFO: Building PYZ (ZlibArchive) F:\test\build\test\PYZ-00.pyz completed successfully.
12862 INFO: checking PKG
12862 INFO: Building PKG because PKG-00.toc is non existent
12862 INFO: Building PKG (CArchive) test.pkg
12875 INFO: Building PKG (CArchive) test.pkg completed successfully.
12875 INFO: Bootloader F:\test\env\lib\site-packages\PyInstaller\bootloader\Windows-64bit-intel\run.exe
12876 INFO: checking EXE
12876 INFO: Building EXE because EXE-00.toc is non existent
12876 INFO: Building EXE from EXE-00.toc
12876 INFO: Copying bootloader EXE to F:\test\build\test\test.exe
12954 INFO: Copying icon to EXE
13020 INFO: Copying 0 resources to EXE
13020 INFO: Embedding manifest in EXE
13086 INFO: Appending PKG archive to EXE
13092 INFO: Fixing EXE headers
13417 INFO: Building EXE from EXE-00.toc completed successfully.
13418 INFO: checking COLLECT
13420 INFO: Building COLLECT because COLLECT-00.toc is non existent
13420 INFO: Removing dir F:\test\dist\test
13424 INFO: Building COLLECT COLLECT-00.toc
13570 INFO: Building COLLECT COLLECT-00.toc completed successfully.
```
Looks like [this import](https://foss.heptapod.net/openpyxl/openpyxl/-/blob/branch/3.1/openpyxl/reader/excel.py#L16) confuses modulegraph into collection of the `tests` package, so we import it prior to binary dependency scan. And the import runs the unittests.

Seems that the bug can be triggered by either `from ..tests import KEEP_VBA` or `from .tests import KEEP_VBA`, so this is likely a context issue (i.e., modulegraph forgets it should not be looking at global import names).

As a quick work-around, you can use `--exclude tests` - it is very likely that this package is not imported anywhere (so the question is why it is installed in the first place...), as that would also trigger the unit tests.
It's always nice when suspicious code blocks are already marked as such by our predecessors...

https://github.com/pyinstaller/pyinstaller/blob/3ff53a87dd856c71f3a9a31abffbcf673d6a8237/PyInstaller/lib/modulegraph/modulegraph.py#L1514-L1525

Guess it's time we add that `if level < 1`...


Resolving patch:
diff --git a/PyInstaller/lib/modulegraph/modulegraph.py b/PyInstaller/lib/modulegraph/modulegraph.py
--- a/PyInstaller/lib/modulegraph/modulegraph.py
+++ b/PyInstaller/lib/modulegraph/modulegraph.py
@@ -1511,16 +1511,15 @@ def _find_head_package(
         target_package = self._safe_import_module(
             target_module_headname, target_package_name, source_package)
 
-        #FIXME: Why exactly is this necessary again? This doesn't quite seem
-        #right but maybe it is. Shouldn't absolute imports only be performed if
-        #the passed "level" is either "ABSOLUTE_IMPORT_LEVEL" or
-        #"ABSOLUTE_OR_RELATIVE_IMPORT_LEVEL" -- or, more succinctly:
-        #
-        #    if level < 1:
-
         # If this target package is *NOT* importable and a source package was
         # passed, attempt to import this target package as an absolute import.
-        if target_package is None and source_package is not None:
+        #
+        # ADDENDUM: but do this only if the passed "level" is either
+        # ABSOLUTE_IMPORT_LEVEL (0) or ABSOLUTE_OR_RELATIVE_IMPORT_LEVEL (-1).
+        # Otherwise, an attempt at relative import of a missing sub-module
+        # (from .module import something) might pull in an unrelated
+        # but eponymous top-level module, which should not happen.
+        if target_package is None and source_package is not None and level <= ABSOLUTE_IMPORT_LEVEL:
             target_package_name = target_module_headname
             source_package = None
 


Test patch:
diff --git a/tests/functional/modules/pyi_missing_relative_import/hooks/hook-myotherpackage.py b/tests/functional/modules/pyi_missing_relative_import/hooks/hook-myotherpackage.py
new file mode 100644
--- /dev/null
+++ b/tests/functional/modules/pyi_missing_relative_import/hooks/hook-myotherpackage.py
@@ -0,0 +1 @@
+raise Exception("This package must not be collected!")
diff --git a/tests/functional/modules/pyi_missing_relative_import/myotherpackage/__init__.py b/tests/functional/modules/pyi_missing_relative_import/myotherpackage/__init__.py
new file mode 100644
--- /dev/null
+++ b/tests/functional/modules/pyi_missing_relative_import/myotherpackage/__init__.py
@@ -0,0 +1 @@
+# Nothing to do here.
diff --git a/tests/functional/modules/pyi_missing_relative_import/mypackage/__init__.py b/tests/functional/modules/pyi_missing_relative_import/mypackage/__init__.py
new file mode 100644
--- /dev/null
+++ b/tests/functional/modules/pyi_missing_relative_import/mypackage/__init__.py
@@ -0,0 +1,11 @@
+# Different forms of relative-import for an optional sub-module. While the said sub-module does not exist, an
+# unrelated eponymous top-level module does - and these import attempts should *NOT* trigger its collection!
+try:
+    from . import myotherpackage  # noqa: F401
+except ImportError:
+    pass
+
+try:
+    from .myotherpackage import something  # noqa: F401
+except ImportError:
+    pass
diff --git a/tests/functional/test_misc.py b/tests/functional/test_misc.py
--- a/tests/functional/test_misc.py
+++ b/tests/functional/test_misc.py
@@ -368,3 +368,29 @@ def test_import_main_should_not_collect_pyinstaller2(pyi_builder):
         """,
         pyi_args=['--additional-hooks-dir', hooks_dir]
     )
+
+
+# Test that a relative import attempt of a missing optional sub-module in a package does not trigger collection of an
+# unrelated but eponymous top-level module. Simulates the scenario from #8010, where the following block in
+# `openpyxl.reader.excel`:
+#
+# ```
+# try:
+#    from ..tests import KEEP_VBA
+# except ImportError:
+#    KEEP_VBA = False
+# ```
+#
+# (https://foss.heptapod.net/openpyxl/openpyxl/-/blob/branch/3.1/openpyxl/reader/excel.py#L16)
+#
+# triggers collection of top-level `tests` package that is provided by the `LaoNLP` distribution. And importing
+# the said `tests` package during analysis triggers LaoNLP's unit tests...
+def test_missing_relative_import_collects_unrelated_top_level_module(pyi_builder):
+    extra_path = os.path.join(_MODULES_DIR, "pyi_missing_relative_import")
+    hooks_dir = os.path.join(extra_path, 'hooks')
+
+    pyi_builder.test_source(
+        """
+        import mypackage
+        """, pyi_args=['--additional-hooks-dir', hooks_dir, '--paths', extra_path]
+    )
diff --git a/tests/unit/test_modulegraph_more.py b/tests/unit/test_modulegraph_more.py
--- a/tests/unit/test_modulegraph_more.py
+++ b/tests/unit/test_modulegraph_more.py
@@ -492,11 +492,8 @@ def test_swig_import_from_top_level_missing(tmpdir):
     mg.add_script(str(script))
     assert isinstance(mg.find_node('pyi_test_osgeo'), modulegraph.Package)
     assert isinstance(mg.find_node('pyi_test_osgeo.pyi_gdal'), modulegraph.SourceModule)
-    # BUG: Again, this is unecpected behaviour in modulegraph: While MissingModule('_pyi_gdal') is (arguably) removed
-    # when trying to import the SWIG C module, there is no MissingModule('pyi_test_osgeo.pyi_gdal') added, but again
-    # MissingModule('_pyi_gdal'). I still need to understand why.
-    assert mg.find_node('pyi_test_osgeo._pyi_gdal') is None
-    assert isinstance(mg.find_node('_pyi_gdal'), modulegraph.MissingModule)
+    assert isinstance(mg.find_node('pyi_test_osgeo._pyi_gdal'), modulegraph.MissingModule)
+    assert mg.find_node('_pyi_gdal') is None
 
 
 def test_swig_import_from_top_level_but_nested(tmpdir):