From 8e51ac2dcb863b0d781bb8818375ea2be63d195c Mon Sep 17 00:00:00 2001 From: HSN Date: Mon, 11 Nov 2024 12:36:59 +0600 Subject: [PATCH 1/4] refactor: create a fetch dir module --- chk/modules/fetch/__init__.py | 4 ++++ chk/modules/{ => fetch}/fetch.py | 0 2 files changed, 4 insertions(+) create mode 100644 chk/modules/fetch/__init__.py rename chk/modules/{ => fetch}/fetch.py (100%) diff --git a/chk/modules/fetch/__init__.py b/chk/modules/fetch/__init__.py new file mode 100644 index 00000000..8287d4fc --- /dev/null +++ b/chk/modules/fetch/__init__.py @@ -0,0 +1,4 @@ +""" +Fetch module +""" + diff --git a/chk/modules/fetch.py b/chk/modules/fetch/fetch.py similarity index 100% rename from chk/modules/fetch.py rename to chk/modules/fetch/fetch.py From f0ebc474b9e2a4f322663897f944b81dd85f74f6 Mon Sep 17 00:00:00 2001 From: HSN Date: Mon, 11 Nov 2024 13:24:43 +0600 Subject: [PATCH 2/4] refactor: move functionality to corresponding places --- chk/modules/fetch/__init__.py | 119 ++++++ chk/modules/fetch/entities.py | 358 ++++++++++++++++ chk/modules/fetch/fetch.py | 754 ---------------------------------- chk/modules/fetch/services.py | 296 +++++++++++++ 4 files changed, 773 insertions(+), 754 deletions(-) create mode 100644 chk/modules/fetch/entities.py delete mode 100644 chk/modules/fetch/fetch.py create mode 100644 chk/modules/fetch/services.py diff --git a/chk/modules/fetch/__init__.py b/chk/modules/fetch/__init__.py index 8287d4fc..9418e735 100644 --- a/chk/modules/fetch/__init__.py +++ b/chk/modules/fetch/__init__.py @@ -1,4 +1,123 @@ """ Fetch module """ +import sys +from collections import abc +from chk.infrastructure.document import VersionedDocumentSupport +from chk.infrastructure.file_loader import ExecuteContext, FileContext +from chk.infrastructure.logging import debug, error_trace, with_catch_log +from chk.infrastructure.symbol_table import ExecResponse, ExposeManager, \ + VariableTableManager, \ + Variables +from chk.infrastructure.view import PresentationService, die_with_error +from chk.modules.fetch.entities import FetchTask +from chk.modules.fetch.services import FetchPresenter, HttpDocumentSupport + + +@with_catch_log +def call(file_ctx: FileContext, exec_ctx: ExecuteContext) -> ExecResponse: + """Call a http document""" + + debug(file_ctx) + debug(exec_ctx) + + r_exception: Exception | None = None + variable_doc = Variables() + output_data = Variables() + exposed_data = {} + + try: + http_doc = HttpDocumentSupport.from_file_context(file_ctx) + debug(http_doc.model_dump_json()) + + VersionedDocumentSupport.validate_with_schema( + HttpDocumentSupport.build_schema(), http_doc + ) + + VariableTableManager.handle(variable_doc, http_doc, exec_ctx) + debug(variable_doc.data) + + HttpDocumentSupport.process_request_template(http_doc, variable_doc) + debug(http_doc.model_dump_json()) + + # try: + response = HttpDocumentSupport.execute_request(http_doc) + + output_data = Variables({"_response": response.model_dump()}) + debug(output_data.data) + + exposed_data = ExposeManager.get_exposed_replaced_data( + http_doc, + {**variable_doc.data, **output_data.data}, + ) + debug(exposed_data) + + except Exception as ex: + r_exception = ex + error_trace(exception=sys.exc_info()).error(ex) + + # TODO: instead if sending specific report items, and making presentable in other + # module, we should prepare and long and short form of presentable that can be + # loaded via other module + + request_method, request_url = "", "" + + if "request" in file_ctx.document: + if "method" in file_ctx.document["request"]: + request_method = file_ctx.document["request"]["method"] + if "url" in file_ctx.document["request"]: + request_url = file_ctx.document["request"]["url"] + + return ExecResponse( + file_ctx=file_ctx, + exec_ctx=exec_ctx, + variables_exec=output_data, + variables=variable_doc, + exception=r_exception, + exposed=exposed_data, + report={ + "is_success": r_exception is None, + "request_method": request_method, + "request_url": request_url, + }, + ) + + +@with_catch_log +def execute( + ctx: FileContext, exec_ctx: ExecuteContext, cb: abc.Callable = lambda *args: ... +) -> None: + """Call with a http document + + Args: + ctx: FileContext object to handle + exec_ctx: ExecuteContext + cb: Callable + """ + + try: + exr = call(file_ctx=ctx, exec_ctx=exec_ctx) + cb({ctx.filepath_hash: exr.variables_exec.data}) + PresentationService.display(exr, exec_ctx, FetchPresenter) + except Exception as ex: + error_trace(exception=sys.exc_info()).error(ex) + die_with_error(ex, FetchPresenter, exec_ctx.options["format"]) + + +@with_catch_log +def task_fetch(**kwargs: dict) -> ExecResponse: + """Task impl""" + + if not (doc := kwargs.get("task", {})): + raise ValueError("Wrong task format given.") + + _task = FetchTask(**doc) + + return call( + FileContext.from_file(_task.file), + ExecuteContext( + options={"dump": True, "format": True}, + arguments=_task.arguments | {"variables": _task.variables}, + ), + ) diff --git a/chk/modules/fetch/entities.py b/chk/modules/fetch/entities.py new file mode 100644 index 00000000..bf2061f2 --- /dev/null +++ b/chk/modules/fetch/entities.py @@ -0,0 +1,358 @@ +""" +Fetch module entities +""" + +from __future__ import annotations + +import enum +import json +from collections import abc +from typing import Any + +import requests +import xmltodict +from defusedxml.minidom import parseString +from pydantic import BaseModel, Field, model_serializer, computed_field + +from chk.infrastructure.document import VersionedDocumentV2 +from chk.infrastructure.helper import Cast + +VERSION_SCOPE = ["http"] +Http_V10 = "HTTP/1.0" +Http_V11 = "HTTP/1.1" +CTYPE_JSON = "application/json" +CTYPE_XML = "application/xml" + + +class HttpMethod(enum.StrEnum): + """Constants of wellknown http methods""" + + GET = "GET" + POST = "POST" + PUT = "PUT" + PATCH = "PATCH" + DELETE = "DELETE" + HEAD = "HEAD" + OPTIONS = "OPTIONS" + + +class RequestConfigNode(enum.StrEnum): + """represent request config section""" + + ROOT = "request" + LOCAL = "_response" + + # common request + URL = enum.auto() + METHOD = enum.auto() + HEADERS = enum.auto() + PARAMS = "url_params" + + # Basic + AUTH_BA = "auth .scm=basic" + AUTH_BA_USR = "username" + AUTH_BA_PAS = "password" + + # Bearer + AUTH_BE = "auth .scm=bearer" + AUTH_BE_TOK = "token" + + # Body + BODY_FRM = "body .enc=form" + BODY_FRM_DAT = "body .enc=form-data" + BODY_JSN = "body .enc=json" + BODY_XML = "body .enc=xml" + BODY_TXT = "body .enc=text" + + +SCHEMA = { + RequestConfigNode.ROOT: { + "required": True, + "type": "dict", + "schema": { + RequestConfigNode.URL: { + "required": True, + "empty": False, + "type": "string", + }, + RequestConfigNode.METHOD: { + "required": True, + "type": "string", + }, + RequestConfigNode.PARAMS: { + "required": False, + "empty": False, + "type": "dict", + }, + RequestConfigNode.HEADERS: { + "required": False, + "empty": False, + "type": "dict", + }, + RequestConfigNode.AUTH_BA: { + "required": False, + "empty": False, + "type": "dict", + "excludes": RequestConfigNode.AUTH_BE, + }, + RequestConfigNode.AUTH_BE: { + "required": False, + "empty": False, + "type": "dict", + "excludes": RequestConfigNode.AUTH_BA, + }, + RequestConfigNode.BODY_FRM: { + "required": False, + "empty": False, + "type": "dict", + "excludes": [ + RequestConfigNode.BODY_FRM_DAT, + RequestConfigNode.BODY_JSN, + RequestConfigNode.BODY_XML, + RequestConfigNode.BODY_TXT, + ], + }, + RequestConfigNode.BODY_FRM_DAT: { + "required": False, + "empty": False, + "type": "dict", + "excludes": [ + RequestConfigNode.BODY_FRM, + RequestConfigNode.BODY_JSN, + RequestConfigNode.BODY_XML, + RequestConfigNode.BODY_TXT, + ], + }, + RequestConfigNode.BODY_JSN: { + "required": False, + "empty": False, + "type": "dict", + "excludes": [ + RequestConfigNode.BODY_FRM, + RequestConfigNode.BODY_FRM_DAT, + RequestConfigNode.BODY_XML, + RequestConfigNode.BODY_TXT, + ], + }, + RequestConfigNode.BODY_XML: { + "required": False, + "empty": False, + "type": "string", + "excludes": [ + RequestConfigNode.BODY_FRM, + RequestConfigNode.BODY_FRM_DAT, + RequestConfigNode.BODY_JSN, + RequestConfigNode.BODY_TXT, + ], + }, + RequestConfigNode.BODY_TXT: { + "required": False, + "empty": False, + "type": "string", + "excludes": [ + RequestConfigNode.BODY_FRM, + RequestConfigNode.BODY_FRM_DAT, + RequestConfigNode.BODY_JSN, + RequestConfigNode.BODY_XML, + ], + }, + }, + } +} + + +class ApiResponseModel(BaseModel): + """ApiResponseModel""" + + code: int = Field(default=0) + version: int = Field(default_factory=int) + reason: str = Field(default_factory=str) + headers: dict[str, str] = Field(default_factory=dict) + body: str = Field(default_factory=str) + + def __bool__(self) -> bool: + """implement __bool__""" + return self.code != 0 and self.version in (10, 11) and len(self.reason) > 0 + + @computed_field # type: ignore + @property + def info(self) -> str: + return ( + f"{Http_V10 if self.version == 10 else Http_V11} {self.code} {self.reason}" + ) + + @computed_field # type: ignore + @property + def body_content_type(self) -> str: + if "Content-Type" in self.headers: + content_type = self.headers["Content-Type"] + elif "content-type" in self.headers: + content_type = self.headers["content-type"] + + if CTYPE_JSON in content_type: + return CTYPE_JSON + elif CTYPE_XML in content_type: + return CTYPE_XML + else: + raise ValueError("Non-convertable body found") + + @info.setter # type: ignore + def set_info(self, sinfo: str) -> None: + """set info""" + + if not sinfo: + raise ValueError("Info can not be empty.") + + _version, _, _reason = sinfo.split() + + if Http_V10 == _version: + self.version = 10 + elif Http_V11 == _version: + self.version = 11 + else: + raise ValueError("Unsupported protocol.") + + self.reason = _reason + + def body_as_dict(self) -> abc.Iterable: + if CTYPE_JSON == self.body_content_type: + return json.loads(self.body) + elif CTYPE_XML == self.body_content_type: + return xmltodict.parse(parseString(self.body).toxml()) + else: + raise ValueError("Non-convertable body found") + + @model_serializer + def mdl_serializer(self) -> dict[str, Any]: + _dict = { + "code": self.code, + "info": self.info, + "headers": self.headers, + } + + try: + _dict["body"] = self.body_as_dict() + except (TypeError, ValueError): + _dict["body"] = self.body + + return _dict + + @staticmethod + def from_response(response: requests.Response) -> ApiResponseModel: + """Create a ApiResponseModel object from requests.Response object + + Args: + response (requests.Response): _description_ + + Returns: + ApiResponseModel: _description_ + """ + + arm = ApiResponseModel( + code=response.status_code, + version=11 if response.raw.version == 0 else response.raw.version, + reason=response.reason, + headers=dict(response.headers), + body=response.text, + ) + + if arm: + arm.body_content_type + + return arm + + @staticmethod + def from_dict(**kwargs: dict) -> ApiResponseModel: + """Construct from dict""" + + if not all( + [item in kwargs.keys() for item in ["code", "info", "headers", "body"]] + ): + raise KeyError("Expected keys to make ApiResponseModel not found") + + if not isinstance(kwargs["code"], int): + raise ValueError("Invalid code.") + + if not isinstance(kwargs["headers"], dict): + raise ValueError("Invalid headers.") + + if "Content-Type" in kwargs["headers"]: + content_type = kwargs["headers"]["Content-Type"] + elif "content-type" in kwargs["headers"]: + content_type = kwargs["headers"]["content-type"] + + if CTYPE_JSON in content_type: + _body = json.dumps(kwargs["body"]) + elif CTYPE_XML in content_type: + _body = parseString(kwargs["body"]).toxml() + else: + raise ValueError("Non-convertable body found") + + model = ApiResponseModel( + code=Cast.to_int(kwargs["code"]), + headers=kwargs["headers"], + body=_body, + ) + model.info = kwargs["info"] + + return model + + def as_fmt_str(self) -> str: + """String representation of ApiResponseModel + + Returns: + str: String representation + """ + + # set info + presentation = f"{self.info}\r\n\r\n" + + # set headers + presentation += "\r\n".join(f"{k}: {v}" for k, v in self.headers.items()) + presentation += "\r\n\r\n" + + presentation += ( + json.dumps(self.body_as_dict()) + if self.body_content_type == CTYPE_JSON + else self.body + ) + + return presentation + + +class BearerAuthentication(requests.auth.AuthBase): + """Authentication: Bearer ... support""" + + def __init__(self, token: str) -> None: + """Construct BearerAuthentication""" + + self.token = token + + def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest: + """Add the actual header on call""" + + r.headers["authorization"] = "Bearer " + self.token + return r + + +class HttpDocument(VersionedDocumentV2, BaseModel): + """ + Http document entity + """ + + request: dict = Field(default_factory=dict) + + def __bool__(self) -> bool: + """Check is the document is empty""" + + return len(self.request) > 0 + + +class FetchTask(BaseModel): + """Parsed FetchTask""" + + name: str + uses: str + file: str + variables: dict = Field(default_factory=dict) + arguments: dict = Field(default_factory=dict) diff --git a/chk/modules/fetch/fetch.py b/chk/modules/fetch/fetch.py deleted file mode 100644 index 243a56ba..00000000 --- a/chk/modules/fetch/fetch.py +++ /dev/null @@ -1,754 +0,0 @@ -""" -Fetch module -""" - -from __future__ import annotations - -import enum -import json -import pathlib -import sys -from collections import abc -from typing import Any -from urllib.parse import unquote, urlparse - -import requests -import xmltodict -from defusedxml.minidom import parseString -from pydantic import BaseModel, Field, computed_field, model_serializer -from requests.auth import HTTPBasicAuth - -from chk.infrastructure.document import ( - VersionedDocumentSupport, - VersionedDocumentV2, -) -from chk.infrastructure.file_loader import ExecuteContext, FileContext -from chk.infrastructure.helper import Cast, data_get -from chk.infrastructure.logging import debug, error_trace, with_catch_log -from chk.infrastructure.symbol_table import ( - EXPOSE_SCHEMA as EXP_SCHEMA, - ExecResponse, - ExposeManager, - VARIABLE_SCHEMA as VAR_SCHEMA, - VariableTableManager, - Variables, - replace_value, -) -from chk.infrastructure.version import DocumentVersionMaker, SCHEMA as VER_SCHEMA -from chk.infrastructure.view import ( - PresentationBuilder, - PresentationService, - die_with_error, -) - -VERSION_SCOPE = ["http"] -Http_V10 = "HTTP/1.0" -Http_V11 = "HTTP/1.1" -CTYPE_JSON = "application/json" -CTYPE_XML = "application/xml" - - -class ApiResponseModel(BaseModel): - """ApiResponseModel""" - - code: int = Field(default=0) - version: int = Field(default_factory=int) - reason: str = Field(default_factory=str) - headers: dict[str, str] = Field(default_factory=dict) - body: str = Field(default_factory=str) - - def __bool__(self) -> bool: - """implement __bool__""" - return self.code != 0 and self.version in (10, 11) and len(self.reason) > 0 - - @computed_field # type: ignore[prop-decorator] - @property - def info(self) -> str: - return ( - f"{Http_V10 if self.version == 10 else Http_V11} {self.code} {self.reason}" - ) - - @computed_field # type: ignore[prop-decorator] - @property - def body_content_type(self) -> str: - if "Content-Type" in self.headers: - content_type = self.headers["Content-Type"] - elif "content-type" in self.headers: - content_type = self.headers["content-type"] - - if CTYPE_JSON in content_type: - return CTYPE_JSON - elif CTYPE_XML in content_type: - return CTYPE_XML - else: - raise ValueError("Non-convertable body found") - - @info.setter - def set_info(self, sinfo: str) -> None: - """set info""" - - if not sinfo: - raise ValueError("Info can not be empty.") - - _version, _, _reason = sinfo.split() - - if Http_V10 == _version: - self.version = 10 - elif Http_V11 == _version: - self.version = 11 - else: - raise ValueError("Unsupported protocol.") - - self.reason = _reason - - def body_as_dict(self) -> abc.Iterable: - if CTYPE_JSON == self.body_content_type: - return json.loads(self.body) - elif CTYPE_XML == self.body_content_type: - return xmltodict.parse(parseString(self.body).toxml()) - else: - raise ValueError("Non-convertable body found") - - @model_serializer - def mdl_serializer(self) -> dict[str, Any]: - _dict = { - "code": self.code, - "info": self.info, - "headers": self.headers, - } - - try: - _dict["body"] = self.body_as_dict() - except (TypeError, ValueError): - _dict["body"] = self.body - - return _dict - - @staticmethod - def from_response(response: requests.Response) -> ApiResponseModel: - """Create a ApiResponseModel object from requests.Response object - - Args: - response (requests.Response): _description_ - - Returns: - ApiResponseModel: _description_ - """ - - arm = ApiResponseModel( - code=response.status_code, - version=11 if response.raw.version == 0 else response.raw.version, - reason=response.reason, - headers=dict(response.headers), - body=response.text, - ) - - if arm: - arm.body_content_type - - return arm - - @staticmethod - def from_dict(**kwargs: dict) -> ApiResponseModel: - """Construct from dict""" - - if not all( - [item in kwargs.keys() for item in ["code", "info", "headers", "body"]] - ): - raise KeyError("Expected keys to make ApiResponseModel not found") - - if not isinstance(kwargs["code"], int): - raise ValueError("Invalid code.") - - if not isinstance(kwargs["headers"], dict): - raise ValueError("Invalid headers.") - - if "Content-Type" in kwargs["headers"]: - content_type = kwargs["headers"]["Content-Type"] - elif "content-type" in kwargs["headers"]: - content_type = kwargs["headers"]["content-type"] - - if CTYPE_JSON in content_type: - _body = json.dumps(kwargs["body"]) - elif CTYPE_XML in content_type: - _body = parseString(kwargs["body"]).toxml() - else: - raise ValueError("Non-convertable body found") - - model = ApiResponseModel( - code=Cast.to_int(kwargs["code"]), - headers=kwargs["headers"], - body=_body, - ) - model.info = kwargs["info"] - - return model - - def as_fmt_str(self) -> str: - """String representation of ApiResponseModel - - Returns: - str: String representation - """ - - # set info - presentation = f"{self.info}\r\n\r\n" - - # set headers - presentation += "\r\n".join(f"{k}: {v}" for k, v in self.headers.items()) - presentation += "\r\n\r\n" - - presentation += ( - json.dumps(self.body_as_dict()) - if self.body_content_type == CTYPE_JSON - else self.body - ) - - return presentation - - -class BearerAuthentication(requests.auth.AuthBase): - """Authentication: Bearer ... support""" - - def __init__(self, token: str) -> None: - """Construct BearerAuthentication""" - - self.token = token - - def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest: - """Add the actual header on call""" - - r.headers["authorization"] = "Bearer " + self.token - return r - - -class HttpMethod(enum.StrEnum): - """Constants of wellknown http methods""" - - GET = "GET" - POST = "POST" - PUT = "PUT" - PATCH = "PATCH" - DELETE = "DELETE" - HEAD = "HEAD" - OPTIONS = "OPTIONS" - - -class RequestConfigNode(enum.StrEnum): - """represent request config section""" - - ROOT = "request" - LOCAL = "_response" - - # common request - URL = enum.auto() - METHOD = enum.auto() - HEADERS = enum.auto() - PARAMS = "url_params" - - # Basic - AUTH_BA = "auth .scm=basic" - AUTH_BA_USR = "username" - AUTH_BA_PAS = "password" - - # Bearer - AUTH_BE = "auth .scm=bearer" - AUTH_BE_TOK = "token" - - # Body - BODY_FRM = "body .enc=form" - BODY_FRM_DAT = "body .enc=form-data" - BODY_JSN = "body .enc=json" - BODY_XML = "body .enc=xml" - BODY_TXT = "body .enc=text" - - -SCHEMA = { - RequestConfigNode.ROOT: { - "required": True, - "type": "dict", - "schema": { - RequestConfigNode.URL: { - "required": True, - "empty": False, - "type": "string", - }, - RequestConfigNode.METHOD: { - "required": True, - "type": "string", - }, - RequestConfigNode.PARAMS: { - "required": False, - "empty": False, - "type": "dict", - }, - RequestConfigNode.HEADERS: { - "required": False, - "empty": False, - "type": "dict", - }, - RequestConfigNode.AUTH_BA: { - "required": False, - "empty": False, - "type": "dict", - "excludes": RequestConfigNode.AUTH_BE, - }, - RequestConfigNode.AUTH_BE: { - "required": False, - "empty": False, - "type": "dict", - "excludes": RequestConfigNode.AUTH_BA, - }, - RequestConfigNode.BODY_FRM: { - "required": False, - "empty": False, - "type": "dict", - "excludes": [ - RequestConfigNode.BODY_FRM_DAT, - RequestConfigNode.BODY_JSN, - RequestConfigNode.BODY_XML, - RequestConfigNode.BODY_TXT, - ], - }, - RequestConfigNode.BODY_FRM_DAT: { - "required": False, - "empty": False, - "type": "dict", - "excludes": [ - RequestConfigNode.BODY_FRM, - RequestConfigNode.BODY_JSN, - RequestConfigNode.BODY_XML, - RequestConfigNode.BODY_TXT, - ], - }, - RequestConfigNode.BODY_JSN: { - "required": False, - "empty": False, - "type": "dict", - "excludes": [ - RequestConfigNode.BODY_FRM, - RequestConfigNode.BODY_FRM_DAT, - RequestConfigNode.BODY_XML, - RequestConfigNode.BODY_TXT, - ], - }, - RequestConfigNode.BODY_XML: { - "required": False, - "empty": False, - "type": "string", - "excludes": [ - RequestConfigNode.BODY_FRM, - RequestConfigNode.BODY_FRM_DAT, - RequestConfigNode.BODY_JSN, - RequestConfigNode.BODY_TXT, - ], - }, - RequestConfigNode.BODY_TXT: { - "required": False, - "empty": False, - "type": "string", - "excludes": [ - RequestConfigNode.BODY_FRM, - RequestConfigNode.BODY_FRM_DAT, - RequestConfigNode.BODY_JSN, - RequestConfigNode.BODY_XML, - ], - }, - }, - } -} - - -def allowed_method(value: str) -> bool: - """Validate if given method is allowed - - Raises: - ValueError: When unsupported method found - """ - - if value not in set(method for method in HttpMethod): - raise ValueError("Unsupported method") - - return True - - -def allowed_url(value: str) -> bool: - """Validate if given URL is allowed""" - - parsed_url = urlparse(value) - - if all([parsed_url.scheme, parsed_url.netloc]) is False: - raise ValueError("Invalid `url`") - - if parsed_url.scheme not in ["http", "https"]: - raise ValueError("Invalid `url` scheme. http and https allowed") - - return True - - -class FetchTask(BaseModel): - """Parsed FetchTask""" - - name: str - uses: str - file: str - variables: dict = Field(default_factory=dict) - arguments: dict = Field(default_factory=dict) - - -class HttpRequestArgCompiler: - """HttpRequestArgCompiler""" - - @staticmethod - def add_url_and_method(request_data: dict, request_arg: dict) -> None: - """add default request url and request method""" - if RequestConfigNode.METHOD not in request_data: - raise KeyError("required key `method:` not found") - - if allowed_method(request_data[RequestConfigNode.METHOD]): - request_arg["method"] = request_data.get(RequestConfigNode.METHOD) - - if RequestConfigNode.URL not in request_data: - raise KeyError("required key `url:` not found") - - if allowed_url(request_data[RequestConfigNode.URL]): - request_arg["url"] = request_data.get(RequestConfigNode.URL) - - @staticmethod - def add_query_string(request_data: dict, request_arg: dict) -> None: - """add query string""" - if (params := request_data.get(RequestConfigNode.PARAMS)) is not None: - request_arg["params"] = params - - @staticmethod - def add_headers(request_data: dict, request_arg: dict) -> None: - """add custom header""" - if (headers := request_data.get(RequestConfigNode.HEADERS)) is not None: - request_arg["headers"] = headers - - @staticmethod - def add_authorization(request_data: dict, request_arg: dict) -> None: - """handle authorization header""" - # handle basic auth - if (tag_ba := request_data.get(RequestConfigNode.AUTH_BA)) is not None: - request_arg["auth"] = HTTPBasicAuth( - tag_ba.get(RequestConfigNode.AUTH_BA_USR), - tag_ba.get(RequestConfigNode.AUTH_BA_PAS), - ) - - # handle bearer auth - if (tag_be := request_data.get(RequestConfigNode.AUTH_BE)) is not None: - request_arg["auth"] = BearerAuthentication( - tag_be.get(RequestConfigNode.AUTH_BE_TOK) - ) - - @staticmethod - def add_body(request_data: dict, request_arg: dict) -> None: - """add body""" - if (body := request_data.get(RequestConfigNode.BODY_FRM)) is not None: - request_arg["data"] = dict(body) - elif (body := request_data.get(RequestConfigNode.BODY_FRM_DAT)) is not None: - data = {} - files = {} - - for key, val in dict(body).items(): - if isinstance(val, str) and val.startswith("file://"): - path_parsed = urlparse(val) - path = unquote(path_parsed.path) - netloc = unquote(path_parsed.netloc) - - filepath = pathlib.Path(f"{netloc}{path}") - if not filepath.expanduser().exists(): - raise FileNotFoundError(f"path `{val}` do not exists") - - files[key] = str(filepath.expanduser().resolve()) - else: - data[key] = val - - request_arg["data"] = data - request_arg["files"] = files - - elif (body := request_data.get(RequestConfigNode.BODY_JSN)) is not None: - request_arg["json"] = dict(body) - elif (body := request_data.get(RequestConfigNode.BODY_XML)) is not None: - if "headers" not in request_arg: - request_arg["headers"] = {} - - if ( - "content-type" not in request_arg["headers"] - and "Content-Type" not in request_arg["headers"] - ): - request_arg["headers"]["content-type"] = CTYPE_XML - - request_arg["data"] = body - elif (body := request_data.get(RequestConfigNode.BODY_TXT)) is not None: - if "headers" not in request_arg: - request_arg["headers"] = {} - - if ( - "content-type" not in request_arg["headers"] - and "Content-Type" not in request_arg["headers"] - ): - request_arg["headers"]["content-type"] = "text/plain" - - request_arg["data"] = str(body) - - @staticmethod - def add_generic_args(request_data: dict, request_arg: dict) -> None: - """add default request parameters regardless of method""" - HttpRequestArgCompiler.add_url_and_method(request_data, request_arg) - HttpRequestArgCompiler.add_query_string(request_data, request_arg) - HttpRequestArgCompiler.add_headers(request_data, request_arg) - HttpRequestArgCompiler.add_authorization(request_data, request_arg) - HttpRequestArgCompiler.add_body(request_data, request_arg) - - -class HttpDocument(VersionedDocumentV2, BaseModel): - """ - Http document entity - """ - - request: dict = Field(default_factory=dict) - - def __bool__(self) -> bool: - """Check is the document is empty""" - - return len(self.request) > 0 - - @staticmethod - def from_file_context(ctx: FileContext) -> HttpDocument: - """Create a HttpDocument from FileContext - :param ctx: FileContext to create the HttpDocument from - """ - - doc_ver = DocumentVersionMaker.from_dict(ctx.document) - DocumentVersionMaker.verify_if_allowed(doc_ver, VERSION_SCOPE) - - if not (version_str := data_get(ctx.document, "version")): - raise RuntimeError("`version:` not found.") - - if not (request_dct := data_get(ctx.document, "request")): - raise RuntimeError("`request:` not found.") - - # @TODO keep `context`, `version` as object - # @TODO implement __repr__ for WorkflowDocument - return HttpDocument( - context=tuple(ctx), - version=version_str, - request=request_dct, - ) - - -class HttpDocumentSupport: - """Service class for HttpDocument""" - - @staticmethod - def execute_request(http_doc: HttpDocument) -> ApiResponseModel: - """Execute http request from given HttpDocument - - Args: - http_doc (HttpDocument): Http request document object - - Returns: - dict: Returns response for http request - """ - - if not http_doc: - raise ValueError("Empty document found.") - - request_args: dict = {} - - HttpRequestArgCompiler.add_generic_args(http_doc.request, request_args) - - return ApiResponseModel.from_response(requests.request(**request_args)) - - @staticmethod - def process_request_template(http_doc: HttpDocument, variables: Variables) -> None: - """Replace variables in request body - - Args: - http_doc: HttpDocument, for the request body - variables: Variables, variable base - """ - - http_doc.request = replace_value(http_doc.request, variables.data) - - @staticmethod - def build_schema() -> dict: - """Validate a http document with given json-schema - - Returns: - dict: Containing http document schema - """ - - return {**VER_SCHEMA, **SCHEMA, **VAR_SCHEMA, **EXP_SCHEMA} - - -class FetchPresenter(PresentationBuilder): - """FetchPresenter""" - - def dump_error_json(self, err: object = None) -> str: - """dump_error_json""" - - if not err: - err = self.data.exception - - return json.dumps({"error": (repr(err) if err else "Unspecified error")}) - - def dump_error_fmt(self, err: object = None) -> str: - """dump fmt error str""" - - if not err: - err = self.data.exception - - return ( - f"Fetch error\n------\n{repr(err)}" - if err - else "Fetch error\n------\nUnspecified error" - ) - - def dump_json(self) -> str: - """dump json""" - - displayables: list[object] = [] - - for key, expose_item in self.data.exposed.items(): - if key == RequestConfigNode.LOCAL and all( - [ - item in expose_item.keys() - for item in ["code", "info", "headers", "body"] - ] - ): - resp = ApiResponseModel.from_dict(**expose_item) - displayables.append(resp.model_dump()) - else: - displayables.append(expose_item) - - return json.dumps(displayables) - - def dump_fmt(self) -> str: - """dump fmt string""" - - displayables: list[str] = [] - - for key, expose_item in self.data.exposed.items(): - if key == RequestConfigNode.LOCAL and all( - [ - item in expose_item.keys() - for item in ["code", "info", "headers", "body"] - ] - ): - resp = ApiResponseModel.from_dict(**expose_item) - displayables.append(resp.as_fmt_str()) - else: - displayables.append(json.dumps(expose_item)) - - return "\n======\n".join(displayables) - - -@with_catch_log -def call(file_ctx: FileContext, exec_ctx: ExecuteContext) -> ExecResponse: - """Call a http document""" - - debug(file_ctx) - debug(exec_ctx) - - r_exception: Exception | None = None - variable_doc = Variables() - output_data = Variables() - exposed_data = {} - - try: - http_doc = HttpDocument.from_file_context(file_ctx) - debug(http_doc.model_dump_json()) - - VersionedDocumentSupport.validate_with_schema( - HttpDocumentSupport.build_schema(), http_doc - ) - - VariableTableManager.handle(variable_doc, http_doc, exec_ctx) - debug(variable_doc.data) - - HttpDocumentSupport.process_request_template(http_doc, variable_doc) - debug(http_doc.model_dump_json()) - - # try: - response = HttpDocumentSupport.execute_request(http_doc) - - output_data = Variables({"_response": response.model_dump()}) - debug(output_data.data) - - exposed_data = ExposeManager.get_exposed_replaced_data( - http_doc, - {**variable_doc.data, **output_data.data}, - ) - debug(exposed_data) - - except Exception as ex: - r_exception = ex - error_trace(exception=sys.exc_info()).error(ex) - - # TODO: instead if sending specific report items, and making presentable in other - # module, we should prepare and long and short form of presentable that can be - # loaded via other module - - request_method, request_url = "", "" - - if "request" in file_ctx.document: - if "method" in file_ctx.document["request"]: - request_method = file_ctx.document["request"]["method"] - if "url" in file_ctx.document["request"]: - request_url = file_ctx.document["request"]["url"] - - return ExecResponse( - file_ctx=file_ctx, - exec_ctx=exec_ctx, - variables_exec=output_data, - variables=variable_doc, - exception=r_exception, - exposed=exposed_data, - report={ - "is_success": r_exception is None, - "request_method": request_method, - "request_url": request_url, - }, - ) - - -@with_catch_log -def execute( - ctx: FileContext, exec_ctx: ExecuteContext, cb: abc.Callable = lambda *args: ... -) -> None: - """Call with a http document - - Args: - ctx: FileContext object to handle - exec_ctx: ExecuteContext - cb: Callable - """ - - try: - exr = call(file_ctx=ctx, exec_ctx=exec_ctx) - cb({ctx.filepath_hash: exr.variables_exec.data}) - PresentationService.display(exr, exec_ctx, FetchPresenter) - except Exception as ex: - error_trace(exception=sys.exc_info()).error(ex) - die_with_error(ex, FetchPresenter, exec_ctx.options["format"]) - - -@with_catch_log -def task_fetch(**kwargs: dict) -> ExecResponse: - """Task impl""" - - if not (doc := kwargs.get("task", {})): - raise ValueError("Wrong task format given.") - - _task = FetchTask(**doc) - - return call( - FileContext.from_file(_task.file), - ExecuteContext( - options={"dump": True, "format": True}, - arguments=_task.arguments | {"variables": _task.variables}, - ), - ) diff --git a/chk/modules/fetch/services.py b/chk/modules/fetch/services.py new file mode 100644 index 00000000..5113e61c --- /dev/null +++ b/chk/modules/fetch/services.py @@ -0,0 +1,296 @@ +""" +Fetch module +""" + +from __future__ import annotations + +import json +import pathlib +from urllib.parse import unquote, urlparse + +import requests +from requests.auth import HTTPBasicAuth + +from chk.infrastructure.file_loader import FileContext +from chk.infrastructure.helper import data_get +from chk.infrastructure.symbol_table import ( + EXPOSE_SCHEMA as EXP_SCHEMA, + VARIABLE_SCHEMA as VAR_SCHEMA, + Variables, + replace_value, +) +from chk.infrastructure.version import DocumentVersionMaker, SCHEMA as VER_SCHEMA +from chk.infrastructure.view import ( + PresentationBuilder, +) +from chk.modules.fetch.entities import ( + ApiResponseModel, + BearerAuthentication, + CTYPE_XML, HttpDocument, + HttpMethod, + RequestConfigNode, SCHEMA, VERSION_SCOPE, +) + + +def allowed_method(value: str) -> bool: + """Validate if given method is allowed + + Raises: + ValueError: When unsupported method found + """ + + if value not in set(method for method in HttpMethod): + raise ValueError("Unsupported method") + + return True + + +def allowed_url(value: str) -> bool: + """Validate if given URL is allowed""" + + parsed_url = urlparse(value) + + if all([parsed_url.scheme, parsed_url.netloc]) is False: + raise ValueError("Invalid `url`") + + if parsed_url.scheme not in ["http", "https"]: + raise ValueError("Invalid `url` scheme. http and https allowed") + + return True + + +class HttpRequestArgCompiler: + """HttpRequestArgCompiler""" + + @staticmethod + def add_url_and_method(request_data: dict, request_arg: dict) -> None: + """add default request url and request method""" + if RequestConfigNode.METHOD not in request_data: + raise KeyError("required key `method:` not found") + + if allowed_method(request_data[RequestConfigNode.METHOD]): + request_arg["method"] = request_data.get(RequestConfigNode.METHOD) + + if RequestConfigNode.URL not in request_data: + raise KeyError("required key `url:` not found") + + if allowed_url(request_data[RequestConfigNode.URL]): + request_arg["url"] = request_data.get(RequestConfigNode.URL) + + @staticmethod + def add_query_string(request_data: dict, request_arg: dict) -> None: + """add query string""" + if (params := request_data.get(RequestConfigNode.PARAMS)) is not None: + request_arg["params"] = params + + @staticmethod + def add_headers(request_data: dict, request_arg: dict) -> None: + """add custom header""" + if (headers := request_data.get(RequestConfigNode.HEADERS)) is not None: + request_arg["headers"] = headers + + @staticmethod + def add_authorization(request_data: dict, request_arg: dict) -> None: + """handle authorization header""" + # handle basic auth + if (tag_ba := request_data.get(RequestConfigNode.AUTH_BA)) is not None: + request_arg["auth"] = HTTPBasicAuth( + tag_ba.get(RequestConfigNode.AUTH_BA_USR), + tag_ba.get(RequestConfigNode.AUTH_BA_PAS), + ) + + # handle bearer auth + if (tag_be := request_data.get(RequestConfigNode.AUTH_BE)) is not None: + request_arg["auth"] = BearerAuthentication( + tag_be.get(RequestConfigNode.AUTH_BE_TOK) + ) + + @staticmethod + def add_body(request_data: dict, request_arg: dict) -> None: + """add body""" + if (body := request_data.get(RequestConfigNode.BODY_FRM)) is not None: + request_arg["data"] = dict(body) + elif (body := request_data.get(RequestConfigNode.BODY_FRM_DAT)) is not None: + data = {} + files = {} + + for key, val in dict(body).items(): + if isinstance(val, str) and val.startswith("file://"): + path_parsed = urlparse(val) + path = unquote(path_parsed.path) + netloc = unquote(path_parsed.netloc) + + filepath = pathlib.Path(f"{netloc}{path}") + if not filepath.expanduser().exists(): + raise FileNotFoundError(f"path `{val}` do not exists") + + files[key] = str(filepath.expanduser().resolve()) + else: + data[key] = val + + request_arg["data"] = data + request_arg["files"] = files + + elif (body := request_data.get(RequestConfigNode.BODY_JSN)) is not None: + request_arg["json"] = dict(body) + elif (body := request_data.get(RequestConfigNode.BODY_XML)) is not None: + if "headers" not in request_arg: + request_arg["headers"] = {} + + if ( + "content-type" not in request_arg["headers"] + and "Content-Type" not in request_arg["headers"] + ): + request_arg["headers"]["content-type"] = CTYPE_XML + + request_arg["data"] = body + elif (body := request_data.get(RequestConfigNode.BODY_TXT)) is not None: + if "headers" not in request_arg: + request_arg["headers"] = {} + + if ( + "content-type" not in request_arg["headers"] + and "Content-Type" not in request_arg["headers"] + ): + request_arg["headers"]["content-type"] = "text/plain" + + request_arg["data"] = str(body) + + @staticmethod + def add_generic_args(request_data: dict, request_arg: dict) -> None: + """add default request parameters regardless of method""" + HttpRequestArgCompiler.add_url_and_method(request_data, request_arg) + HttpRequestArgCompiler.add_query_string(request_data, request_arg) + HttpRequestArgCompiler.add_headers(request_data, request_arg) + HttpRequestArgCompiler.add_authorization(request_data, request_arg) + HttpRequestArgCompiler.add_body(request_data, request_arg) + + +class HttpDocumentSupport: + """Service class for HttpDocument""" + + @staticmethod + def from_file_context(ctx: FileContext) -> HttpDocument: + """Create a HttpDocument from FileContext + :param ctx: FileContext to create the HttpDocument from + """ + + doc_ver = DocumentVersionMaker.from_dict(ctx.document) + DocumentVersionMaker.verify_if_allowed(doc_ver, VERSION_SCOPE) + + if not (version_str := data_get(ctx.document, "version")): + raise RuntimeError("`version:` not found.") + + if not (request_dct := data_get(ctx.document, "request")): + raise RuntimeError("`request:` not found.") + + # @TODO keep `context`, `version` as object + # @TODO implement __repr__ for WorkflowDocument + return HttpDocument( + context=tuple(ctx), + version=version_str, + request=request_dct, + ) + + @staticmethod + def execute_request(http_doc: HttpDocument) -> ApiResponseModel: + """Execute http request from given HttpDocument + + Args: + http_doc (HttpDocument): Http request document object + + Returns: + dict: Returns response for http request + """ + + if not http_doc: + raise ValueError("Empty document found.") + + request_args: dict = {} + + HttpRequestArgCompiler.add_generic_args(http_doc.request, request_args) + + return ApiResponseModel.from_response(requests.request(**request_args)) + + @staticmethod + def process_request_template(http_doc: HttpDocument, variables: Variables) -> None: + """Replace variables in request body + + Args: + http_doc: HttpDocument, for the request body + variables: Variables, variable base + """ + + http_doc.request = replace_value(http_doc.request, variables.data) + + @staticmethod + def build_schema() -> dict: + """Validate a http document with given json-schema + + Returns: + dict: Containing http document schema + """ + + return {**VER_SCHEMA, **SCHEMA, **VAR_SCHEMA, **EXP_SCHEMA} + + +class FetchPresenter(PresentationBuilder): + """FetchPresenter""" + + def dump_error_json(self, err: object = None) -> str: + """dump_error_json""" + + if not err: + err = self.data.exception + + return json.dumps({"error": (repr(err) if err else "Unspecified error")}) + + def dump_error_fmt(self, err: object = None) -> str: + """dump fmt error str""" + + if not err: + err = self.data.exception + + return ( + f"Fetch error\n------\n{repr(err)}" + if err + else "Fetch error\n------\nUnspecified error" + ) + + def dump_json(self) -> str: + """dump json""" + + displayables: list[object] = [] + + for key, expose_item in self.data.exposed.items(): + if key == RequestConfigNode.LOCAL and all( + [ + item in expose_item.keys() + for item in ["code", "info", "headers", "body"] + ] + ): + resp = ApiResponseModel.from_dict(**expose_item) + displayables.append(resp.model_dump()) + else: + displayables.append(expose_item) + + return json.dumps(displayables) + + def dump_fmt(self) -> str: + """dump fmt string""" + + displayables: list[str] = [] + + for key, expose_item in self.data.exposed.items(): + if key == RequestConfigNode.LOCAL and all( + [ + item in expose_item.keys() + for item in ["code", "info", "headers", "body"] + ] + ): + resp = ApiResponseModel.from_dict(**expose_item) + displayables.append(resp.as_fmt_str()) + else: + displayables.append(json.dumps(expose_item)) + + return "\n======\n".join(displayables) From c06db399ce5b9fb0ca4008d2ab0025a099c0a824 Mon Sep 17 00:00:00 2001 From: HSN Date: Mon, 11 Nov 2024 13:25:08 +0600 Subject: [PATCH 3/4] refactor: fix test imports and includes for fetch module ns update --- tests/infrastructure/symbol_table_test.py | 8 ++-- .../third_party/http_fetcher_test.py | 2 +- tests/modules/fetch/fetch_entities_test.py | 37 +++++++++++++------ tests/modules/fetch/fetch_module_test.py | 2 +- .../fetch/fetch_request_helper_test.py | 3 +- 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/tests/infrastructure/symbol_table_test.py b/tests/infrastructure/symbol_table_test.py index 8fcf0746..b962f6ba 100644 --- a/tests/infrastructure/symbol_table_test.py +++ b/tests/infrastructure/symbol_table_test.py @@ -13,7 +13,7 @@ Variables, replace_value, ) -from chk.modules.fetch import HttpDocument +from chk.modules.fetch import HttpDocumentSupport class TestVariableTableManager: @@ -39,7 +39,7 @@ def test_handle_pass(): } ) - http_doc = HttpDocument.from_file_context(file_ctx) + http_doc = HttpDocumentSupport.from_file_context(file_ctx) variable_doc = Variables() VariableTableManager.handle(variable_doc, http_doc, exc) @@ -277,7 +277,7 @@ def test_get_exposed_replaced_data_pass_returns_empty_list(): file_ctx = FileContext(filepath_hash="ab12", document=document) - http_doc = HttpDocument.from_file_context(file_ctx) + http_doc = HttpDocumentSupport.from_file_context(file_ctx) exposed_data = ExposeManager.get_exposed_replaced_data( http_doc, {"a": 1, "b": 2} @@ -302,7 +302,7 @@ def test_get_exposed_replaced_data_pass_returns_nonempty_list(): arguments={VariableConfigNode.VARIABLES: {"extension": ".org"}} ) - http_doc = HttpDocument.from_file_context(file_ctx) + http_doc = HttpDocumentSupport.from_file_context(file_ctx) variable_doc = Variables() VariableTableManager.handle(variable_doc, http_doc, exec_ctx) diff --git a/tests/infrastructure/third_party/http_fetcher_test.py b/tests/infrastructure/third_party/http_fetcher_test.py index 9ddad43a..a29a938c 100644 --- a/tests/infrastructure/third_party/http_fetcher_test.py +++ b/tests/infrastructure/third_party/http_fetcher_test.py @@ -6,7 +6,7 @@ import requests from defusedxml.minidom import parseString -from chk.modules.fetch import ApiResponseModel +from chk.modules.fetch.entities import ApiResponseModel BODY_JSON = """[ { diff --git a/tests/modules/fetch/fetch_entities_test.py b/tests/modules/fetch/fetch_entities_test.py index 1ae9ea6d..1835e6d1 100644 --- a/tests/modules/fetch/fetch_entities_test.py +++ b/tests/modules/fetch/fetch_entities_test.py @@ -1,17 +1,32 @@ # type: ignore -import json import pytest from chk.infrastructure.file_loader import FileContext from chk.infrastructure.symbol_table import Variables -from chk.modules.fetch import ( - HttpDocument, - HttpDocumentSupport, -) +from chk.modules.fetch import HttpDocumentSupport +from chk.modules.fetch.entities import HttpDocument class TestHttpDocument: + @staticmethod + def test_bool_pass(): + ctx = FileContext( + document={ + "version": "default:http:0.7.2", + "request": { + "url": "https://jsonplaceholder.typicode.com/albums/1", + "method": "GET", + }, + } + ) + + doc = HttpDocumentSupport.from_file_context(ctx) + + assert doc + + +class TestHttpDocumentSupport: @staticmethod def test_from_file_context_pass(): ctx = FileContext( @@ -24,7 +39,7 @@ def test_from_file_context_pass(): } ) - doc = HttpDocument.from_file_context(ctx) + doc = HttpDocumentSupport.from_file_context(ctx) assert isinstance(doc.context, tuple) assert isinstance(doc.version, str) @@ -39,7 +54,7 @@ def test_from_file_context_fail_no_request(): ) with pytest.raises(RuntimeError): - HttpDocument.from_file_context(ctx) + HttpDocumentSupport.from_file_context(ctx) @staticmethod def test_from_file_context_fail_no_version(): @@ -53,10 +68,8 @@ def test_from_file_context_fail_no_version(): ) with pytest.raises(ValueError): - HttpDocument.from_file_context(ctx) + HttpDocumentSupport.from_file_context(ctx) - -class TestHttpDocumentSupport: @staticmethod def test_execute_request_pass(): ctx = FileContext( @@ -69,7 +82,7 @@ def test_execute_request_pass(): } ) - http_doc = HttpDocument.from_file_context(ctx) + http_doc = HttpDocumentSupport.from_file_context(ctx) resp = HttpDocumentSupport.execute_request(http_doc) assert "userId" in resp.body @@ -86,7 +99,7 @@ def test_process_request_template_pass(): } ) - http_doc = HttpDocument.from_file_context(ctx) + http_doc = HttpDocumentSupport.from_file_context(ctx) variable_doc = Variables({"method": "GET"}) HttpDocumentSupport.process_request_template(http_doc, variable_doc) diff --git a/tests/modules/fetch/fetch_module_test.py b/tests/modules/fetch/fetch_module_test.py index 3bd16c24..e3c104e1 100644 --- a/tests/modules/fetch/fetch_module_test.py +++ b/tests/modules/fetch/fetch_module_test.py @@ -3,7 +3,7 @@ """ Fetch module tests """ -from chk.infrastructure.file_loader import FileContext, ExecuteContext +from chk.infrastructure.file_loader import ExecuteContext, FileContext from chk.modules.fetch import call diff --git a/tests/modules/fetch/fetch_request_helper_test.py b/tests/modules/fetch/fetch_request_helper_test.py index 67af3420..f341fff8 100644 --- a/tests/modules/fetch/fetch_request_helper_test.py +++ b/tests/modules/fetch/fetch_request_helper_test.py @@ -2,7 +2,8 @@ import pytest -from chk.modules.fetch import BearerAuthentication, HttpRequestArgCompiler +from chk.modules.fetch.entities import BearerAuthentication +from chk.modules.fetch.services import HttpRequestArgCompiler class TestHttpRequestArgCompiler: From 3f843905ccde2068fc1b8febb52ac7cc10bfc383 Mon Sep 17 00:00:00 2001 From: HSN Date: Mon, 11 Nov 2024 13:33:36 +0600 Subject: [PATCH 4/4] chore: update packages version --- Pipfile.lock | 191 +++++++++++++++++++++---------------------- requirements-dev.txt | 14 ++-- requirements.txt | 2 +- 3 files changed, 103 insertions(+), 104 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index a179847e..3775d1f7 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -373,7 +373,7 @@ "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], - "markers": "python_version < '3.13'", + "markers": "python_version >= '3.8'", "version": "==4.12.2" }, "urllib3": { @@ -497,6 +497,7 @@ "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092", "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414" ], + "markers": "python_version >= '3.8'", "version": "==3.10.10" }, "aiosignal": { @@ -685,7 +686,6 @@ "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" ], - "index": "pypi", "markers": "python_version >= '3.7'", "version": "==8.1.7" }, @@ -782,7 +782,7 @@ "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c" ], - "markers": "python_version >= '3.11'", + "markers": "python_version >= '3.8'", "version": "==0.3.9" }, "docopt": { @@ -1093,11 +1093,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "pathspec": { "hashes": [ @@ -1271,19 +1271,18 @@ }, "pytest-cov": { "hashes": [ - "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", - "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857" + "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", + "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==5.0.0" + "markers": "python_version >= '3.9'", + "version": "==6.0.0" }, "requests": { "hashes": [ "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], - "index": "pypi", "markers": "python_version >= '3.8'", "version": "==2.32.3" }, @@ -1301,7 +1300,7 @@ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==1.16.0" }, "tomlkit": { @@ -1335,7 +1334,7 @@ "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], - "markers": "python_version < '3.13'", + "markers": "python_version >= '3.8'", "version": "==4.12.2" }, "urllib3": { @@ -1348,91 +1347,91 @@ }, "yarl": { "hashes": [ - "sha256:01c96efa4313c01329e88b7e9e9e1b2fc671580270ddefdd41129fa8d0db7696", - "sha256:02608fb3f6df87039212fc746017455ccc2a5fc96555ee247c45d1e9f21f1d7b", - "sha256:05e13f389038842da930d439fbed63bdce3f7644902714cb68cf527c971af804", - "sha256:07055a9e8b647a362e7d4810fe99d8f98421575e7d2eede32e008c89a65a17bd", - "sha256:08fc76df7fd8360e9ff30e6ccc3ee85b8dbd6ed5d3a295e6ec62bcae7601b932", - "sha256:0bc53cc349675b32ead83339a8de79eaf13b88f2669c09d4962322bb0f064cbc", - "sha256:0d44f67e193f0a7acdf552ecb4d1956a3a276c68e7952471add9f93093d1c30d", - "sha256:13468d291fe8c12162b7cf2cdb406fe85881c53c9e03053ecb8c5d3523822cd9", - "sha256:146ca582ed04a5664ad04b0e0603934281eaab5c0115a5a46cce0b3c061a56a1", - "sha256:147c527a80bb45b3dcd6e63401af8ac574125d8d120e6afe9901049286ff64ef", - "sha256:153c38ee2b4abba136385af4467459c62d50f2a3f4bde38c7b99d43a20c143ef", - "sha256:16ca76c7ac9515320cd09d6cc083d8d13d1803f6ebe212b06ea2505fd66ecff8", - "sha256:16ea0aa5f890cdcb7ae700dffa0397ed6c280840f637cd07bffcbe4b8d68b985", - "sha256:174d6a6cad1068f7850702aad0c7b1bca03bcac199ca6026f84531335dfc2646", - "sha256:1803bf2a7a782e02db746d8bd18f2384801bc1d108723840b25e065b116ad726", - "sha256:19a4fe0279626c6295c5b0c8c2bb7228319d2e985883621a6e87b344062d8135", - "sha256:1d013a7c9574e98c14831a8f22d27277688ec3b2741d0188ac01a910b009987a", - "sha256:1fab91292f51c884b290ebec0b309a64a5318860ccda0c4940e740425a67b6b7", - "sha256:24cf43bcd17a0a1f72284e47774f9c60e0bf0d2484d5851f4ddf24ded49f33c6", - "sha256:261be774a0d71908c8830c33bacc89eef15c198433a8cc73767c10eeeb35a7d0", - "sha256:263b487246858e874ab53e148e2a9a0de8465341b607678106829a81d81418c6", - "sha256:2d8715edfe12eee6f27f32a3655f38d6c7410deb482158c0b7d4b7fad5d07628", - "sha256:2e66589110e20c2951221a938fa200c7aa134a8bdf4e4dc97e6b21539ff026d4", - "sha256:30534a03c87484092080e3b6e789140bd277e40f453358900ad1f0f2e61fc8ec", - "sha256:32f3ee19ff0f18a7a522d44e869e1ebc8218ad3ae4ebb7020445f59b4bbe5897", - "sha256:350cacb2d589bc07d230eb995d88fcc646caad50a71ed2d86df533a465a4e6e1", - "sha256:3616df510ffac0df3c9fa851a40b76087c6c89cbcea2de33a835fc80f9faac24", - "sha256:4065b4259d1ae6f70fd9708ffd61e1c9c27516f5b4fae273c41028afcbe3a094", - "sha256:41fd5498975418cdc34944060b8fbeec0d48b2741068077222564bea68daf5a6", - "sha256:437bf6eb47a2d20baaf7f6739895cb049e56896a5ffdea61a4b25da781966e8b", - "sha256:46027e326cecd55e5950184ec9d86c803f4f6fe4ba6af9944a0e537d643cdbe0", - "sha256:48f51b529b958cd06e78158ff297a8bf57b4021243c179ee03695b5dbf9cb6e1", - "sha256:4934e0f96dadc567edc76d9c08181633c89c908ab5a3b8f698560124167d9488", - "sha256:4d14be4613dd4f96c25feb4bd8c0d8ce0f529ab0ae555a17df5789e69d8ec0c5", - "sha256:5542e57dc15d5473da5a39fbde14684b0cc4301412ee53cbab677925e8497c11", - "sha256:56294218b348dcbd3d7fce0ffd79dd0b6c356cb2a813a1181af730b7c40de9e7", - "sha256:5b937c216b6dee8b858c6afea958de03c5ff28406257d22b55c24962a2baf6fd", - "sha256:5bb8bf3843e1fa8cf3fe77813c512818e57368afab7ebe9ef02446fe1a10b492", - "sha256:5cf93fa61ff4d9c7d40482ce1a2c9916ca435e34a1b8451e17f295781ccc034f", - "sha256:5dc16477a4a2c71e64c5d3d15d7ae3d3a6bb1e8b955288a9f73c60d2a391282f", - "sha256:5fcaa06bf788e19f913d315d9c99a69e196a40277dc2c23741a1d08c93f4d430", - "sha256:60d6693eef43215b1ccfb1df3f6eae8db30a9ff1e7989fb6b2a6f0b468930ee8", - "sha256:61584f33196575a08785bb56db6b453682c88f009cd9c6f338a10f6737ce419f", - "sha256:62dd42bb0e49423f4dd58836a04fcf09c80237836796025211bbe913f1524993", - "sha256:6abb8c06107dbec97481b2392dafc41aac091a5d162edf6ed7d624fe7da0587a", - "sha256:6af417ca2c7349b101d3fd557ad96b4cd439fdb6ab0d288e3f64a068eea394d0", - "sha256:7069d411cfccf868e812497e0ec4acb7c7bf8d684e93caa6c872f1e6f5d1664d", - "sha256:755d6176b442fba9928a4df787591a6a3d62d4969f05c406cad83d296c5d4e05", - "sha256:7bacc8b77670322132a1b2522c50a1f62991e2f95591977455fd9a398b4e678d", - "sha256:8260e88f1446904ba20b558fa8ce5d0ab9102747238e82343e46d056d7304d7e", - "sha256:84095ab25ba69a8fa3fb4936e14df631b8a71193fe18bd38be7ecbe34d0f5512", - "sha256:8d0a278170d75c88e435a1ce76557af6758bfebc338435b2eba959df2552163e", - "sha256:8d522f390686acb6bab2b917dd9ca06740c5080cd2eaa5aef8827b97e967319d", - "sha256:8da3f8f368fb7e2f052fded06d5672260c50b5472c956a5f1bd7bf474ae504ab", - "sha256:8deda7b8eb15a52db94c2014acdc7bdd14cb59ec4b82ac65d2ad16dc234a109e", - "sha256:9987a439ad33a7712bd5bbd073f09ad10d38640425fa498ecc99d8aa064f8fc4", - "sha256:a4fb69a81ae2ec2b609574ae35420cf5647d227e4d0475c16aa861dd24e840b0", - "sha256:abf366391a02a8335c5c26163b5fe6f514cc1d79e74d8bf3ffab13572282368e", - "sha256:b30df4ff98703649915144be6f0df3b16fd4870ac38a09c56d5d9e54ff2d5f96", - "sha256:b728bdf38ca58f2da1d583e4af4ba7d4cd1a58b31a363a3137a8159395e7ecc7", - "sha256:c068aba9fc5b94dfae8ea1cedcbf3041cd4c64644021362ffb750f79837e881f", - "sha256:c18f6e708d1cf9ff5b1af026e697ac73bea9cb70ee26a2b045b112548579bed2", - "sha256:c28a44b9e0fba49c3857360e7ad1473fc18bc7f6659ca08ed4f4f2b9a52c75fa", - "sha256:c5bf17b32f392df20ab5c3a69d37b26d10efaa018b4f4e5643c7520d8eee7ac7", - "sha256:c804b07622ba50a765ca7fb8145512836ab65956de01307541def869e4a456c9", - "sha256:cadd0113f4db3c6b56868d6a19ca6286f5ccfa7bc08c27982cf92e5ed31b489a", - "sha256:cbf70ba16118db3e4b0da69dcde9d4d4095d383c32a15530564c283fa38a7c52", - "sha256:cf5469dc7dcfa65edf5cc3a6add9f84c5529c6b556729b098e81a09a92e60e51", - "sha256:d0131b14cb545c1a7bd98f4565a3e9bdf25a1bd65c83fc156ee5d8a8499ec4a3", - "sha256:d2a5b35fd1d8d90443e061d0c8669ac7600eec5c14c4a51f619e9e105b136715", - "sha256:d3f13583f378930377e02002b4085a3d025b00402d5a80911726d43a67911cd9", - "sha256:d6aa18a402d1c80193ce97c8729871f17fd3e822037fbd7d9b719864018df746", - "sha256:d89c5bc701861cfab357aa0cd039bc905fe919997b8c312b4b0c358619c38d4d", - "sha256:deec9693b67f6af856a733b8a3e465553ef09e5e8ead792f52c25b699b8f9e6e", - "sha256:e2cfcba719bd494c7413dcf0caafb51772dec168c7c946e094f710d6aa70494e", - "sha256:e564b57e5009fb150cb513804d7e9e9912fee2e48835638f4f47977f88b4a39c", - "sha256:e662bf2f6e90b73cf2095f844e2bc1fda39826472a2aa1959258c3f2a8500a2f", - "sha256:eb1a5b97388f2613f9305d78a3473cdf8d80c7034e554d8199d96dcf80c62ac4", - "sha256:eb3c4cff524b4c1c1dba3a6da905edb1dfd2baf6f55f18a58914bbb2d26b59e1", - "sha256:ec0507ab6523980bed050137007c76883d941b519aca0e26d4c1ec1f297dd646", - "sha256:fc95e46c92a2b6f22e70afe07e34dbc03a4acd07d820204a6938798b16f4014f", - "sha256:fd1ab1373274dea1c6448aee420d7b38af163b5c4732057cd7ee9f5454efc8b1" + "sha256:06157fb3c58f2736a5e47c8fcbe1afc8b5de6fb28b14d25574af9e62150fcaac", + "sha256:067a63fcfda82da6b198fa73079b1ca40b7c9b7994995b6ee38acda728b64d47", + "sha256:0b1794853124e2f663f0ea54efb0340b457f08d40a1cef78edfa086576179c91", + "sha256:0bdff5e0995522706c53078f531fb586f56de9c4c81c243865dd5c66c132c3b5", + "sha256:117ed8b3732528a1e41af3aa6d4e08483c2f0f2e3d3d7dca7cf538b3516d93df", + "sha256:14bc88baa44e1f84164a392827b5defb4fa8e56b93fecac3d15315e7c8e5d8b3", + "sha256:1654ec814b18be1af2c857aa9000de7a601400bd4c9ca24629b18486c2e35463", + "sha256:16bca6678a83657dd48df84b51bd56a6c6bd401853aef6d09dc2506a78484c7b", + "sha256:1a3b91c44efa29e6c8ef8a9a2b583347998e2ba52c5d8280dbd5919c02dfc3b5", + "sha256:1a52a1ffdd824fb1835272e125385c32fd8b17fbdefeedcb4d543cc23b332d74", + "sha256:1ce36ded585f45b1e9bb36d0ae94765c6608b43bd2e7f5f88079f7a85c61a4d3", + "sha256:299f11b44d8d3a588234adbe01112126010bd96d9139c3ba7b3badd9829261c3", + "sha256:2b24ec55fad43e476905eceaf14f41f6478780b870eda5d08b4d6de9a60b65b4", + "sha256:2d374d70fdc36f5863b84e54775452f68639bc862918602d028f89310a034ab0", + "sha256:2d9f0606baaec5dd54cb99667fcf85183a7477f3766fbddbe3f385e7fc253299", + "sha256:2e7ba4c9377e48fb7b20dedbd473cbcbc13e72e1826917c185157a137dac9df2", + "sha256:2f0a6423295a0d282d00e8701fe763eeefba8037e984ad5de44aa349002562ac", + "sha256:327828786da2006085a4d1feb2594de6f6d26f8af48b81eb1ae950c788d97f61", + "sha256:380e6c38ef692b8fd5a0f6d1fa8774d81ebc08cfbd624b1bca62a4d4af2f9931", + "sha256:3b74ff4767d3ef47ffe0cd1d89379dc4d828d4873e5528976ced3b44fe5b0a21", + "sha256:3e844be8d536afa129366d9af76ed7cb8dfefec99f5f1c9e4f8ae542279a6dc3", + "sha256:459e81c2fb920b5f5df744262d1498ec2c8081acdcfe18181da44c50f51312f7", + "sha256:46ddf6e0b975cd680eb83318aa1d321cb2bf8d288d50f1754526230fcf59ba96", + "sha256:482c122b72e3c5ec98f11457aeb436ae4aecca75de19b3d1de7cf88bc40db82f", + "sha256:561c87fea99545ef7d692403c110b2f99dced6dff93056d6e04384ad3bc46243", + "sha256:578d00c9b7fccfa1745a44f4eddfdc99d723d157dad26764538fbdda37209857", + "sha256:58c8e9620eb82a189c6c40cb6b59b4e35b2ee68b1f2afa6597732a2b467d7e8f", + "sha256:5b29beab10211a746f9846baa39275e80034e065460d99eb51e45c9a9495bcca", + "sha256:5d1d42556b063d579cae59e37a38c61f4402b47d70c29f0ef15cee1acaa64488", + "sha256:5f236cb5999ccd23a0ab1bd219cfe0ee3e1c1b65aaf6dd3320e972f7ec3a39da", + "sha256:62a91aefff3d11bf60e5956d340eb507a983a7ec802b19072bb989ce120cd948", + "sha256:64cc6e97f14cf8a275d79c5002281f3040c12e2e4220623b5759ea7f9868d6a5", + "sha256:6f4c9156c4d1eb490fe374fb294deeb7bc7eaccda50e23775b2354b6a6739934", + "sha256:7294e38f9aa2e9f05f765b28ffdc5d81378508ce6dadbe93f6d464a8c9594473", + "sha256:7615058aabad54416ddac99ade09a5510cf77039a3b903e94e8922f25ed203d7", + "sha256:7e48cdb8226644e2fbd0bdb0a0f87906a3db07087f4de77a1b1b1ccfd9e93685", + "sha256:7f63d176a81555984e91f2c84c2a574a61cab7111cc907e176f0f01538e9ff6e", + "sha256:7f6595c852ca544aaeeb32d357e62c9c780eac69dcd34e40cae7b55bc4fb1147", + "sha256:7fac95714b09da9278a0b52e492466f773cfe37651cf467a83a1b659be24bf71", + "sha256:81713b70bea5c1386dc2f32a8f0dab4148a2928c7495c808c541ee0aae614d67", + "sha256:846dd2e1243407133d3195d2d7e4ceefcaa5f5bf7278f0a9bda00967e6326b04", + "sha256:84c063af19ef5130084db70ada40ce63a84f6c1ef4d3dbc34e5e8c4febb20822", + "sha256:881764d610e3269964fc4bb3c19bb6fce55422828e152b885609ec176b41cf11", + "sha256:8994b29c462de9a8fce2d591028b986dbbe1b32f3ad600b2d3e1c482c93abad6", + "sha256:8c79e9d7e3d8a32d4824250a9c6401194fb4c2ad9a0cec8f6a96e09a582c2cc0", + "sha256:8ee427208c675f1b6e344a1f89376a9613fc30b52646a04ac0c1f6587c7e46ec", + "sha256:949681f68e0e3c25377462be4b658500e85ca24323d9619fdc41f68d46a1ffda", + "sha256:9e275792097c9f7e80741c36de3b61917aebecc08a67ae62899b074566ff8556", + "sha256:9fb815155aac6bfa8d86184079652c9715c812d506b22cfa369196ef4e99d1b4", + "sha256:a2a64e62c7a0edd07c1c917b0586655f3362d2c2d37d474db1a509efb96fea1c", + "sha256:a7ac5b4984c468ce4f4a553df281450df0a34aefae02e58d77a0847be8d1e11f", + "sha256:aa46dce75078fceaf7cecac5817422febb4355fbdda440db55206e3bd288cfb8", + "sha256:ae3476e934b9d714aa8000d2e4c01eb2590eee10b9d8cd03e7983ad65dfbfcba", + "sha256:b0341e6d9a0c0e3cdc65857ef518bb05b410dbd70d749a0d33ac0f39e81a4258", + "sha256:b40d1bf6e6f74f7c0a567a9e5e778bbd4699d1d3d2c0fe46f4b717eef9e96b95", + "sha256:b5c4804e4039f487e942c13381e6c27b4b4e66066d94ef1fae3f6ba8b953f383", + "sha256:b5d6a6c9602fd4598fa07e0389e19fe199ae96449008d8304bf5d47cb745462e", + "sha256:b5f1ac7359e17efe0b6e5fec21de34145caef22b260e978336f325d5c84e6938", + "sha256:c0167540094838ee9093ef6cc2c69d0074bbf84a432b4995835e8e5a0d984374", + "sha256:c180ac742a083e109c1a18151f4dd8675f32679985a1c750d2ff806796165b55", + "sha256:c73df5b6e8fabe2ddb74876fb82d9dd44cbace0ca12e8861ce9155ad3c886139", + "sha256:c7e177c619342e407415d4f35dec63d2d134d951e24b5166afcdfd1362828e17", + "sha256:cbad927ea8ed814622305d842c93412cb47bd39a496ed0f96bfd42b922b4a217", + "sha256:cc353841428d56b683a123a813e6a686e07026d6b1c5757970a877195f880c2d", + "sha256:cc7c92c1baa629cb03ecb0c3d12564f172218fb1739f54bf5f3881844daadc6d", + "sha256:cc7d768260f4ba4ea01741c1b5fe3d3a6c70eb91c87f4c8761bbcce5181beafe", + "sha256:d0eea830b591dbc68e030c86a9569826145df485b2b4554874b07fea1275a199", + "sha256:d216e5d9b8749563c7f2c6f7a0831057ec844c68b4c11cb10fc62d4fd373c26d", + "sha256:d401f07261dc5aa36c2e4efc308548f6ae943bfff20fcadb0a07517a26b196d8", + "sha256:d6324274b4e0e2fa1b3eccb25997b1c9ed134ff61d296448ab8269f5ac068c4c", + "sha256:d8a8b74d843c2638f3864a17d97a4acda58e40d3e44b6303b8cc3d3c44ae2d29", + "sha256:d9b6b28a57feb51605d6ae5e61a9044a31742db557a3b851a74c13bc61de5172", + "sha256:de599af166970d6a61accde358ec9ded821234cbbc8c6413acfec06056b8e860", + "sha256:e594b22688d5747b06e957f1ef822060cb5cb35b493066e33ceac0cf882188b7", + "sha256:e5b078134f48552c4d9527db2f7da0b5359abd49393cdf9794017baec7506170", + "sha256:eb6dce402734575e1a8cc0bb1509afca508a400a57ce13d306ea2c663bad1138", + "sha256:f1790a4b1e8e8e028c391175433b9c8122c39b46e1663228158e61e6f915bf06", + "sha256:f5efe0661b9fcd6246f27957f6ae1c0eb29bc60552820f01e970b4996e016004", + "sha256:f9cbfbc5faca235fbdf531b93aa0f9f005ec7d267d9d738761a4d42b744ea159", + "sha256:fbea1751729afe607d84acfd01efd95e3b31db148a181a441984ce9b3d3469da", + "sha256:fca4b4307ebe9c3ec77a084da3a9d1999d164693d16492ca2b64594340999988", + "sha256:ff5c6771c7e3511a06555afa317879b7db8d640137ba55d6ab0d0c50425cab75" ], "markers": "python_version >= '3.9'", - "version": "==1.17.0" + "version": "==1.17.1" } } } diff --git a/requirements-dev.txt b/requirements-dev.txt index 28bc5eb8..e168479e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,6 @@ -i https://pypi.org/simple aiohappyeyeballs==2.4.3; python_version >= '3.8' -aiohttp==3.10.10 +aiohttp==3.10.10; python_version >= '3.8' aiosignal==1.3.1; python_version >= '3.7' astroid==3.3.5; python_full_version >= '3.9.0' asttokens==2.4.1 @@ -12,7 +12,7 @@ click==8.1.7; python_version >= '3.7' colorama==0.4.6; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6' coverage[toml]==7.6.4; python_version >= '3.9' coveralls==4.0.1; python_version < '3.13' and python_version >= '3.8' -dill==0.3.9; python_version >= '3.11' +dill==0.3.9; python_version >= '3.8' docopt==0.6.2 executing==2.1.0; python_version >= '3.8' flake8==7.1.1; python_full_version >= '3.8.1' @@ -25,7 +25,7 @@ mccabe==0.7.0; python_version >= '3.6' multidict==6.1.0; python_version >= '3.8' mypy==1.13.0; python_version >= '3.8' mypy-extensions==1.0.0; python_version >= '3.5' -packaging==24.1; python_version >= '3.8' +packaging==24.2; python_version >= '3.8' pathspec==0.12.1; python_version >= '3.8' platformdirs==4.3.6; python_version >= '3.8' pluggy==1.5.0; python_version >= '3.8' @@ -35,16 +35,16 @@ pyflakes==3.2.0; python_version >= '3.8' pygments==2.18.0; python_version >= '3.8' pylint==3.3.1; python_full_version >= '3.9.0' pytest==8.3.3; python_version >= '3.8' -pytest-cov==5.0.0; python_version >= '3.8' +pytest-cov==6.0.0; python_version >= '3.9' requests==2.32.3; python_version >= '3.8' requests-mock==1.12.1; python_version >= '3.5' -six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2' tomlkit==0.13.2; python_version >= '3.8' types-pyyaml==6.0.12.20240917; python_version >= '3.8' types-requests==2.32.0.20241016; python_version >= '3.8' -typing-extensions==4.12.2; python_version < '3.13' +typing-extensions==4.12.2; python_version >= '3.8' urllib3==2.2.3; python_version >= '3.8' -yarl==1.17.0; python_version >= '3.9' +yarl==1.17.1; python_version >= '3.9' annotated-types==0.7.0; python_version >= '3.8' cerberus==1.3.5 defusedxml==0.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' diff --git a/requirements.txt b/requirements.txt index 34035419..20fba54c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,6 @@ pydantic-core==2.23.4; python_version >= '3.8' python-dotenv==1.0.1; python_version >= '3.8' pyyaml==6.0.2; python_version >= '3.8' requests==2.32.3; python_version >= '3.8' -typing-extensions==4.12.2; python_version < '3.13' +typing-extensions==4.12.2; python_version >= '3.8' urllib3==2.2.3; python_version >= '3.8' xmltodict==0.14.2; python_version >= '3.6'