Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions nox/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import sys
import urllib.parse
from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal, NoReturn, cast
from typing import TYPE_CHECKING, Literal, NoReturn, cast

import packaging.requirements
import packaging.utils
Expand All @@ -38,6 +38,7 @@
from nox.project import load_toml

if TYPE_CHECKING:
from argparse import Namespace
from collections.abc import Generator

__all__ = ["execute_workflow", "main", "nox_main"]
Expand All @@ -47,7 +48,7 @@ def __dir__() -> list[str]:
return __all__


def execute_workflow(args: Any) -> int:
def execute_workflow(args: Namespace) -> int:
"""
Execute the appropriate tasks.
"""
Expand Down
2 changes: 1 addition & 1 deletion nox/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def format(self, record: Any) -> str:


class LoggerWithSuccessAndOutput(logging.getLoggerClass()): # type: ignore[misc]
def __init__(self, name: str, level: int = logging.NOTSET):
def __init__(self, name: str, level: int = logging.NOTSET) -> None:
super().__init__(name, level)
logging.addLevelName(SESSION_INFO, "SESSION_INFO")
logging.addLevelName(SUCCESS, "SUCCESS")
Expand Down
4 changes: 2 additions & 2 deletions nox/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,8 @@ def __len__(self) -> int:
def keyword_match(expression: str, keywords: Iterable[str]) -> Any:
"""See if an expression matches the given set of keywords."""
# TODO: see if we can use ast.literal_eval here.
locals = KeywordLocals(set(keywords))
return eval(expression, {}, locals) # noqa: S307
my_locals = KeywordLocals(set(keywords))
return eval(expression, {}, my_locals) # noqa: S307


def _null_session_func_(session: Session) -> None:
Expand Down
75 changes: 62 additions & 13 deletions nox/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,8 @@
import subprocess
import sys
import time
import typing
import unicodedata
from typing import (
TYPE_CHECKING,
Any,
NoReturn,
)

import humanize

Expand All @@ -46,7 +42,7 @@
get_virtualenv,
)

if TYPE_CHECKING:
if typing.TYPE_CHECKING:
import argparse
from collections.abc import (
Callable,
Expand All @@ -57,7 +53,12 @@
Sequence,
)
from types import TracebackType
from typing import IO
from typing import (
IO,
Any,
Literal,
NoReturn,
)

from nox._decorators import Func
from nox.command import ExternalType
Expand Down Expand Up @@ -279,7 +280,7 @@ def install_and_run_script(
stderr: int | IO[str] | None = subprocess.STDOUT,
interrupt_timeout: float | None = DEFAULT_INTERRUPT_TIMEOUT,
terminate_timeout: float | None = DEFAULT_TERMINATE_TIMEOUT,
) -> Any | None:
) -> str | bool | None:
"""
Install dependencies and run a Python script.
"""
Expand Down Expand Up @@ -342,6 +343,54 @@ def _run_func(self, func: Callable[..., Any], args: Iterable[Any]) -> Any:
logger.exception(f"Function {func!r} raised {e!r}.")
raise nox.command.CommandFailed() from e

@typing.overload
def run(
self,
*args: str | os.PathLike[str],
env: Mapping[str, str | None] | None = None,
include_outer_env: bool = True,
silent: Literal[False] = ...,
success_codes: Iterable[int] | None = None,
log: bool = True,
external: ExternalType | None = None,
stdout: int | IO[str] | None = None,
stderr: int | IO[str] | None = subprocess.STDOUT,
interrupt_timeout: float | None = DEFAULT_INTERRUPT_TIMEOUT,
terminate_timeout: float | None = DEFAULT_TERMINATE_TIMEOUT,
) -> bool | None: ...

@typing.overload
def run(
self,
*args: str | os.PathLike[str],
env: Mapping[str, str | None] | None = None,
include_outer_env: bool = True,
silent: Literal[True],
success_codes: Iterable[int] | None = None,
log: bool = True,
external: ExternalType | None = None,
stdout: int | IO[str] | None = None,
stderr: int | IO[str] | None = subprocess.STDOUT,
interrupt_timeout: float | None = DEFAULT_INTERRUPT_TIMEOUT,
terminate_timeout: float | None = DEFAULT_TERMINATE_TIMEOUT,
) -> str | None: ...

@typing.overload
def run(
self,
*args: str | os.PathLike[str],
env: Mapping[str, str | None] | None = None,
include_outer_env: bool = True,
silent: bool,
success_codes: Iterable[int] | None = None,
log: bool = True,
external: ExternalType | None = None,
stdout: int | IO[str] | None = None,
stderr: int | IO[str] | None = subprocess.STDOUT,
interrupt_timeout: float | None = DEFAULT_INTERRUPT_TIMEOUT,
terminate_timeout: float | None = DEFAULT_TERMINATE_TIMEOUT,
) -> str | bool | None: ...

def run(
self,
*args: str | os.PathLike[str],
Expand All @@ -355,7 +404,7 @@ def run(
stderr: int | IO[str] | None = subprocess.STDOUT,
interrupt_timeout: float | None = DEFAULT_INTERRUPT_TIMEOUT,
terminate_timeout: float | None = DEFAULT_TERMINATE_TIMEOUT,
) -> Any | None:
) -> str | bool | None:
"""Run a command.

Commands must be specified as a list of strings, for example::
Expand Down Expand Up @@ -495,7 +544,7 @@ def run_install(
stderr: int | IO[str] | None = subprocess.STDOUT,
interrupt_timeout: float | None = DEFAULT_INTERRUPT_TIMEOUT,
terminate_timeout: float | None = DEFAULT_TERMINATE_TIMEOUT,
) -> Any | None:
) -> str | bool | None:
"""Run a command in the install step.

This is a variant of :meth:`run` that runs even in the presence of
Expand Down Expand Up @@ -578,7 +627,7 @@ def run_always(
stderr: int | IO[str] | None = subprocess.STDOUT,
interrupt_timeout: float | None = DEFAULT_INTERRUPT_TIMEOUT,
terminate_timeout: float | None = DEFAULT_TERMINATE_TIMEOUT,
) -> Any | None:
) -> str | bool | None:
"""This is an alias to ``run_install``, which better describes the use case.

:meta private:
Expand Down Expand Up @@ -611,7 +660,7 @@ def _run(
stderr: int | IO[str] | None,
interrupt_timeout: float | None,
terminate_timeout: float | None,
) -> Any:
) -> str | bool:
"""Like run(), except that it runs even if --install-only is provided."""
# Legacy support - run a function given.
if callable(args[0]):
Expand Down Expand Up @@ -1167,7 +1216,7 @@ def execute(self) -> Result:
logger.error(f"Session {self.friendly_name} interrupted.")
raise

except Exception as exc:
except Exception as exc: # noqa: BLE001
logger.exception(f"Session {self.friendly_name} raised exception {exc!r}")
self.result = Result(
self, Status.FAILED, duration=time.perf_counter() - start
Expand Down
8 changes: 4 additions & 4 deletions nox/virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def pbs_install_python(python_version: str) -> str | None:
version_dir=True,
implementation=implementation,
)
except Exception as err:
except Exception as err: # noqa: BLE001
logger.warning(f"Failed to install a pbs version for {python_version=}: {err}")
return None

Expand Down Expand Up @@ -444,7 +444,7 @@ def __init__(
venv_params: Sequence[str] = (),
conda_cmd: str = "conda",
**kwargs: Any,
):
) -> None:
self.location_name = location
self.location = os.path.abspath(location)
self.interpreter = interpreter
Expand Down Expand Up @@ -537,7 +537,7 @@ def is_offline() -> bool:
try:
# DNS resolution to detect situation (1) or (2).
host = gethostbyname("repo.anaconda.com")
except BaseException: # pragma: no cover
except OSError: # pragma: no cover
return True
return host is None

Expand Down Expand Up @@ -579,7 +579,7 @@ def __init__(
reuse_existing: bool = False,
venv_backend: str = "virtualenv",
venv_params: Sequence[str] = (),
):
) -> None:
# "pypy-" -> "pypy"
if interpreter and interpreter.startswith("pypy-"):
interpreter = interpreter[:4] + interpreter[5:]
Expand Down
64 changes: 22 additions & 42 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,55 +87,35 @@ docs = [
metadata.allow-ambiguous-features = true # disable normalization (tox-to-nox) for back-compat

[tool.ruff]
lint.extend-select = [
# "ANN", # flake8-annotations
"ARG", # flake8-unused-arguments
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"EM", # flake8-errmsg
"EXE", # flake8-executable
"FA", # flake8-future-annotations
"FBT", # flake8-boolean-trap
"FLY", # flynt
"FURB", # refurb
"G", # flake8-logging-format
"I", # isort
"ICN", # flake8-import-conventions
"ISC", # flake8-implicit-str-concat
"LOG", # flake8-logging
"N", # flake8-naming
"PERF", # perflint
"PGH", # pygrep-hooks
"PIE", # flake8-pie
"PL", # pylint
"PT", # flake8-pytest-style
# "PTH", # flake8-use-pathlib
"PYI", # flake8-pyi
"Q", # flake8-quotes
"RET", # flake8-return
"RUF", # Ruff-specific
"S", # eval -> literal_eval
"SIM", # flake8-simplify
"SLOT", # flake8-slots
"T10", # flake8-debugger
"TC", # flake8-type-checking
"TRY", # tryceratops
"UP", # pyupgrade
"YTT", # flake8-2020
]
show-fixes = true
lint.select = [ "ALL" ]
lint.ignore = [
"A002", # Function arguments can shadow builtins
"ANN401", # Any allowed (TODO: some maybe can be removed)
"C9", # Complexity
"COM812", # Trailing commas inform the formatter
"D", # Too many docs
"E501", # Line too long
"FIX", # We have todos
"N802", # Function name should be lowercase
"N818", # Error suffix for errors
"PLR09", # Too many X
"PLR2004", # Magic value used in comparison
"S101", # Use of assert detected
"PTH", # Path usage (TODO)
"RSE102", # Parens on exception
"S101", # Assert is used by mypy and pytest
"S603", # subprocess call - check for execution of untrusted input
]
lint.per-file-ignores."tests/*.py" = [
"FBT001", # Boolean args okay for fixtures
]
lint.per-file-ignores."tests/resources/**.py" = [ "ARG001", "TRY002" ]
"SLF001", # Private member access
"T20", # We use print
"TD", # TODO format
]
lint.per-file-ignores.".github/**.py" = [ "INP001" ]
lint.per-file-ignores."docs/**.py" = [ "ERA001", "INP001" ]
lint.per-file-ignores."tests/*.py" = [ "FBT001", "INP001" ]
lint.per-file-ignores."tests/resources/**.py" = [ "ANN", "ARG001", "DTZ001", "ERA001", "TRY002" ]
lint.typing-modules = [ "nox._typing" ]
lint.flake8-annotations.allow-star-arg-any = true
lint.flake8-builtins.ignorelist = [ "copyright" ]
lint.flake8-unused-arguments.ignore-variadic-names = true

[tool.pyproject-fmt]
Expand Down
Loading
Loading