diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aa4adb5d..a493c71c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,6 +25,19 @@ env: PIP_DISABLE_PIP_VERSION_CHECK: "1" # Reduce noise in logs jobs: + pre-commit: + env: + SKIP: pytest,pytype,tox,no-commit-to-branch + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 + 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 @@ -43,13 +56,13 @@ jobs: env: PYTHON_VERSION_USED_FOR_COVERAGE: ${{ '3.11' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 # Needed by diff-cover to get the changed lines: origin/master..HEAD - name: Set up Python ${{ matrix.python-version }} # Python 3.11 is not supported in the nektos/act container, so we skip this step if: ${{ !(matrix.python-version == '3.11' && github.actor == 'nektos/act') }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba7cb9cb..e10a07e7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -115,6 +115,7 @@ repos: hooks: - id: trailing-whitespace - id: end-of-file-fixer + exclude: docs/source/index.rst - id: check-yaml - id: check-added-large-files args: ['--maxkb=50'] diff --git a/README-Unicode.md b/README-Unicode.md index 3e52c910..30ea6c39 100644 --- a/README-Unicode.md +++ b/README-Unicode.md @@ -132,19 +132,9 @@ To fix these issues, `xcp.compat`, provides a wrapper for `open()`. It adds `encoding="utf-8", errors="replace"` to enable UTF-8 conversion and handle encoding errors: - ```py - # xcp/utf8mode.py - if sys.version_info >= (3, 0): - def utf8open(*args, **kwargs): - if len(args) > 1 and "b" in args[1]: - return open(*args, **kwargs) - return open(*args, encoding="utf-8", errors="replace", **kwargs) - else: - utf8open = open - # xcp/{cmd,pci,environ?,logger?}.py tests/test_{pci,biodevname?,...?}.py - + from .utf8mode import utf8open - ... - - open(filename) - + utf8open(filename) - ``` - +```py + def utf8open(*args, **kwargs): + if len(args) > 1 and "b" in args[1]: + return open(*args, **kwargs) + return open(*args, encoding="utf-8", errors="replace", **kwargs) +``` diff --git a/docs/source/include-toplevel-CONTRIBUTING.md b/docs/source/include-toplevel-CONTRIBUTING.md index b007eebc..4d5b2da7 100644 --- a/docs/source/include-toplevel-CONTRIBUTING.md +++ b/docs/source/include-toplevel-CONTRIBUTING.md @@ -1,2 +1,2 @@ ```{include} ../../CONTRIBUTING.md -:parser: myst \ No newline at end of file +:parser: myst diff --git a/docs/source/include-toplevel-DOCUMENTING.md b/docs/source/include-toplevel-DOCUMENTING.md index e56d3fb9..ef536a0e 100644 --- a/docs/source/include-toplevel-DOCUMENTING.md +++ b/docs/source/include-toplevel-DOCUMENTING.md @@ -1,2 +1,2 @@ ```{include} ../../DOCUMENTING.md -:parser: myst \ No newline at end of file +:parser: myst diff --git a/docs/source/include-toplevel-README-Unicode.md b/docs/source/include-toplevel-README-Unicode.md index a64fb4f2..8109f6d3 100644 --- a/docs/source/include-toplevel-README-Unicode.md +++ b/docs/source/include-toplevel-README-Unicode.md @@ -1,2 +1,2 @@ ```{include} ../../README-Unicode.md -:parser: myst \ No newline at end of file +:parser: myst diff --git a/docs/source/include-toplevel-README.md b/docs/source/include-toplevel-README.md index 6c5b587c..8b89a8fd 100644 --- a/docs/source/include-toplevel-README.md +++ b/docs/source/include-toplevel-README.md @@ -1,2 +1,2 @@ ```{include} ../../README.md -:parser: myst \ No newline at end of file +:parser: myst 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] = ...): ...