diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 90403a09..726dd10c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,6 +20,19 @@ env: PIP_DISABLE_PIP_VERSION_CHECK: "1" # Reduce noise in logs jobs: + pre-commit: + env: + SKIP: pytest,pytype,no-commit-to-branch + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.11 + - uses: pre-commit/action@v3.0.1 + - uses: pre-commit-ci/lite-action@v1.0.3 + if: always() + test: strategy: # See: https://github.com/xenserver/python-libs/pull/26#discussion_r1179482169 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 917040ce..a3bce6a7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ # https://github.com/dexpota/cheatsheets/blob/master/pre-commit exclude: "^tests/data" fail_fast: true -default_stages: [commit] +default_stages: [pre-commit] repos: - repo: local hooks: @@ -47,7 +47,7 @@ repos: types: [binary] language: fail - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: no-commit-to-branch args: [--branch, master] @@ -62,11 +62,21 @@ repos: - isort +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 # Use the ref you want to point at + hooks: + # Enforce that `# type: ignore` annotations always occur with specific codes. + # Sample annotation: # type: ignore[attr-defined,name-defined] + - id: python-check-blanket-type-ignore + + - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.0 + rev: v1.13.0 hooks: - id: mypy additional_dependencies: + - pyfakefs + - pytest_httpserver - pytest-subprocess - types-mock - types-six @@ -74,7 +84,7 @@ repos: - repo: https://github.com/rcmdnk/pyproject-pre-commit - rev: v0.1.9 + rev: v0.3.4 hooks: - id: shellcheck @@ -86,7 +96,7 @@ repos: - repo: https://github.com/pycqa/pylint - rev: v2.17.4 + rev: v3.3.1 hooks: - id: pylint args: @@ -98,12 +108,16 @@ repos: ] log_file: ".git/pre-commit-pylint.log" additional_dependencies: - - pyfakefs + - setuptools - six - mock - pandas + - pyfakefs - pytest_forked + - pytest_httpserver - toml + + - repo: local hooks: - id: pytype @@ -114,24 +128,29 @@ repos: language: python language_version: python3.8 require_serial: true - additional_dependencies: [pytype] + additional_dependencies: + - pytype + - pyfakefs + - pytest + - pytest_forked + - pytest_httpserver + - pytest_localftpserver + - pytest-subprocess + + - id: pytest name: Check pytest unit tests pass types: [python] # entry: sh -c "pytest -x -rf --new-first --show-capture=all >/dev/tty" - entry: sh -c "tox -e py38-covcombine >/dev/tty" + entry: sh -c "tox >/dev/tty" verbose: true language: python require_serial: true pass_filenames: false -- repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.10.0 # Use the ref you want to point at - hooks: - # Enforce that `# type: ignore` annotations always occur with specific codes. - # Sample annotations: # type: ignore[attr-defined] # type: ignore[attr-defined,name-defined] - - id: python-check-blanket-type-ignore + + - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer diff --git a/pytype_runner.py b/pytype_runner.py index 3ee62e98..9625736f 100755 --- a/pytype_runner.py +++ b/pytype_runner.py @@ -77,7 +77,7 @@ def run_pytype(command: List[str], branch_url: str, errorlog: TextIO, results): ok = True while ok: for key, _ in sel.select(): - line = key.fileobj.readline() # type: ignore + line = key.fileobj.readline() # type: ignore[union-attr] if not line: ok = False break diff --git a/stubs/pytest_httpserver.pyi b/stubs/pytest_httpserver.pyi deleted file mode 100644 index 0ee27850..00000000 --- a/stubs/pytest_httpserver.pyi +++ /dev/null @@ -1,134 +0,0 @@ -import abc -from enum import Enum -from ssl import SSLContext -from typing import Any, Callable, Iterable, Mapping, MutableMapping, Optional, Pattern, Tuple, Union - -# pylint: disable=import-error, no-name-in-module, super-init-not-called, multiple-statements, too-few-public-methods, invalid-name, line-too-long -from _typeshed import Incomplete - -from werkzeug.wrappers import Request, Response - -URI_DEFAULT: str -METHOD_ALL: str -HEADERS_T = Union[Mapping[str, Union[str, Iterable[str]]], Iterable[Tuple[str, str]]] -HVMATCHER_T = Callable[[str, Optional[str], str], bool] - -class Error(Exception): ... -class NoHandlerError(Error): ... -class HTTPServerError(Error): ... -class NoMethodFoundForMatchingHeaderValueError(Error): ... - -class WaitingSettings: - raise_assertions: Incomplete - stop_on_nohandler: Incomplete - timeout: Incomplete - def __init__(self, raise_assertions: bool = ..., stop_on_nohandler: bool = ..., timeout: float = ...) -> None: ... - -class Waiting: - def __init__(self) -> None: ... - def complete(self, result: bool): ... - @property - def result(self) -> bool: ... - @property - def elapsed_time(self) -> float: ... - -class HeaderValueMatcher: - DEFAULT_MATCHERS: MutableMapping[str, Callable[[Optional[str], str], bool]] - matchers: Incomplete - def __init__(self, matchers: Optional[Mapping[str, Callable[[Optional[str], str], bool]]] = ...) -> None: ... - @staticmethod - def authorization_header_value_matcher(actual: Optional[str], expected: str) -> bool: ... - @staticmethod - def default_header_value_matcher(actual: Optional[str], expected: str) -> bool: ... - def __call__(self, header_name: str, actual: Optional[str], expected: str) -> bool: ... - -class URIPattern(abc.ABC, metaclass=abc.ABCMeta): - @abc.abstractmethod - def match(self, uri: str) -> bool: ... - -class RequestMatcher: - uri: Incomplete - method: Incomplete - query_string: Incomplete - query_matcher: Incomplete - json: Incomplete - headers: Incomplete - data: Incomplete - data_encoding: Incomplete - header_value_matcher: Incomplete - # def __init__(self) - def match_data(self, request: Request) -> bool: ... - def match_uri(self, request: Request) -> bool: ... - def match_json(self, request: Request) -> bool: ... - def match(self, request: Request) -> bool: ... - -class RequestHandlerBase(abc.ABC, metaclass=abc.ABCMeta): - def respond_with_json(self, response_json, status: int = ..., headers: Optional[Mapping[str, str]] = ..., content_type: str = ...): ... - def respond_with_data(self, response_data: Union[str, bytes] = ..., status: int = ..., headers: Optional[HEADERS_T] = ..., mimetype: Optional[str] = ..., content_type: Optional[str] = ...): ... - @abc.abstractmethod - def respond_with_response(self, response: Response): ... - -class RequestHandler(RequestHandlerBase): - matcher: Incomplete - request_handler: Incomplete - def __init__(self, matcher: RequestMatcher) -> None: ... - def respond(self, request: Request) -> Response: ... - def respond_with_handler(self, func: Callable[[Request], Response]): ... - def respond_with_response(self, response: Response): ... - -class HandlerType(Enum): - PERMANENT: str - ONESHOT: str - ORDERED: str - -class HTTPServerBase(abc.ABC, metaclass=abc.ABCMeta): - host: Incomplete - port: Incomplete - server: Incomplete - server_thread: Incomplete - assertions: Incomplete - handler_errors: Incomplete - log: Incomplete - ssl_context: Incomplete - no_handler_status_code: int - def __init__(self, host: str, port: int, ssl_context: Optional[SSLContext] = ...) -> None: ... - def clear(self) -> None: ... - def clear_assertions(self) -> None: ... - def clear_handler_errors(self) -> None: ... - def clear_log(self) -> None: ... - def url_for(self, suffix: str): ... - def create_matcher(self, *args, **kwargs) -> RequestMatcher: ... - def thread_target(self) -> None: ... - def is_running(self) -> bool: ... - def start(self) -> None: ... - def stop(self) -> None: ... - def add_assertion(self, obj) -> None: ... - def check(self) -> None: ... - def check_assertions(self) -> None: ... - def check_handler_errors(self) -> None: ... - def respond_nohandler(self, request: Request, extra_message: str = ...): ... - @abc.abstractmethod - def dispatch(self, request: Request) -> Response: ... - def application(self, request: Request): ... - def __enter__(self): ... - def __exit__(self, *args, **kwargs) -> None: ... - @staticmethod - def format_host(host): ... - -class HTTPServer(HTTPServerBase): - DEFAULT_LISTEN_HOST: str - DEFAULT_LISTEN_PORT: int - ordered_handlers: Incomplete - oneshot_handlers: Incomplete - handlers: Incomplete - permanently_failed: bool - default_waiting_settings: Incomplete - def __init__(self, host=..., port=..., ssl_context: Optional[SSLContext] = ..., default_waiting_settings: Optional[WaitingSettings] = ...) -> None: ... - def clear(self) -> None: ... - def clear_all_handlers(self) -> None: ... - def expect_request(self, uri: Union[str, URIPattern, Pattern[str]], method: str = ..., data: Union[str, bytes, None] = ..., data_encoding: str = ..., header_value_matcher: Optional[HVMATCHER_T] = ..., handler_type: HandlerType = ..., json: Any = ...) -> RequestHandler: ... - def format_matchers(self) -> str: ... - def respond_nohandler(self, request: Request, extra_message: str = ...): ... - def respond_permanent_failure(self): ... - def dispatch(self, request: Request) -> Response: ... - def wait(self, raise_assertions: Optional[bool] = ..., stop_on_nohandler: Optional[bool] = ..., timeout: Optional[float] = ...): ...