nose-devs / nose2

The successor to nose, based on unittest2
https://nose2.io
Other
796 stars 134 forks source link

suites not always assigned in loadTestsFromNames in loader.py #445

Open jwlarocque opened 5 years ago

jwlarocque commented 5 years ago

If a plugin implements the loadTestsFromNames hook, doesn't set event.handled=True and sets events.names to something Falsey (e.g. []), suites is never assigned.
I think there could be a scenario in which a plugin would want to remove certain or all test names from events.names without setting handled=True, and that suites should default to an empty list in this case.

Example plugin (run with any test):

class Example(Plugin):
    alwaysOn = True

    def loadTestsFromNames(self, event):
        event.names = []

Trace:

  File ".../bin/nose2", line 10, in <module>
    sys.exit(discover())
  File ".../lib/python3.7/site-packages/nose2/main.py", line 301, in discover
    return main(*args, **kwargs)
  File ".../lib/python3.7/site-packages/nose2/main.py", line 87, in __init__
    super(PluggableTestProgram, self).__init__(**kw)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/main.py", line 100, in __init__
    self.parseArgs(argv)
  File ".../lib/python3.7/site-packages/nose2/main.py", line 120, in parseArgs
    self.createTests()
  File ".../lib/python3.7/site-packages/nose2/main.py", line 253, in createTests
    self.testNames, self.module)
  File ".../lib/python3.7/site-packages/nose2/loader.py", line 73, in loadTestsFromNames
    return self.suiteClass(suites)
UnboundLocalError: local variable 'suites' referenced before assignment

Relevant method in loader.py:

def loadTestsFromNames(self, testNames, module=None):
        """Load tests from test names.

        Fires :func:`loadTestsFromNames` hook.

        """
        event = events.LoadFromNamesEvent(
            self, testNames, module)
        result = self.session.hooks.loadTestsFromNames(event)
        log.debug('loadTestsFromNames event %s result %s', event, result)
        if event.handled:
            suites = result or []
        else:
            if event.names:
                suites = [self.loadTestsFromName(name, module)
                          for name in event.names]
            elif module:
                suites = self.loadTestsFromModule(module)
        if event.extraTests:
            suites.extend(event.extraTests)
        return self.suiteClass(suites)
mgrandi commented 4 years ago

this also happens if you just do nose2 --no-plugins (using version 0.9.2)

$ nose2 --no-plugins
Traceback (most recent call last):
  File "c:\python38\lib\runpy.py", line 193, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "c:\python38\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Users\mark\AppData\Local\pypoetry\Cache\virtualenvs\telegram-dl-0Lz1B2mN-py3.8\Scripts\nose2.exe\__main__.py", line 9, in <module>
  File "c:\users\mark\appdata\local\pypoetry\cache\virtualenvs\telegram-dl-0lz1b2mn-py3.8\lib\site-packages\nose2\main.py", line 301, in discover
    return main(*args, **kwargs)
  File "c:\users\mark\appdata\local\pypoetry\cache\virtualenvs\telegram-dl-0lz1b2mn-py3.8\lib\site-packages\nose2\main.py", line 87, in __init__
    super(PluggableTestProgram, self).__init__(**kw)
  File "c:\python38\lib\unittest\main.py", line 100, in __init__
    self.parseArgs(argv)
  File "c:\users\mark\appdata\local\pypoetry\cache\virtualenvs\telegram-dl-0lz1b2mn-py3.8\lib\site-packages\nose2\main.py", line 120, in parseArgs
    self.createTests()
  File "c:\users\mark\appdata\local\pypoetry\cache\virtualenvs\telegram-dl-0lz1b2mn-py3.8\lib\site-packages\nose2\main.py", line 252, in createTests
    test = self.testLoader.loadTestsFromNames(
  File "c:\users\mark\appdata\local\pypoetry\cache\virtualenvs\telegram-dl-0lz1b2mn-py3.8\lib\site-packages\nose2\loader.py", line 73, in loadTestsFromNames
    return self.suiteClass(suites)
UnboundLocalError: local variable 'suites' referenced before assignment