FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

unittest discover throws TypeError on namespace packages · Issue #80958 · python/cpython · GitHub

Repository navigation

unittest discover throws TypeError on namespace packages #80958

Closed

Description

BPO 36777
Nosy @warsaw, @brettcannon, @ericvsmith, @rbtcollins, @ezio-melotti, @voidspace, @methane, @augustogoulart, @tirkarthi
PRs
  • bpo-36777: unittest discover should not throw TypeError on empty packages #13259
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2019-05-02.16:47:03.266>
    labels = ['3.7', '3.8', 'type-bug', 'library']
    title = 'unittest discover throws TypeError on namespace packages'
    updated_at = <Date 2020-07-20.03:33:05.899>
    user = 'https://github.com/tirkarthi'

    bugs.python.org fields:

    activity = <Date 2020-07-20.03:33:05.899>
    actor = 'methane'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2019-05-02.16:47:03.266>
    creator = 'xtreak'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 36777
    keywords = ['patch']
    message_count = 5.0
    messages = ['341285', '341339', '341530', '373941', '373980']
    nosy_count = 9.0
    nosy_names = ['barry', 'brett.cannon', 'eric.smith', 'rbcollins', 'ezio.melotti', 'michael.foord', 'methane', 'augustogoulart', 'xtreak']
    pr_nums = ['13259']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue36777'
    versions = ['Python 3.7', 'Python 3.8']

    Linked PRs

    Activity

    1. tirkarthi commented on May 2, 2019

      MemberAuthor

      In the given folder structure unittest discover fails. I think this is due to bpo-32303 where __file__ was set as None and I guess empty packages didn't have __file__ set before which has None value now. Hence the_module.__file__ returns None and thus subsequent calls to os.path.dirname raise TypeError. There is one another location in the same file in unittest module causing this issue. See also bpo-36406 for similar error in doctest with the change. I reverted bbbcf86 just to confirm the issue. I am adding devs in bpo-32303 and unittest. Any thoughts on this would be helpful.

      ➜  cpython git:(master) ✗ tree test1
      test1
      └── test2
          └── test_foo.py
      
      1 directory, 1 file
      ➜  cpython git:(master) ✗ ./python.exe -m unittest discover test1.test2
      Traceback (most recent call last):
        File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/runpy.py", line 192, in _run_module_as_main
          return _run_code(code, main_globals, None,
        File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/runpy.py", line 85, in _run_code
          exec(code, run_globals)
        File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/__main__.py", line 18, in <module>
          main(module=None)
        File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/main.py", line 100, in __init__
          self.parseArgs(argv)
        File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/main.py", line 124, in parseArgs
          self._do_discovery(argv[2:])
        File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/main.py", line 244, in _do_discovery
          self.createTests(from_discovery=True, Loader=Loader)
        File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/main.py", line 154, in createTests
          self.test = loader.discover(self.start, self.pattern, self.top)
        File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/loader.py", line 306, in discover
          os.path.dirname((the_module.__file__)))
        File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/posixpath.py", line 152, in dirname
          p = os.fspath(p)
      TypeError: expected str, bytes or os.PathLike object, not NoneType

      # 3.6.4 works that doesn't have the change

      ➜ cpython git:(master) ✗ python3.6 -m unittest discover test1.test2
      .
      ----------------------------------------------------------------------
      Ran 1 test in 0.000s

      OK

      A unittest would be as below :

      def test_empty_package_discovery(self):
          # bpo-36789: Return zero test cases when using discovery in
          # empty packages.
      
          with support.temp_dir() as path:
              dirname, basename = os.path.split(path)
              os.mkdir(os.path.join(path, 'test2'))
      
              with support.DirsOnSysPath(dirname):
                  loader = unittest.TestLoader()
                  empty_package = f"{basename}.test2"
                  tests_count = loader.discover(empty_package).countTestCases()
                  self.assertEqual(loader.discover(empty_package).countTestCases(), 0)

      One possible fix would be to check for __file__ == None and return empty list of test cases but this causes a behavior change like below where my patch returns 0 and python 3.6 returns 1 test.

      # Patch

      diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py
      index ba7105e1ad..f465b2419f 100644
      --- a/Lib/unittest/loader.py
      +++ b/Lib/unittest/loader.py
      @@ -302,6 +302,10 @@ class TestLoader(object):
                       the_module = sys.modules[start_dir]
                       top_part = start_dir.split('.')[0]
                       try:
      +                    filepath = the_module.__file__
      +                    # __file__ is None for empty packages. Hence return empty list of tests.
      +                    if filepath == None:
      +                        return self.suiteClass([])
                           start_dir = os.path.abspath(
                              os.path.dirname((the_module.__file__)))
                       except AttributeError:

      # Behavior change

      ➜ cpython git:(unittest-empty-package) ✗ ./python.exe -m unittest discover test1.test2

      ----------------------------------------------------------------------
      Ran 0 tests in 0.000s

      OK
      ➜ cpython git:(unittest-empty-package) ✗ python3.6 -m unittest discover test1.test2
      .
      ----------------------------------------------------------------------
      Ran 1 test in 0.000s

      OK

      My patch also causes reference leak and I am not sure why. So I guess mine is not the best approach to solving this.

      ➜ cpython git:(unittest-empty-package) ✗ ./python.exe -m test --fail-env-changed -R 3:3 test_unittest
      Run tests sequentially
      0:00:00 load avg: 1.46 [1/1] test_unittest
      beginning 6 repetitions
      123456
      ......
      test_unittest leaked [99, 99, 99] references, sum=297
      test_unittest leaked [38, 38, 38] memory blocks, sum=114
      test_unittest failed in 43 sec 634 ms

      == Tests result: FAILURE ==

      1 test failed:
      test_unittest

      Total duration: 43 sec 655 ms
      Tests result: FAILURE

    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-bugAn unexpected behavior, bug, or error
      on May 2, 2019
    3. tirkarthi commented on May 3, 2019

      MemberAuthor

      I opened bpo-36784 for reference leak in the initial report along with a reproducer that is reproducible on master branch without my changes.

    4. augustogoulart commented on May 6, 2019

      augustogoulartmannequin
      Mannequin

      Confirming that the issue seems to have been introduced on 3.7. Working on this at the PyCon US 2019 sprints.

    5. tirkarthi commented on Jul 19, 2020

      MemberAuthor

      I didn't know packages without __init__.py are namespace packages back then and used empty packages. I guess it's related to the other issues with unittest and namespace packages bpo-36723, bpo-23882 and bpo-35617

    6. changed the title [-]unittest discover throws TypeError on empty packages[/-] [+]unittest discover throws TypeError on namespace packages[/+] on Jul 19, 2020
    7. methane commented on Jul 20, 2020

      Member

      This issue is not duplicate of bpo-23882.
      In the example, test1 and test1.test2 are namespace package. But they are specified explicitly.

      ./python.exe -m unittest discover test1.test2

    8. transferred this issue fromon Apr 10, 2022
    9. DataAcctLisa commented on Sep 2, 2024

      ran this through an ai to diagnose and got this suggested patch

      diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py
      index ba7105e1ad..f465b2419f 100644
      --- a/Lib/unittest/loader.py
      +++ b/Lib/unittest/loader.py
      @@ -302,6 +302,13 @@ class TestLoader(object):
                       the_module = sys.modules[start_dir]
                       top_part = start_dir.split('.')[0]
                       try:
      +                    # Handle the case where __file__ is None (empty package)
      +                    if the_module.__file__ is None:
      +                        if not hasattr(the_module, '__path__') or not list(the_module.__path__):
      +                            # If the package is empty, return an empty suite
      +                            return self.suiteClass([])
      +                        # Continue processing if it's not empty
      +                        
                           start_dir = os.path.abspath(
                              os.path.dirname(the_module.__file__))
                       except AttributeError:
      

      which came with this explanation:

      Check for file: The patch first checks if file is None. If it is, it further checks if the package is truly empty by examining path (which should exist for packages).
      Return Empty Suite Only if Empty: If both conditions confirm that the package is empty, it returns an empty test suite. Otherwise, it proceeds with the normal discovery process.
      Avoid Reference Leaks: The refined patch avoids creating unnecessary TestSuite objects, which helps in preventing reference leaks.
      Additional Testing:
      After applying the patch, it is crucial to run extensive tests, including the specific cases of empty packages and reference management, to ensure no new issues are introduced and that the fix resolves the original problem.

    10. added 2 commits that reference this issue on Sep 7, 2024
    11. jacobtylerwalls commented on Sep 7, 2024

      Contributor

      Support for namespace package discovery was removed in #29745, however as noted in this issue, we still have a cryptic failure.

      I think we should consider reverting #29745 and solving the underlying issue, which was pretty trivial.

      PR

    12. methane commented on Sep 8, 2024

      Member

      @jacobtylerwalls Do you understand that test discovery in namespace packages had been broken for long time before I remove?
      Reverting #29745 just re-introduce broken feature again.

      Update: I was wrong, suggested patch is not enough and code removed in #29745 seems needed.
      But I need more time to understand test discovery codebase again.

    13. added a commit that references this issue on Sep 8, 2024
    14. jacobtylerwalls commented on Sep 8, 2024

      Contributor

      I agree there is no reason to do a revert alone without fixing what was broken about it. Subsequent commits on #29745 apply a fix. Sorry if this was not signalled sufficiently.

      But I need more time to understand test discovery codebase again.

      Take your time, I'm eager to have your view when you are able, and thanks!

    15. added a commit that references this issue on Oct 23, 2024
    16. added a commit that references this issue on Jan 12, 2025
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.7 (EOL)end of life3.8 (EOL)end of lifestdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

      Projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL