def test_iteration(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) # There should be two sessions in the queue. assert len(manifest._queue) == 2 assert len(manifest._consumed) == 0 # The first item should be our "foo" session. foo = next(manifest) assert foo.func == sessions["foo"] assert foo in manifest._consumed assert foo not in manifest._queue assert len(manifest._consumed) == 1 assert len(manifest._queue) == 1 # The .next() or .__next__() methods can be called directly according # to Python's data model. bar = manifest.next() assert bar.func == sessions["bar"] assert bar in manifest._consumed assert bar not in manifest._queue assert len(manifest._consumed) == 2 assert len(manifest._queue) == 0 # Continuing past the end raises StopIteration. with pytest.raises(StopIteration): manifest.__next__()
def test_run_manifest(): # Set up a valid manifest. config = argparse.Namespace(stop_on_first_error=False) sessions_ = [ mock.Mock(spec=sessions.SessionRunner), mock.Mock(spec=sessions.SessionRunner), ] manifest = Manifest({}, config) manifest._queue = copy.copy(sessions_) # Ensure each of the mocks returns a successful result for mock_session in sessions_: mock_session.execute.return_value = sessions.Result( session=mock_session, status=sessions.Status.SUCCESS ) # Run the manifest. results = tasks.run_manifest(manifest, global_config=config) # Verify the results look correct. assert len(results) == 2 assert results[0].session == sessions_[0] assert results[1].session == sessions_[1] for result in results: assert isinstance(result, sessions.Result) assert result.status == sessions.Status.SUCCESS
def test_run_manifest_abort_on_first_failure(): # Set up a valid manifest. config = argparse.Namespace(stop_on_first_error=True) sessions_ = [ mock.Mock(spec=sessions.SessionRunner), mock.Mock(spec=sessions.SessionRunner), ] manifest = Manifest({}, config) manifest._queue = copy.copy(sessions_) # Ensure each of the mocks returns a successful result. for mock_session in sessions_: mock_session.execute.return_value = sessions.Result( session=mock_session, status=sessions.Status.FAILED ) # Run the manifest. results = tasks.run_manifest(manifest, global_config=config) # Verify the results look correct. assert len(results) == 1 assert isinstance(results[0], sessions.Result) assert results[0].session == sessions_[0] assert results[0].status == sessions.Status.FAILED # Verify that only the first session was called. assert sessions_[0].execute.called assert not sessions_[1].execute.called
def test_add_session_idempotent(): manifest = Manifest({}, mock.sentinel.CONFIG) session_func = mock.Mock(spec=(), python=None) for session in manifest.make_session("my_session", session_func): manifest.add_session(session) manifest.add_session(session) assert len(manifest) == 1
def test_filter_by_keyword(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) assert len(manifest) == 2 manifest.filter_by_keywords("foo or bar") assert len(manifest) == 2 manifest.filter_by_keywords("foo") assert len(manifest) == 1
def test_add_session_multiple_pythons(): manifest = Manifest({}, mock.sentinel.CONFIG) def session_func(): pass session_func.python = ["3.5", "3.6"] for session in manifest.make_session("my_session", session_func): manifest.add_session(session) assert len(manifest) == 2
def test_add_session_parametrized_multiple_pythons(): manifest = Manifest({}, mock.sentinel.CONFIG) # Define a session with parameters. @nox.parametrize("param", ("a", "b")) def my_session(session, param): pass my_session.python = ["2.7", "3.6"] # Add the session to the manifest. for session in manifest.make_session("my_session", my_session): manifest.add_session(session) assert len(manifest) == 4
def test_add_session_parametrized_noop(): manifest = Manifest({}, mock.sentinel.CONFIG) # Define a session without any parameters. @nox.parametrize("param", ()) def my_session(session, param): pass my_session.python = None # Add the session to the manifest. for session in manifest.make_session("my_session", my_session): manifest.add_session(session) assert len(manifest) == 1 session = manifest["my_session"] assert session.func == _null_session_func
def test_getitem(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) # Establish that each session is present, and a made-up session # is not. assert manifest['foo'].func is sessions['foo'] assert manifest['bar'].func is sessions['bar'] with pytest.raises(KeyError): manifest['baz'] # Establish that the sessions are still present even after being # consumed by iteration. for session in manifest: pass assert manifest['foo'].func is sessions['foo'] assert manifest['bar'].func is sessions['bar']
def test_getitem(): sessions = create_mock_sessions() manifest = Manifest(sessions, create_mock_config()) # Establish that each session is present, and a made-up session # is not. assert manifest["foo"].func is sessions["foo"] assert manifest["bar"].func is sessions["bar"] with pytest.raises(KeyError): manifest["baz"] # Establish that the sessions are still present even after being # consumed by iteration. for session in manifest: pass assert manifest["foo"].func is sessions["foo"] assert manifest["bar"].func is sessions["bar"]
def discover_manifest(module, global_config): """Discover all session functions in the noxfile module. Args: module (module): The Noxfile module. global_config (~nox.main.GlobalConfig): The global configuration. Returns: ~.Manifest: A manifest of session functions. """ # Find any function added to the session registry (meaning it was # decorated with @nox.session); do not sort these, as they are being # sorted by decorator call time. functions = registry.get() # Return the final dictionary of session functions. return Manifest(functions, global_config)
def test_contains(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) # Establish that contains works pre-iteration. assert "foo" in manifest assert "bar" in manifest assert "baz" not in manifest # Establish that __contains__ works post-iteration. for session in manifest: pass assert "foo" in manifest assert "bar" in manifest assert "baz" not in manifest # Establish that sessions themselves work. assert manifest["foo"] in manifest
def test_notify_noop(): manifest = Manifest({}, mock.sentinel.CONFIG) # Define a session and add it to the manifest. def my_session(session): pass my_session.python = None for session in manifest.make_session("my_session", my_session): manifest.add_session(session) assert len(manifest) == 1 # Establish idempotency; notifying a session already in the queue no-ops. manifest.notify("my_session") assert len(manifest) == 1
def test_notify_noop(): manifest = Manifest({}, create_mock_config()) # Define a session and add it to the manifest. def my_session(session): pass my_session.python = None my_session.venv_backend = None for session in manifest.make_session("my_session", my_session): manifest.add_session(session) assert len(manifest) == 1 # Establish idempotency; notifying a session already in the queue no-ops. manifest.notify("my_session") assert len(manifest) == 1
def discover_manifest(module: types.ModuleType | int, global_config: Namespace) -> Manifest: """Discover all session functions in the Noxfile module. Args: module (module): The Noxfile module. global_config (~nox.main.GlobalConfig): The global configuration. Returns: ~.Manifest: A manifest of session functions. """ # Find any function added to the session registry (meaning it was # decorated with @nox.session); do not sort these, as they are being # sorted by decorator call time. functions = registry.get() # Get the docstring from the Noxfile module_docstring = module.__doc__ # Return the final dictionary of session functions. return Manifest(functions, global_config, module_docstring)
def filter_manifest( manifest: Manifest, global_config: Namespace ) -> Union[Manifest, int]: """Filter the manifest according to the provided configuration. Args: manifest (~.Manifest): The manifest of sessions to be run. global_config (~nox.main.GlobalConfig): The global configuration. Returns: Union[~.Manifest,int]: ``3`` if a specified session is not found, the manifest otherwise (to be sent to the next task). """ # Filter by the name of any explicit sessions. # This can raise KeyError if a specified session does not exist; # log this if it happens. if global_config.sessions: try: manifest.filter_by_name(global_config.sessions) except KeyError as exc: logger.error("Error while collecting sessions.") logger.error(exc.args[0]) return 3 # Filter by python interpreter versions. # This function never errors, but may cause an empty list of sessions # (which is an error condition later). if global_config.pythons: manifest.filter_by_python_interpreter(global_config.pythons) # Filter by keywords. if global_config.keywords: try: ast.parse(global_config.keywords, mode="eval") except SyntaxError: logger.error( "Error while collecting sessions: keywords argument must be a Python expression." ) return 3 # This function never errors, but may cause an empty list of sessions # (which is an error condition later). manifest.filter_by_keywords(global_config.keywords) # Return the modified manifest. return manifest
def _produce_listing(manifest: Manifest, global_config: Namespace) -> None: # If the user just asked for a list of sessions, print that # and any docstring specified in noxfile.py and be done. This # can also be called if Noxfile sessions is an empty list. if manifest.module_docstring: print(manifest.module_docstring.strip(), end="\n\n") print(f"Sessions defined in {global_config.noxfile}:\n") reset = parse_colors("reset") if global_config.color else "" selected_color = parse_colors("cyan") if global_config.color else "" skipped_color = parse_colors("white") if global_config.color else "" for session, selected in manifest.list_all_sessions(): output = "{marker} {color}{session}{reset}" if selected: marker = "*" color = selected_color else: marker = "-" color = skipped_color if session.description is not None: output += " -> {description}" print( output.format( color=color, reset=reset, session=session.friendly_name, description=session.description, marker=marker, )) print( f"\nsessions marked with {selected_color}*{reset} are selected, sessions marked" f" with {skipped_color}-{reset} are skipped.")
def test_filter_manifest_not_found(): config = argparse.Namespace(sessions=("baz",), keywords=()) manifest = Manifest({"foo": session_func, "bar": session_func}, config) return_value = tasks.filter_manifest(manifest, config) assert return_value == 3
def test_filter_by_name(): sessions = create_mock_sessions() manifest = Manifest(sessions, create_mock_config()) manifest.filter_by_name(("foo",)) assert "foo" in manifest assert "bar" not in manifest
def filter_manifest(manifest: Manifest, global_config: Namespace) -> Manifest | int: """Filter the manifest according to the provided configuration. Args: manifest (~.Manifest): The manifest of sessions to be run. global_config (~nox.main.GlobalConfig): The global configuration. Returns: Union[~.Manifest,int]: ``3`` if a specified session is not found, the manifest otherwise (to be sent to the next task). """ # Shouldn't happen unless the Noxfile is empty if not manifest: logger.error(f"No sessions found in {global_config.noxfile}.") return 3 # Filter by the name of any explicit sessions. # This can raise KeyError if a specified session does not exist; # log this if it happens. The sessions does not come from the Noxfile # if keywords is not empty. if global_config.sessions is not None: try: manifest.filter_by_name(global_config.sessions) except KeyError as exc: logger.error("Error while collecting sessions.") logger.error(exc.args[0]) return 3 if not manifest and not global_config.list_sessions: print( "No sessions selected. Please select a session with -s <session name>.\n" ) _produce_listing(manifest, global_config) return 0 # Filter by python interpreter versions. if global_config.pythons: manifest.filter_by_python_interpreter(global_config.pythons) if not manifest and not global_config.list_sessions: logger.error( "Python version selection caused no sessions to be selected.") return 3 # Filter by tags. if global_config.tags is not None: manifest.filter_by_tags(global_config.tags) if not manifest and not global_config.list_sessions: logger.error("Tag selection caused no sessions to be selected.") return 3 # Filter by keywords. if global_config.keywords: try: ast.parse(global_config.keywords, mode="eval") except SyntaxError: logger.error( "Error while collecting sessions: keywords argument must be a Python" " expression.") return 3 # This function never errors, but may cause an empty list of sessions # (which is an error condition later). manifest.filter_by_keywords(global_config.keywords) if not manifest and not global_config.list_sessions: logger.error("No sessions selected after filtering by keyword.") return 3 # Return the modified manifest. return manifest
def test_notify(): manifest = Manifest({}, mock.sentinel.CONFIG) # Define a session. def my_session(session): pass my_session.python = None def notified(session): pass notified.python = None # Add the sessions to the manifest. for session in manifest.make_session("my_session", my_session): manifest.add_session(session) for session in manifest.make_session("notified", notified): manifest.add_session(session) assert len(manifest) == 2 # Filter so only the first session is included in the queue. manifest.filter_by_name(("my_session",)) assert len(manifest) == 1 # Notify the notified session. manifest.notify("notified") assert len(manifest) == 2
def test_add_session_plain(): manifest = Manifest({}, mock.sentinel.CONFIG) session_func = mock.Mock(spec=(), python=None) for session in manifest.make_session("my_session", session_func): manifest.add_session(session) assert len(manifest) == 1
def test_filter_by_name(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) manifest.filter_by_name(('foo', )) assert 'foo' in manifest assert 'bar' not in manifest
def test_filter_by_name_maintains_order(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) manifest.filter_by_name(("bar", "foo")) assert [session.name for session in manifest] == ["bar", "foo"]
def test_notify_error(): manifest = Manifest({}, create_mock_config()) with pytest.raises(ValueError): manifest.notify("does_not_exist")
def test_add_session_plain(): manifest = Manifest({}, create_mock_config()) session_func = mock.Mock(spec=(), python=None, venv_backend=None) for session in manifest.make_session("my_session", session_func): manifest.add_session(session) assert len(manifest) == 1
def test_filter_by_name_not_found(): sessions = create_mock_sessions() manifest = Manifest(sessions, create_mock_config()) with pytest.raises(KeyError): manifest.filter_by_name(("baz",))
def test_filter_by_name_maintains_order(): sessions = create_mock_sessions() manifest = Manifest(sessions, create_mock_config()) manifest.filter_by_name(("bar", "foo")) assert [session.name for session in manifest] == ["bar", "foo"]
def test_len(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) assert len(manifest) == 2 for session in manifest: assert len(manifest) == 2
def test_verify_manifest_empty(): config = _options.options.namespace(sessions=(), keywords=()) manifest = Manifest({}, config) return_value = tasks.verify_manifest_nonempty(manifest, global_config=config) assert return_value == 3
def test_filter_by_name(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) manifest.filter_by_name(("foo", )) assert "foo" in manifest assert "bar" not in manifest
def test_verify_manifest_nonempty(): config = _options.options.namespace(sessions=(), keywords=(), posargs=[]) manifest = Manifest({"session": session_func}, config) return_value = tasks.verify_manifest_nonempty(manifest, global_config=config) assert return_value == manifest
def test_filter_by_name_not_found(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) with pytest.raises(KeyError): manifest.filter_by_name(("baz", ))
def test_verify_manifest_nonempty(): config = argparse.Namespace(sessions=(), keywords=()) manifest = Manifest({"session": session_func}, config) return_value = tasks.verify_manifest_nonempty(manifest, global_config=config) assert return_value == manifest
def test_notify(): manifest = Manifest({}, mock.sentinel.CONFIG) # Define a session. def my_session(session): pass my_session.python = None def notified(session): pass notified.python = None # Add the sessions to the manifest. for session in manifest.make_session("my_session", my_session): manifest.add_session(session) for session in manifest.make_session("notified", notified): manifest.add_session(session) assert len(manifest) == 2 # Filter so only the first session is included in the queue. manifest.filter_by_name(("my_session", )) assert len(manifest) == 1 # Notify the notified session. manifest.notify("notified") assert len(manifest) == 2
def test_add_session_idempotent(): manifest = Manifest({}, mock.sentinel.CONFIG) for session in manifest.make_session('my_session', lambda session: None): manifest.add_session(session) manifest.add_session(session) assert len(manifest) == 1
def test_notify_error(): manifest = Manifest({}, mock.sentinel.CONFIG) with pytest.raises(ValueError): manifest.notify("does_not_exist")
def test_filter_by_name_not_found(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) with pytest.raises(KeyError): manifest.filter_by_name(("baz",))
def test_filter_manifest(): config = argparse.Namespace(sessions=(), keywords=()) manifest = Manifest({"foo": session_func, "bar": session_func}, config) return_value = tasks.filter_manifest(manifest, config) assert return_value is manifest assert len(manifest) == 2
def test_filter_by_name(): sessions = create_mock_sessions() manifest = Manifest(sessions, mock.sentinel.CONFIG) manifest.filter_by_name(("foo",)) assert "foo" in manifest assert "bar" not in manifest
def test_len(): sessions = create_mock_sessions() manifest = Manifest(sessions, create_mock_config()) assert len(manifest) == 2 for session in manifest: assert len(manifest) == 2