Ejemplo n.º 1
0
def run(global_config):
    try:
        user_nox_module = load_user_nox_module(global_config.noxfile)
    except IOError:
        logger.error('Noxfile {} not found.'.format(global_config.noxfile))
        return False

    session_functions = discover_session_functions(user_nox_module)
    sessions = make_sessions(session_functions, global_config)

    if global_config.list_sessions:
        print('Available sessions:')
        for session in sessions:
            print('*', session.signature or session.name)
        return True

    if global_config.sessions:
        sessions = [x for x in sessions if (
            x.name in global_config.sessions or
            x.signature in global_config.sessions)]

    success = True

    for session in sessions:
        result = session.execute()
        success = success and result
        if not success and global_config.stop_on_first_error:
            return False

    return success
Ejemplo n.º 2
0
def _normalize_path(envdir, path):
    """Normalizes a string to be a "safe" filesystem path for a virtualenv."""
    if isinstance(path, bytes):
        path = path.decode("utf-8")

    path = unicodedata.normalize("NFKD", path).encode("ascii", "ignore")
    path = path.decode("ascii")
    path = re.sub(r"[^\w\s-]", "-", path).strip().lower()
    path = re.sub(r"[-\s]+", "-", path)
    path = path.strip("-")

    full_path = os.path.join(envdir, path)
    if len(full_path) > 100 - len("bin/pythonX.Y"):
        if len(envdir) < 100 - 9:
            path = hashlib.sha1(path.encode("ascii")).hexdigest()[:8]
            full_path = os.path.join(envdir, path)
            logger.warning("The virtualenv name was hashed to avoid being too long.")
        else:
            logger.error(
                "The virtualenv path {} is too long and will cause issues on "
                "some environments. Use the --envdir path to modify where "
                "nox stores virtualenvs.".format(full_path)
            )

    return full_path
Ejemplo n.º 3
0
    def run(self):
        cmd, args = self.args[0], self.args[1:]
        full_cmd = ' '.join(self.args)

        logger.info(full_cmd)

        cmd_path = which(cmd, self.path)

        try:
            return_code, output = popen([cmd_path] + list(args),
                                        silent=self.silent,
                                        env=self.env)

            if return_code not in self.success_codes:
                logger.error('Command {} failed with exit code {}{}'.format(
                    full_cmd, return_code, ':' if self.silent else ''))

                if self.silent:
                    sys.stderr.write(output)

                raise CommandFailed('Returned code {}'.format(return_code))

            return output if self.silent else True

        except KeyboardInterrupt:
            logger.error('Interrupted...')
            raise
Ejemplo n.º 4
0
    def execute(self):
        session_friendly_name = self.signature or self.name
        logger.warning('Running session {}'.format(session_friendly_name))

        try:
            # Set up the SessionConfig object (which session functions refer
            # to as `session`) and then the virtualenv.
            self._create_config()
            self._create_venv()

            # Run the actual commands prescribed by the session.
            cwd = py.path.local(os.getcwd()).as_cwd()
            with cwd:
                self._run_commands()

            # Nothing went wrong; return a success.
            return Result(self, Status.SUCCESS)

        except _SessionQuit:
            return Result(self, Status.ABORTED)

        except _SessionSkip:
            return Result(self, Status.SKIPPED)

        except CommandFailed:
            return Result(self, Status.FAILED)

        except KeyboardInterrupt:
            logger.error('Session {} interrupted.'.format(self))
            raise
Ejemplo n.º 5
0
def load_nox_module(global_config):
    """Load the user's noxfile and return the module object for it.

    .. note::

        This task has two side effects; it makes ``global_config.noxfile``
        an absolute path, and changes the working directory of the process.

    Args:
        global_config (.nox.main.GlobalConfig): The global config.

    Returns:
        module: The module designated by the Noxfile path.
    """
    try:
        # Save the absolute path to the Noxfile.
        # This will inoculate it if nox changes paths because of an implicit
        # or explicit chdir (like the one below).
        global_config.noxfile = os.path.realpath(
            # Be sure to expand variables
            os.path.expandvars(global_config.noxfile)
        )

        # Move to the path where the Noxfile is.
        # This will ensure that the Noxfile's path is on sys.path, and that
        # import-time path resolutions work the way the Noxfile author would
        # guess.
        os.chdir(os.path.realpath(os.path.dirname(global_config.noxfile)))
        return imp.load_source("user_nox_module", global_config.noxfile)
    except (IOError, OSError):
        logger.error("Noxfile {} not found.".format(global_config.noxfile))
        return 2
Ejemplo n.º 6
0
def filter_manifest(manifest, global_config):
    """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 keywords.
    # This function never errors, but may cause an empty list of sessions
    # (which is an error condition later).
    if global_config.keywords:
        manifest.filter_by_keywords(global_config.keywords)

    # Return the modified manifest.
    return manifest
Ejemplo n.º 7
0
def load_nox_module(global_config):
    """Load the user's noxfile and return the module object for it.

    .. note::

        This task has two side effects; it makes ``global_config.noxfile``
        an absolute path, and changes the working directory of the process.

    Args:
        global_config (.nox.main.GlobalConfig): The global config.

    Returns:
        module: The module designated by the Noxfile path.
    """
    try:
        # Save the absolute path to the Noxfile.
        # This will inoculate it if nox changes paths because of an implicit
        # or explicit chdir (like the one below).
        global_config.noxfile = os.path.realpath(
            # Be sure to expand variables
            os.path.expandvars(global_config.noxfile)
        )

        # Move to the path where the Noxfile is.
        # This will ensure that the Noxfile's path is on sys.path, and that
        # import-time path resolutions work the way the Noxfile author would
        # guess.
        os.chdir(os.path.realpath(os.path.dirname(global_config.noxfile)))
        return imp.load_source("user_nox_module", global_config.noxfile)
    except (IOError, OSError):
        logger.error("Noxfile {} not found.".format(global_config.noxfile))
        return 2
Ejemplo n.º 8
0
    def execute(self):
        logger.warning('Running session {}'.format(self.signature
                                                   or self.name))

        try:
            self._create_config()
            self._create_venv()
            self._install_dependencies()

            if self.config._dir != '.':
                logger.info('Changing directory to {}'.format(
                    self.config._dir))

            cwd = py.path.local(self.config._dir).as_cwd()
            with cwd:
                self._run_commands()

            logger.success('Session {} successful. :)'.format(self.name))
            return True

        except CommandFailed:
            logger.error('Session {} failed. :('.format(self.name))
            return False

        except KeyboardInterrupt:
            logger.error('Session {} interrupted.'.format(self.name))
            raise
Ejemplo n.º 9
0
def _normalize_path(envdir, path):
    """Normalizes a string to be a "safe" filesystem path for a virtualenv."""
    if isinstance(path, six.binary_type):
        path = path.decode('utf-8')

    path = unicodedata.normalize('NFKD', path).encode('ascii', 'ignore')
    path = path.decode('ascii')
    path = re.sub('[^\w\s-]', '-', path).strip().lower()
    path = re.sub('[-\s]+', '-', path)
    path = path.strip('-')

    full_path = os.path.join(envdir, path)
    if len(full_path) > 100 - len('bin/pythonX.Y'):
        if len(envdir) < 100 - 9:
            path = hashlib.sha1(path.encode('ascii')).hexdigest()[:8]
            full_path = os.path.join(envdir, path)
            logger.warning(
                'The virtualenv name was hashed to avoid being too long.')
        else:
            logger.error(
                'The virtualenv path {} is too long and will cause issues on '
                'some environments. Use the --envdir path to modify where '
                'nox stores virtualenvs.'.format(full_path))

    return full_path
Ejemplo n.º 10
0
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 keywords.
    # This function never errors, but may cause an empty list of sessions
    # (which is an error condition later).
    if global_config.keywords:
        manifest.filter_by_keywords(global_config.keywords)

    # Return the modified manifest.
    return manifest
Ejemplo n.º 11
0
def run(global_config):
    try:
        user_nox_module = load_user_nox_module(global_config.noxfile)
    except IOError:
        logger.error('Noxfile {} not found.'.format(global_config.noxfile))
        return False

    session_functions = discover_session_functions(user_nox_module)
    sessions = make_sessions(session_functions, global_config)

    if global_config.list_sessions:
        print('Available sessions:')
        for session in sessions:
            print('*', session.signature or session.name)
        return True

    if global_config.sessions:
        sessions = [
            x for x in sessions if (x.name in global_config.sessions
                                    or x.signature in global_config.sessions)
        ]

    success = True

    for session in sessions:
        result = session.execute()
        success = success and result
        if not success and global_config.stop_on_first_error:
            return False

    return success
Ejemplo n.º 12
0
def _normalize_path(envdir, path):
    """Normalizes a string to be a "safe" filesystem path for a virtualenv."""
    if isinstance(path, bytes):
        path = path.decode("utf-8")

    path = unicodedata.normalize("NFKD", path).encode("ascii", "ignore")
    path = path.decode("ascii")
    path = re.sub(r"[^\w\s-]", "-", path).strip().lower()
    path = re.sub(r"[-\s]+", "-", path)
    path = path.strip("-")

    full_path = os.path.join(envdir, path)
    if len(full_path) > 100 - len("bin/pythonX.Y"):
        if len(envdir) < 100 - 9:
            path = hashlib.sha1(path.encode("ascii")).hexdigest()[:8]
            full_path = os.path.join(envdir, path)
            logger.warning("The virtualenv name was hashed to avoid being too long.")
        else:
            logger.error(
                "The virtualenv path {} is too long and will cause issues on "
                "some environments. Use the --envdir path to modify where "
                "nox stores virtualenvs.".format(full_path)
            )

    return full_path
Ejemplo n.º 13
0
Archivo: main.py Proyecto: tseaver/nox
def run(global_config):
    try:
        # Save the absolute path to the Noxfile.
        # This will innoculate it if nox changes paths because of an implicit
        # or explicit chdir (like the one below).
        global_config.noxfile = os.path.realpath(global_config.noxfile)

        # Move to the path where the Noxfile is.
        # This will ensure that the Noxfile's path is on sys.path, and that
        # import-time path resolutions work the way the Noxfile author would
        # guess.
        os.chdir(os.path.realpath(os.path.dirname(global_config.noxfile)))
        user_nox_module = load_user_nox_module(global_config.noxfile)
    except (IOError, OSError):
        logger.error('Noxfile {} not found.'.format(global_config.noxfile))
        return False

    session_functions = discover_session_functions(user_nox_module)
    sessions = make_sessions(session_functions, global_config)

    if global_config.sessions:
        sessions = filter_sessions_by_name(global_config.sessions, sessions)

    if global_config.keywords:
        sessions = filter_sessions_by_keywords(global_config.keywords,
                                               sessions)

    if global_config.list_sessions:
        print('Available sessions:')
        for session in sessions:
            print('*', session.signature or session.name)
        return True

    if not sessions:
        return False

    success = True
    results = []

    for session in sessions:
        result = session.execute()
        results.append((session, result))
        success = success and result
        if not success and global_config.stop_on_first_error:
            success = False
            break

    if len(results) > 1:
        print_summary(results)

    if global_config.report is not None:
        create_report(global_config.report, success, results)

    return success
Ejemplo n.º 14
0
    def run(self, path_override=None, env_fallback=None, **kwargs):
        path = self.path if path_override is None else path_override

        env = env_fallback.copy() if env_fallback is not None else None
        if self.env is not None:
            if env is None:
                env = self.env
            else:
                env.update(self.env)

        cmd, args = self.args[0], self.args[1:]
        full_cmd = ' '.join(self.args)

        logger.info(full_cmd)

        cmd_path = which(cmd, path)

        # Environment variables must be the "str" type.
        # In other words, they must be bytestrings in Python 2, and Unicode
        # text strings in Python 3.
        clean_env = {} if env is not None else None
        if clean_env is not None:
            # Ensure systemroot is passed down, otherwise Windows will explode.
            clean_env[str('SYSTEMROOT')] = os.environ.get(
                'SYSTEMROOT', str(''))

            for key, value in six.iteritems(env):
                key = coerce_str(key)
                value = coerce_str(value)
                clean_env[key] = value

        try:
            return_code, output = popen(
                [cmd_path] + list(args),
                silent=self.silent,
                env=clean_env)

            if return_code not in self.success_codes:
                logger.error('Command {} failed with exit code {}{}'.format(
                    full_cmd, return_code, ':' if self.silent else ''))

                if self.silent:
                    sys.stderr.write(output)

                raise CommandFailed('Returned code {}'.format(return_code))

            return output if self.silent else True

        except KeyboardInterrupt:
            logger.error('Interrupted...')
            raise
Ejemplo n.º 15
0
def filter_sessions(specified_sessions, available_sessions):
    sessions = [x for x in available_sessions if (
        x.name in specified_sessions or
        x.signature in specified_sessions)]
    missing_sessions = set(specified_sessions) - set(
        itertools.chain(
            [x.name for x in sessions if x.name],
            [x.signature for x in sessions if x.signature]))
    if missing_sessions:
        logger.error('Sessions {} not found.'.format(', '.join(
            missing_sessions)))
        return False

    return sessions
Ejemplo n.º 16
0
def load_nox_module(global_config: Namespace) -> types.ModuleType | int:
    """Load the user's Noxfile and return the module object for it.

    .. note::

        This task has two side effects; it makes ``global_config.noxfile``
        an absolute path, and changes the working directory of the process.

    Args:
        global_config (.nox.main.GlobalConfig): The global config.

    Returns:
        module: The module designated by the Noxfile path.
    """
    try:
        # Save the absolute path to the Noxfile.
        # This will inoculate it if Nox changes paths because of an implicit
        # or explicit chdir (like the one below).
        global_config.noxfile = os.path.realpath(
            # Be sure to expand variables
            os.path.expandvars(global_config.noxfile))
        noxfile_parent_dir = os.path.realpath(
            os.path.dirname(global_config.noxfile))

        # Check ``nox.needs_version`` by parsing the AST.
        check_nox_version(global_config.noxfile)

        # Move to the path where the Noxfile is.
        # This will ensure that the Noxfile's path is on sys.path, and that
        # import-time path resolutions work the way the Noxfile author would
        # guess. The original working directory (the directory that Nox was
        # invoked from) gets stored by the .invoke_from "option" in _options.
        os.chdir(noxfile_parent_dir)

    except (VersionCheckFailed, InvalidVersionSpecifier) as error:
        logger.error(str(error))
        return 2
    except FileNotFoundError:
        logger.error(
            f"Failed to load Noxfile {global_config.noxfile}, no such file exists."
        )
        return 2
    except OSError:
        logger.exception(f"Failed to load Noxfile {global_config.noxfile}")
        return 2
    else:
        return _load_and_exec_nox_module(global_config)
Ejemplo n.º 17
0
def which(program: str, paths: list[str] | None) -> str:
    """Finds the full path to an executable."""
    full_path = None

    if paths:
        full_path = py.path.local.sysfind(program, paths=paths)

    if full_path:
        return full_path.strpath  # type: ignore[no-any-return]

    full_path = py.path.local.sysfind(program)

    if full_path:
        return full_path.strpath  # type: ignore[no-any-return]

    logger.error(f"Program {program} not found.")
    raise CommandFailed(f"Program {program} not found")
Ejemplo n.º 18
0
def which(program: str, paths: Optional[List[str]]) -> str:
    """Finds the full path to an executable."""
    full_path = None

    if paths:
        full_path = py.path.local.sysfind(program, paths=paths)

    if full_path:
        return full_path.strpath

    full_path = py.path.local.sysfind(program)

    if full_path:
        return full_path.strpath

    logger.error(f"Program {program} not found.")
    raise CommandFailed(f"Program {program} not found")
Ejemplo n.º 19
0
def which(program, path):
    """Finds the full path to an executable."""
    full_path = None

    if path:
        full_path = py.path.local.sysfind(program, paths=[path])

    if full_path:
        return full_path.strpath

    full_path = py.path.local.sysfind(program)

    if full_path:
        return full_path.strpath

    logger.error("Program {} not found.".format(program))
    raise CommandFailed("Program {} not found".format(program))
Ejemplo n.º 20
0
def which(program: str, path: Optional[str]) -> str:
    """Finds the full path to an executable."""
    full_path = None

    if path:
        full_path = py.path.local.sysfind(program, paths=[path])

    if full_path:
        return full_path.strpath

    full_path = py.path.local.sysfind(program)

    if full_path:
        return full_path.strpath

    logger.error("Program {} not found.".format(program))
    raise CommandFailed("Program {} not found".format(program))
Ejemplo n.º 21
0
def which(program, path):
    """Finds the full path to an executable."""
    full_path = None

    if path:
        full_path = py.path.local.sysfind(program, paths=[path])

    if full_path:
        return full_path.strpath

    full_path = py.path.local.sysfind(program)

    if full_path:
        return full_path.strpath

    logger.error('Program {} not found.'.format(program))
    raise CommandFailed('Program {} not found'.format(program))
Ejemplo n.º 22
0
    def run(self, path_override=None, env_override=None):
        path = self.path if path_override is None else path_override
        env = self.env if env_override is None else env_override

        cmd, args = self.args[0], self.args[1:]
        full_cmd = ' '.join(self.args)

        logger.info(full_cmd)

        cmd_path = which(cmd, path)

        # Environment variables must be bytestrings.
        clean_env = {} if env is not None else None
        if clean_env is not None:
            # Ensure systemroot is passed down, otherwise Windows will explode.
            clean_env[str('SYSTEMROOT')] = os.environ.get(
                'SYSTEMROOT', str(''))

            for key, value in six.iteritems(env):
                if not isinstance(key, six.text_type):
                    key = key.decode('utf-8')
                if not isinstance(value, six.text_type):
                    value = value.decode('utf-8')
                clean_env[key] = value

        try:
            return_code, output = popen([cmd_path] + list(args),
                                        silent=self.silent,
                                        env=clean_env)

            if return_code not in self.success_codes:
                logger.error('Command {} failed with exit code {}{}'.format(
                    full_cmd, return_code, ':' if self.silent else ''))

                if self.silent:
                    sys.stderr.write(output)

                raise CommandFailed('Returned code {}'.format(return_code))

            return output if self.silent else True

        except KeyboardInterrupt:
            logger.error('Interrupted...')
            raise
Ejemplo n.º 23
0
    def execute(self) -> Result:
        logger.warning(f"Running session {self.friendly_name}")

        try:
            # By default, Nox should quietly change to the directory where
            # the noxfile.py file is located.
            cwd = py.path.local(
                os.path.realpath(os.path.dirname(
                    self.global_config.noxfile))).as_cwd()

            with cwd:
                self._create_venv()
                session = Session(self)
                self.func(session)

            # Nothing went wrong; return a success.
            return Result(self, Status.SUCCESS)

        except nox.virtualenv.InterpreterNotFound as exc:
            if self.global_config.error_on_missing_interpreters:
                return Result(self, Status.FAILED, reason=str(exc))
            else:
                logger.warning(
                    "Missing interpreters will error by default on CI systems."
                )
                return Result(self, Status.SKIPPED, reason=str(exc))

        except _SessionQuit as exc:
            return Result(self, Status.ABORTED, reason=str(exc))

        except _SessionSkip as exc:
            return Result(self, Status.SKIPPED, reason=str(exc))

        except nox.command.CommandFailed:
            return Result(self, Status.FAILED)

        except KeyboardInterrupt:
            logger.error(f"Session {self.friendly_name} interrupted.")
            raise

        except Exception as exc:
            logger.exception(
                f"Session {self.friendly_name} raised exception {exc!r}")
            return Result(self, Status.FAILED)
Ejemplo n.º 24
0
    def execute(self):
        logger.warning("Running session {}".format(self.friendly_name))

        try:
            # By default, nox should quietly change to the directory where
            # the noxfile.py file is located.
            cwd = py.path.local(
                os.path.realpath(os.path.dirname(self.global_config.noxfile))
            ).as_cwd()

            with cwd:
                self._create_venv()
                session = Session(self)
                self.func(session)

            # Nothing went wrong; return a success.
            return Result(self, Status.SUCCESS)

        except nox.virtualenv.InterpreterNotFound as exc:
            if self.global_config.error_on_missing_interpreters:
                return Result(self, Status.FAILED, reason=str(exc))
            else:
                return Result(self, Status.SKIPPED, reason=str(exc))

        except _SessionQuit as exc:
            return Result(self, Status.ABORTED, reason=str(exc))

        except _SessionSkip as exc:
            return Result(self, Status.SKIPPED, reason=str(exc))

        except nox.command.CommandFailed:
            return Result(self, Status.FAILED)

        except KeyboardInterrupt:
            logger.error("Session {} interrupted.".format(self.friendly_name))
            raise

        except Exception as exc:
            logger.exception(
                "Session {} raised exception {!r}".format(self.friendly_name, exc)
            )
            return Result(self, Status.FAILED)
Ejemplo n.º 25
0
def load_nox_module(global_config: Namespace) -> Union[types.ModuleType, int]:
    """Load the user's noxfile and return the module object for it.

    .. note::

        This task has two side effects; it makes ``global_config.noxfile``
        an absolute path, and changes the working directory of the process.

    Args:
        global_config (.nox.main.GlobalConfig): The global config.

    Returns:
        module: The module designated by the Noxfile path.
    """
    try:
        # Save the absolute path to the Noxfile.
        # This will inoculate it if nox changes paths because of an implicit
        # or explicit chdir (like the one below).
        global_config.noxfile = os.path.realpath(
            # Be sure to expand variables
            os.path.expandvars(global_config.noxfile))

        # Check ``nox.needs_version`` by parsing the AST.
        check_nox_version(global_config.noxfile)

        # Move to the path where the Noxfile is.
        # This will ensure that the Noxfile's path is on sys.path, and that
        # import-time path resolutions work the way the Noxfile author would
        # guess.
        os.chdir(os.path.realpath(os.path.dirname(global_config.noxfile)))
        return importlib.machinery.SourceFileLoader(
            "user_nox_module",
            global_config.noxfile).load_module()  # type: ignore

    except (VersionCheckFailed, InvalidVersionSpecifier) as error:
        logger.error(str(error))
        return 2
    except (IOError, OSError):
        logger.exception("Failed to load Noxfile {}".format(
            global_config.noxfile))
        return 2
Ejemplo n.º 26
0
    def execute(self):
        session_friendly_name = self.signature or self.name
        logger.warning('Running session {}'.format(session_friendly_name))

        try:
            if not self._create_config():
                logger.error(
                    'Session {} aborted.'.format(session_friendly_name))
                return False

            self._create_venv()

            cwd = py.path.local(os.getcwd()).as_cwd()
            with cwd:
                self._run_commands()

            logger.success(
                'Session {} successful. :)'.format(session_friendly_name))
            return True

        except CommandFailed:
            logger.error('Session {} failed. :('.format(session_friendly_name))
            return False

        except KeyboardInterrupt:
            logger.error(
                'Session {} interrupted.'.format(session_friendly_name))
            raise
Ejemplo n.º 27
0
def run(global_config):
    try:
        user_nox_module = load_user_nox_module(global_config.noxfile)
    except IOError:
        logger.error('Noxfile {} not found.'.format(global_config.noxfile))
        return False

    session_functions = discover_session_functions(user_nox_module)
    sessions = make_sessions(session_functions, global_config)

    if global_config.list_sessions:
        print('Available sessions:')
        for session in sessions:
            print('*', session.signature or session.name)
        return True

    if global_config.sessions:
        sessions = filter_sessions(global_config.sessions, sessions)

    if not sessions:
        return False

    success = True
    results = []

    for session in sessions:
        result = session.execute()
        results.append((session, result))
        success = success and result
        if not success and global_config.stop_on_first_error:
            success = False
            break

    if len(results) > 1:
        print_summary(results)

    if global_config.report is not None:
        create_report(global_config.report, success, results)

    return success
Ejemplo n.º 28
0
def run(args,
        *,
        env=None,
        silent=False,
        path=None,
        success_codes=None,
        log=True,
        external=False,
        **popen_kws):
    """Run a command-line program."""

    if success_codes is None:
        success_codes = [0]

    cmd, args = args[0], args[1:]
    full_cmd = "{} {}".format(cmd, " ".join(args))

    cmd_path = which(cmd, path)

    if log:
        logger.info(full_cmd)

        is_external_tool = path is not None and not cmd_path.startswith(path)
        if is_external_tool:
            if external == "error":
                logger.error(
                    "Error: {} is not installed into the virtualenv, it is located at {}. "
                    "Pass external=True into run() to explicitly allow this.".
                    format(cmd, cmd_path))
                raise CommandFailed("External program disallowed.")
            elif external is False:
                logger.warning(
                    "Warning: {} is not installed into the virtualenv, it is located at {}. This might cause issues! "
                    "Pass external=True into run() to silence this message.".
                    format(cmd, cmd_path))

    env = _clean_env(env)

    try:
        return_code, output = popen([cmd_path] + list(args),
                                    silent=silent,
                                    env=env,
                                    **popen_kws)

        if return_code not in success_codes:
            logger.error("Command {} failed with exit code {}{}".format(
                full_cmd, return_code, ":" if silent else ""))

            if silent:
                sys.stderr.write(output)

            raise CommandFailed("Returned code {}".format(return_code))

        return output if silent else True

    except KeyboardInterrupt:
        logger.error("Interrupted...")
        raise
Ejemplo n.º 29
0
    def execute(self):
        session_friendly_name = self.signature or self.name
        logger.warning("Running session {}".format(session_friendly_name))

        try:
            # By default, nox should quietly change to the directory where
            # the noxfile.py file is located.
            cwd = py.path.local(
                os.path.realpath(os.path.dirname(
                    self.global_config.noxfile))).as_cwd()

            with cwd:
                self._create_venv()
                session = Session(self)
                self.func(session)

            # Nothing went wrong; return a success.
            return Result(self, Status.SUCCESS)

        except _SessionQuit:
            return Result(self, Status.ABORTED)

        except _SessionSkip:
            return Result(self, Status.SKIPPED)

        except nox.command.CommandFailed:
            return Result(self, Status.FAILED)

        except KeyboardInterrupt:
            logger.error("Session {} interrupted.".format(self))
            raise

        except Exception as exc:
            logger.exception("Session {} raised exception {!r}".format(
                self, exc))
            return Result(self, Status.FAILED)
Ejemplo n.º 30
0
def run(
    args: Sequence[str],
    *,
    env: Optional[dict] = None,
    silent: bool = False,
    paths: Optional[List[str]] = None,
    success_codes: Optional[Iterable[int]] = None,
    log: bool = True,
    external: Union[Literal["error"], bool] = False,
    **popen_kws: Any,
) -> Union[str, bool]:
    """Run a command-line program."""

    if success_codes is None:
        success_codes = [0]

    cmd, args = args[0], args[1:]
    full_cmd = f"{cmd} {_shlex_join(args)}"

    cmd_path = which(cmd, paths)

    if log:
        logger.info(full_cmd)

        is_external_tool = paths is not None and not any(
            cmd_path.startswith(path) for path in paths)
        if is_external_tool:
            if external == "error":
                logger.error(
                    f"Error: {cmd} is not installed into the virtualenv, it is located at {cmd_path}. "
                    "Pass external=True into run() to explicitly allow this.")
                raise CommandFailed("External program disallowed.")
            elif external is False:
                logger.warning(
                    f"Warning: {cmd} is not installed into the virtualenv, it is located at {cmd_path}. This might cause issues! "
                    "Pass external=True into run() to silence this message.")

    env = _clean_env(env)

    try:
        return_code, output = popen([cmd_path] + list(args),
                                    silent=silent,
                                    env=env,
                                    **popen_kws)

        if return_code not in success_codes:
            suffix = ":" if silent else ""
            logger.error(
                f"Command {full_cmd} failed with exit code {return_code}{suffix}"
            )

            if silent:
                sys.stderr.write(output)

            raise CommandFailed(f"Returned code {return_code}")

        if output:
            logger.output(output)

        return output if silent else True

    except KeyboardInterrupt:
        logger.error("Interrupted...")
        raise
Ejemplo n.º 31
0
 def error(self, *args, **kwargs):
     logger.error(*args, **kwargs)
     raise _SessionQuit()
Ejemplo n.º 32
0
def run(
    args,
    *,
    env=None,
    silent=False,
    path=None,
    success_codes=None,
    log=True,
    external=False
):
    """Run a command-line program."""

    if success_codes is None:
        success_codes = [0]

    cmd, args = args[0], args[1:]
    full_cmd = "{} {}".format(cmd, " ".join(args))

    cmd_path = which(cmd, path)

    if log:
        logger.info(full_cmd)

        is_external_tool = path is not None and not cmd_path.startswith(path)
        if is_external_tool:
            if external == "error":
                logger.error(
                    "Error: {} is not installed into the virtualenv, it is located at {}. "
                    "Pass external=True into run() to explicitly allow this.".format(
                        cmd, cmd_path
                    )
                )
                raise CommandFailed("External program disallowed.")
            elif external is False:
                logger.warning(
                    "Warning: {} is not installed into the virtualenv, is it located at {}. This might cause issues! "
                    "Pass external=True into run() to silence this message.".format(
                        cmd, cmd_path
                    )
                )

    env = _clean_env(env)

    try:
        return_code, output = popen([cmd_path] + list(args), silent=silent, env=env)

        if return_code not in success_codes:
            logger.error(
                "Command {} failed with exit code {}{}".format(
                    full_cmd, return_code, ":" if silent else ""
                )
            )

            if silent:
                sys.stderr.write(output)

            raise CommandFailed("Returned code {}".format(return_code))

        return output if silent else True

    except KeyboardInterrupt:
        logger.error("Interrupted...")
        raise
Ejemplo n.º 33
0
 def error(self, *args, **kwargs):
     """Immediately aborts the session and optionally logs an error."""
     if args or kwargs:
         logger.error(*args, **kwargs)
     raise _SessionQuit()
Ejemplo n.º 34
0
def pre_commit(session: Session) -> None:  # noqa: R0912
    """Format and check the code."""
    if "skip_install" not in session.posargs:
        extras = "pre-commit testing docs poetry dev_nox"
        session.poetry_install(
            extras,
            no_root=(TOX_CALLS or SKIP_INSTALL),
            no_dev=(TOX_CALLS or IN_CI),
            pip_require_venv=poetry_require_venv(session),
        )
    else:
        session.log("Skipping install step.")

    #: Set 'show-diff' and 'skip identity hook'
    show_diff = []
    env = {"SKIP": "identity"}
    if (session.interactive and "diff" in session.posargs) or (
            not session.interactive and "nodiff" not in session.posargs):
        show_diff = ["--show-diff-on-failure"]
        env = {}

    #: Add SKIP from posargs to env
    skip = ""
    for arg in session.posargs:
        if arg.startswith("SKIP="):
            skip = arg
            break

    if skip:
        env = {"SKIP": f"{skip[5:]},{env.get('SKIP', '')}"}

    #: Get hooks from posargs
    hooks_arg = ""
    for arg in session.posargs:
        if arg.startswith("HOOKS="):
            hooks_arg = arg
            break

    #: Remove processed posargs
    for arg in ("skip_install", "diff", "nodiff", skip, hooks_arg):
        with contextlib.suppress(ValueError):
            session.posargs.remove(arg)

    hooks = hooks_arg[6:].split(",") if hooks_arg else [""]

    color = ["--color=always"] if FORCE_COLOR else []

    error_hooks = []
    for hook in hooks:
        add_args = show_diff + session.posargs + ([hook] if hook else [])
        try:
            session.run("pre-commit",
                        "run",
                        *color,
                        "--all-files",
                        *add_args,
                        env=env)
        except CommandFailed:
            error_hooks.append(hook)

    print(
        "HINT: to add checks as pre-commit hook run: ",
        f'"{get_venv_bin_dir(get_venv_path()) / "pre-commit"} '
        "install -t pre-commit -t commit-msg.",
    )

    if error_hooks:
        if hooks != [""]:
            nox_logger.error(
                f"The following pre-commit hooks failed: {error_hooks}."  # noqa: G004
            )
        raise CommandFailed
Ejemplo n.º 35
0
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