diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 240a86d..0d0e7db 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,7 +19,7 @@ jobs: run: poetry install --only main,dev --no-interaction --no-ansi --no-cache - name: Lint - run: poetry run ruff . + run: poetry run ruff --unsafe-fixes --preview --fix . type_checking: name: Static type checking diff --git a/poetry.lock b/poetry.lock index 4c09fd1..95aa98b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -279,19 +279,18 @@ wmi = ["wmi (>=1.5.1,<2.0.0)"] [[package]] name = "eventlet" -version = "0.34.2" +version = "0.34.3" description = "Highly concurrent networking library" optional = false python-versions = ">=3.7" files = [ - {file = "eventlet-0.34.2-py3-none-any.whl", hash = "sha256:eca0114398d3133f94d16590d205bbb010668a3bc7453fddadb51d63e8c1bac2"}, - {file = "eventlet-0.34.2.tar.gz", hash = "sha256:2115c7c6742e6893bf1347f82915572f8895c911cb5abaad4d3596a7daa847cc"}, + {file = "eventlet-0.34.3-py3-none-any.whl", hash = "sha256:3093f2822ce2f40792bf9aa7eb8920fe3b90db785d3bea7640c50412969dcfd7"}, + {file = "eventlet-0.34.3.tar.gz", hash = "sha256:ed2d28a64414a001894b3baf5b650f2c9596b00d57f57d4d7a38f9d3d0c252e8"}, ] [package.dependencies] dnspython = ">=1.15.0" greenlet = ">=1.0" -six = ">=1.10.0" [package.extras] dev = ["black", "build", "commitizen", "isort", "pip-tools", "pre-commit", "twine"] @@ -449,13 +448,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "kombu" -version = "5.3.4" +version = "5.3.5" description = "Messaging library for Python." optional = false python-versions = ">=3.8" files = [ - {file = "kombu-5.3.4-py3-none-any.whl", hash = "sha256:63bb093fc9bb80cfb3a0972336a5cec1fa7ac5f9ef7e8237c6bf8dda9469313e"}, - {file = "kombu-5.3.4.tar.gz", hash = "sha256:0bb2e278644d11dea6272c17974a3dbb9688a949f3bb60aeb5b791329c44fadc"}, + {file = "kombu-5.3.5-py3-none-any.whl", hash = "sha256:0eac1bbb464afe6fb0924b21bf79460416d25d8abc52546d4f16cad94f789488"}, + {file = "kombu-5.3.5.tar.gz", hash = "sha256:30e470f1a6b49c70dc6f6d13c3e4cc4e178aa6c469ceb6bcd55645385fc84b93"}, ] [package.dependencies] @@ -1254,28 +1253,28 @@ middleware = ["webob"] [[package]] name = "ruff" -version = "0.0.289" -description = "An extremely fast Python linter, written in Rust." +version = "0.1.13" +description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.0.289-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:c9a89d748e90c840bac9c37afe90cf13a5bfd460ca02ea93dad9d7bee3af03b4"}, - {file = "ruff-0.0.289-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7f7396c6ea01ba332a6ad9d47642bac25d16bd2076aaa595b001f58b2f32ff05"}, - {file = "ruff-0.0.289-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7180de86c8ecd39624dec1699136f941c07e723201b4ce979bec9e7c67b40ad2"}, - {file = "ruff-0.0.289-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73f37c65508203dd01a539926375a10243769c20d4fcab3fa6359cd3fbfc54b7"}, - {file = "ruff-0.0.289-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c14abcd7563b5c80be2dd809eeab20e4aa716bf849860b60a22d87ddf19eb88"}, - {file = "ruff-0.0.289-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:91b6d63b6b46d4707916472c91baa87aa0592e73f62a80ff55efdf6c0668cfd6"}, - {file = "ruff-0.0.289-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6479b8c4be3c36046c6c92054762b276fa0fddb03f6b9a310fbbf4c4951267fd"}, - {file = "ruff-0.0.289-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5424318c254bcb091cb67e140ec9b9f7122074e100b06236f252923fb41e767"}, - {file = "ruff-0.0.289-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4daa90865796aedcedf0d8897fdd4cd09bf0ddd3504529a4ccf211edcaff3c7d"}, - {file = "ruff-0.0.289-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8057e8ab0016c13b9419bad119e854f881e687bd96bc5e2d52c8baac0f278a44"}, - {file = "ruff-0.0.289-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7eebfab2e6a6991908ff1bf82f2dc1e5095fc7e316848e62124526837b445f4d"}, - {file = "ruff-0.0.289-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ebc7af550018001a7fb39ca22cdce20e1a0de4388ea4a007eb5c822f6188c297"}, - {file = "ruff-0.0.289-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6e4e6eccb753efe760ba354fc8e9f783f6bba71aa9f592756f5bd0d78db898ed"}, - {file = "ruff-0.0.289-py3-none-win32.whl", hash = "sha256:bbb3044f931c09cf17dbe5b339896eece0d6ac10c9a86e172540fcdb1974f2b7"}, - {file = "ruff-0.0.289-py3-none-win_amd64.whl", hash = "sha256:6d043c5456b792be2615a52f16056c3cf6c40506ce1f2d6f9d3083cfcb9eeab6"}, - {file = "ruff-0.0.289-py3-none-win_arm64.whl", hash = "sha256:04a720bcca5e987426bb14ad8b9c6f55e259ea774da1cbeafe71569744cfd20a"}, - {file = "ruff-0.0.289.tar.gz", hash = "sha256:2513f853b0fc42f0339b7ab0d2751b63ce7a50a0032d2689b54b2931b3b866d7"}, + {file = "ruff-0.1.13-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e3fd36e0d48aeac672aa850045e784673449ce619afc12823ea7868fcc41d8ba"}, + {file = "ruff-0.1.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9fb6b3b86450d4ec6a6732f9f60c4406061b6851c4b29f944f8c9d91c3611c7a"}, + {file = "ruff-0.1.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b13ba5d7156daaf3fd08b6b993360a96060500aca7e307d95ecbc5bb47a69296"}, + {file = "ruff-0.1.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ebb40442f7b531e136d334ef0851412410061e65d61ca8ce90d894a094feb22"}, + {file = "ruff-0.1.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226b517f42d59a543d6383cfe03cccf0091e3e0ed1b856c6824be03d2a75d3b6"}, + {file = "ruff-0.1.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0312ba1061e9b8c724e9a702d3c8621e3c6e6c2c9bd862550ab2951ac75c16"}, + {file = "ruff-0.1.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2f59bcf5217c661254bd6bc42d65a6fd1a8b80c48763cb5c2293295babd945dd"}, + {file = "ruff-0.1.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6894b00495e00c27b6ba61af1fc666f17de6140345e5ef27dd6e08fb987259d"}, + {file = "ruff-0.1.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1600942485c6e66119da294c6294856b5c86fd6df591ce293e4a4cc8e72989"}, + {file = "ruff-0.1.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ee3febce7863e231a467f90e681d3d89210b900d49ce88723ce052c8761be8c7"}, + {file = "ruff-0.1.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dcaab50e278ff497ee4d1fe69b29ca0a9a47cd954bb17963628fa417933c6eb1"}, + {file = "ruff-0.1.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f57de973de4edef3ad3044d6a50c02ad9fc2dff0d88587f25f1a48e3f72edf5e"}, + {file = "ruff-0.1.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a36fa90eb12208272a858475ec43ac811ac37e91ef868759770b71bdabe27b6"}, + {file = "ruff-0.1.13-py3-none-win32.whl", hash = "sha256:a623349a505ff768dad6bd57087e2461be8db58305ebd5577bd0e98631f9ae69"}, + {file = "ruff-0.1.13-py3-none-win_amd64.whl", hash = "sha256:f988746e3c3982bea7f824c8fa318ce7f538c4dfefec99cd09c8770bd33e6539"}, + {file = "ruff-0.1.13-py3-none-win_arm64.whl", hash = "sha256:6bbbc3042075871ec17f28864808540a26f0f79a4478c357d3e3d2284e832998"}, + {file = "ruff-0.1.13.tar.gz", hash = "sha256:e261f1baed6291f434ffb1d5c6bd8051d1c2a26958072d38dfbec39b3dda7352"}, ] [[package]] @@ -1577,4 +1576,4 @@ test = ["gevent (>=20.6.2)"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "36922dba6eca754b824c9baaedf371f9b2cc6de4512f7fd66c56e6f0dfa6a56a" +content-hash = "93b42f4aaee458977b968ce19d163074c292b67937f5f295bfb3a077a19ffeff" diff --git a/pyproject.toml b/pyproject.toml index 8dc4ae0..13e9c70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ voluptuous = "^0.14.1" [tool.poetry.group.dev.dependencies] black = "^23.3.0" mypy = "^1.4.1" -ruff = "^0.0.289" +ruff = "^0.1.13" types-pytz = ">=2023.3.0.0" [build-system] @@ -28,70 +28,83 @@ build-backend = "poetry.core.masonry.api" [tool.ruff] select = [ - "A", - "ANN", - "B", - "BLE", - "C4", - "C90", - "COM", - "D", - "DJ", - "DTZ", - "E", - "ERA", - "EXE", - "F", - "G", - "I", - "ICN", - "INP", - "INT", - "ISC", - "N", - "NPY", - "PD", - "PGH", - "PIE", - "PL", - "PT", - "PTH", - "PYI", - "Q", - "RET", - "RSE", - "RUF", - "S", - "SIM", - "SLF", - "T10", - "T20", - "TCH", - "TID", - "TRY", - "UP", - "W", - "YTT", + "ALL", ] ignore = [ + "A001", + "A003", + "ANN001", "ANN002", "ANN003", "ANN101", "ANN102", + "ANN201", + "ANN202", + "ANN204", + "ANN206", "ANN401", + "ARG001", + "ARG002", + "B023", + "BLE001", "COM812", + "CPY001", "D100", "D101", + "D102", "D103", "D104", "D105", "D106", "D107", + "D205", + "DTZ001", + "DTZ004", + "DTZ006", + "DTZ012", + "E203", + "E741", "EXE002", + "F403", + "FBT001", + "FBT002", + "FBT003", + "FIX002", + "INP001", + "ISC001", + "N802", "N806", + "N818", + "PIE804", + "PLC1901", + "PLR2004", + "PLR6301", + "PLW0603", + "PLW2901", + "PT011", + "PT012", + "PTH100", + "PTH102", + "PTH110", + "PTH111", + "PTH112", + "PTH118", + "PTH120", + "PTH123", + "RUF012", "S101", + "S105", + "S110", + "S311", + "S602", + "S603", + "S607", "SIM108", + "SLF001", + "TD002", + "TD003", "TRY003", + "TRY301", ] # Exclude a variety of commonly ignored directories. @@ -126,9 +139,6 @@ max-complexity = 14 [tool.ruff.pydocstyle] convention = "google" -[tool.ruff.flake8-quotes] -inline-quotes = "single" - [tool.ruff.pep8-naming] classmethod-decorators = [ "classmethod", @@ -148,11 +158,6 @@ known-third-party = ["elasticsearch"] order-by-type = false required-imports = ["from __future__ import annotations"] -[tool.black] -line-length = 100 -target-version = ['py311'] -skip-string-normalization = true - [tool.mypy] python_version = "3.11" check_untyped_defs = true diff --git a/stepic_plugins/exceptions.py b/stepic_plugins/exceptions.py index 66ebe5a..5433bfe 100644 --- a/stepic_plugins/exceptions.py +++ b/stepic_plugins/exceptions.py @@ -9,9 +9,9 @@ ) __all__ = [ - 'PluginError', - 'FormatError', - 'QuizSetUpError', - 'UnknownPluginError', - 'PluginTimeoutError', + "PluginError", + "FormatError", + "QuizSetUpError", + "UnknownPluginError", + "PluginTimeoutError", ] diff --git a/stepik_plugins_client/exceptions.py b/stepik_plugins_client/exceptions.py index 9d6985b..dc28b32 100644 --- a/stepik_plugins_client/exceptions.py +++ b/stepik_plugins_client/exceptions.py @@ -25,8 +25,8 @@ def __init__( message = truncate_data(message, max_length=USER_OUTPUT_MAX_LENGTH) params = truncate_data(params, max_length=USER_OUTPUT_MAX_LENGTH) super().__init__(message, code, params) - self.message = message or '' - self.code = code or '' + self.message = message or "" + self.code = code or "" self.params = params or {} diff --git a/stepik_plugins_client/plugins.py b/stepik_plugins_client/plugins.py index 6403e4a..91a24f3 100644 --- a/stepik_plugins_client/plugins.py +++ b/stepik_plugins_client/plugins.py @@ -20,15 +20,15 @@ class WrappedEndpointMetaclass(type): - def __new__( # noqa: PYI034 + def __new__( cls, name: str, bases: tuple[type, ...], dct: dict[str, Any] ) -> WrappedEndpointMetaclass: """Create API class.""" instance = super().__new__(cls, name, bases, dct) - for name in dir(instance): - method = getattr(instance, name) - if not name.startswith('_') and callable(method): - setattr(instance, name, cls.method_wrapper(method)) + for _name in dir(instance): + method = getattr(instance, _name) + if not _name.startswith("_") and callable(method): + setattr(instance, _name, cls.method_wrapper(method)) return instance @staticmethod @@ -40,10 +40,11 @@ def wrapper(self: Any, *args, **kwargs) -> Any: try: return method(self, *args, **kwargs) except MessagingTimeout as exception: - raise PluginTimeoutError( - 'A timeout occurred while calling Problem API. ' - 'It looks like calculations take a long time.' - ) from exception + msg = ( + "A timeout occurred while calling Problem API. " + "It looks like calculations take a long time." + ) + raise PluginTimeoutError(msg) from exception return wrapper @@ -58,26 +59,27 @@ class ReportedUtilsAPI(rpcapi.UtilsAPI, metaclass=WrappedEndpointMetaclass): @overload def _get_rpcapi_client( - name: Literal['quiz'], transport_url: str, topic: str | None = None, timeout: int = 5 * 60 + name: Literal["quiz"], transport_url: str, topic: str | None = None, timeout: int = 5 * 60 ) -> ReportedQuizAPI: ... @overload def _get_rpcapi_client( - name: Literal['utils'], transport_url: str, topic: str | None = None, timeout: int = 5 * 60 + name: Literal["utils"], transport_url: str, topic: str | None = None, timeout: int = 5 * 60 ) -> ReportedUtilsAPI: ... def _get_rpcapi_client(name, transport_url, topic=None, timeout=5 * 60): api_class_map = { - 'quiz': ReportedQuizAPI, - 'utils': ReportedUtilsAPI, + "quiz": ReportedQuizAPI, + "utils": ReportedUtilsAPI, } rpc_api_class = api_class_map.get(name) if not rpc_api_class: - raise ValueError('Incorrect RPC API client name') + msg = "Incorrect RPC API client name" + raise ValueError(msg) key = (name, transport_url, topic) if name not in _RPC_API_CLIENTS: rpcapi.set_default_response_timeout(timeout) @@ -89,11 +91,11 @@ def quiz_rpcapi( transport_url: str, topic: str | None = None, timeout: int = 5 * 60 * SECOND ) -> ReportedQuizAPI: """Get client for the quizzes RPC API.""" - return _get_rpcapi_client('quiz', transport_url, topic, timeout) + return _get_rpcapi_client("quiz", transport_url, topic, timeout) def utils_rpcapi( transport_url: str, topic: str | None = None, timeout: int = 5 * 60 * SECOND ) -> ReportedUtilsAPI: """Get client for the utils RPC API.""" - return _get_rpcapi_client('utils', transport_url, topic, timeout) + return _get_rpcapi_client("utils", transport_url, topic, timeout) diff --git a/stepik_plugins_client/rpcapi.py b/stepik_plugins_client/rpcapi.py index bde6a9e..a1b27bc 100644 --- a/stepik_plugins_client/rpcapi.py +++ b/stepik_plugins_client/rpcapi.py @@ -12,9 +12,9 @@ if TYPE_CHECKING: from typing import Any -messaging.set_transport_defaults(control_exchange='stepic.rpc') +messaging.set_transport_defaults(control_exchange="stepic.rpc") -ALLOWED_EXMODS = ['stepic_plugins.exceptions'] +ALLOWED_EXMODS = ["stepic_plugins.exceptions"] def set_default_response_timeout(timeout: int) -> None: @@ -26,7 +26,7 @@ def set_default_response_timeout(timeout: int) -> None: """ cfg.CONF.register_opts(_client_opts) - cfg.CONF.set_default('rpc_response_timeout', timeout) + cfg.CONF.set_default("rpc_response_timeout", timeout) class BaseAPI: @@ -65,13 +65,13 @@ class CodeRunResult(TypedDict): class QuizAPI(BaseAPI): """Client side of the quizzes RPC API.""" - default_topic = 'plugins' - namespace = 'quiz' - version = '0.2' + default_topic = "plugins" + namespace = "quiz" + version = "0.2" def ping(self, msg: str) -> str: """Ping RPC server.""" - return self.client.call({}, 'ping', msg=msg) + return self.client.call({}, "ping", msg=msg) def call( self, @@ -87,7 +87,7 @@ def call( :param kwargs: keyword arguments to pass on to the method (a dict) """ - return self.client.call(quiz_ctxt, 'call', name=name, args=args, kwargs=kwargs) + return self.client.call(quiz_ctxt, "call", name=name, args=args, kwargs=kwargs) def validate_source(self, quiz_ctxt: dict[str, Any]) -> str: """Validate source from the quiz context. @@ -97,15 +97,15 @@ def validate_source(self, quiz_ctxt: dict[str, Any]) -> str: :raises FormatError: if source is not valid """ - return self.client.call(quiz_ctxt, 'validate_source') + return self.client.call(quiz_ctxt, "validate_source") def async_init(self, quiz_ctxt: dict[str, Any]) -> dict[str, Any] | None: """Async init.""" - return self.client.call(quiz_ctxt, 'async_init') + return self.client.call(quiz_ctxt, "async_init") def generate(self, quiz_ctxt: dict[str, Any]) -> tuple[dict[str, Any], Any] | None: """Generate attempt.""" - return self.client.call(quiz_ctxt, 'generate') + return self.client.call(quiz_ctxt, "generate") def clean_reply( self, @@ -114,22 +114,22 @@ def clean_reply( dataset: dict[str, Any] | None = None, ) -> dict[str, Any]: """Clean user reply.""" - return self.client.call(quiz_ctxt, 'clean_reply', reply=reply, dataset=dataset) + return self.client.call(quiz_ctxt, "clean_reply", reply=reply, dataset=dataset) def check( self, quiz_ctxt: dict[str, Any], reply: dict[str, Any], clue: dict[str, Any] | None = None ) -> tuple[float | bool, dict[str, Any] | str]: """Check user reply.""" - return self.client.call(quiz_ctxt, 'check', reply=reply, clue=clue) + return self.client.call(quiz_ctxt, "check", reply=reply, clue=clue) def cleanup(self, quiz_ctxt: dict[str, Any], clue: dict[str, Any] | None = None) -> str: """Cleanup quiz context.""" - return self.client.call(quiz_ctxt, 'cleanup', clue=clue) + return self.client.call(quiz_ctxt, "cleanup", clue=clue) @cached_property def list_computationally_hard_quizzes(self) -> str: """Return list computationally hard quizzes.""" - return self.client.call({}, 'list_computationally_hard_quizzes') + return self.client.call({}, "list_computationally_hard_quizzes") def run_user_code( self, @@ -139,22 +139,22 @@ def run_user_code( stdin: str | None = None, ) -> CodeRunResult: """Run user code.""" - return self.client.call(context, 'run_user_code', args=[code, language, stdin]) + return self.client.call(context, "run_user_code", args=[code, language, stdin]) class UtilsAPI(BaseAPI): """Client side of the utils RPC API.""" - default_topic = 'plugins' - namespace = 'utils' - version = '0.1' + default_topic = "plugins" + namespace = "utils" + version = "0.1" - service_request_methods = ('ping', 'preview_formula') + service_request_methods = ("ping", "preview_formula") def ping(self, msg: str) -> str: """Ping RPC server.""" - return self.client.call({}, 'ping', msg=msg) + return self.client.call({}, "ping", msg=msg) def preview_formula(self, formula: str) -> str: """Convert the given formula to LaTeX representation.""" - return self.client.call({}, 'preview_formula', formula=formula) + return self.client.call({}, "preview_formula", formula=formula) diff --git a/stepik_plugins_client/schema.py b/stepik_plugins_client/schema.py index ea0d376..98da216 100644 --- a/stepik_plugins_client/schema.py +++ b/stepik_plugins_client/schema.py @@ -30,8 +30,9 @@ def is_int_as_string(obj: Any) -> bool: def ensure_type(obj: Any, type_: type) -> None: # if integer passed as string (EDY-1668) - if not isinstance(obj, type_) and not (type_ == int and is_int_as_string(obj)): - raise FormatError(f'Expected {type_.__name__}, got {obj}') + if not isinstance(obj, type_) and not (type_ == int and is_int_as_string(obj)): # noqa: E721 + msg = f"Expected {type_.__name__}, got {obj}" + raise FormatError(msg) def build(scheme: Any, obj: Any) -> Any: @@ -41,7 +42,7 @@ def build(scheme: Any, obj: Any) -> Any: if _is_primitive(scheme): ensure_type(obj, scheme) # if integer passed as string (EDY-1668) - if scheme == int and is_int_as_string(obj): + if scheme == int and is_int_as_string(obj): # noqa: E721 return int(obj) return obj @@ -49,7 +50,7 @@ def build(scheme: Any, obj: Any) -> Any: ensure_type(obj, list) return [build(scheme[0], x) for x in obj] - assert isinstance(scheme, dict), 'Scheme should be dict' + assert isinstance(scheme, dict), "Scheme should be dict" ensure_type(obj, dict) return ParsedJSON(scheme, obj) @@ -60,7 +61,8 @@ def __init__(self, scheme: dict[str, Any], obj: Any) -> None: self._original = obj for key, sub_scheme in scheme.items(): if key not in obj: - raise FormatError(f'Expected key {key} in {obj}') + msg = f"Expected key {key} in {obj}" + raise FormatError(msg) setattr(self, key, build(sub_scheme, obj[key])) def __repr__(self) -> str: @@ -68,7 +70,7 @@ def __repr__(self) -> str: def _is_primitive(obj: Any) -> bool: - return obj in [str, bytes, int, float, bool] + return obj in {str, bytes, int, float, bool} class RPCSerializer(messaging.NoOpSerializer): @@ -81,43 +83,43 @@ def serialize_entity(self, ctxt: dict[str, Any], entity: Any) -> Any: return {k: self.serialize_entity(ctxt, v) for k, v in entity.items()} if isinstance(entity, bytes): - return {'_serialized.bytes': base64.b64encode(entity).decode()} + return {"_serialized.bytes": base64.b64encode(entity).decode()} if isinstance(entity, Decimal): - return {'_serialized.decimal': str(entity)} + return {"_serialized.decimal": str(entity)} if isinstance(entity, datetime.datetime): # doesn't preserve timezone - return {'_serialized.datetime': entity.timestamp()} + return {"_serialized.datetime": entity.timestamp()} if isinstance(entity, datetime.date): - return {'_serialized.date': int(time.mktime(entity.timetuple()))} + return {"_serialized.date": int(time.mktime(entity.timetuple()))} if isinstance(entity, datetime.timedelta): - return {'_serialized.timedelta': entity.total_seconds()} + return {"_serialized.timedelta": entity.total_seconds()} if isinstance(entity, ParsedJSON): - return self.serialize_entity(ctxt, entity._original) # noqa: SLF001 + return self.serialize_entity(ctxt, entity._original) return entity def deserialize_entity(self, ctxt: dict[str, Any], entity: Any) -> Any: """Deserialize entity.""" if isinstance(entity, dict): - if '_serialized.bytes' in entity: - return base64.b64decode(entity['_serialized.bytes']) + if "_serialized.bytes" in entity: + return base64.b64decode(entity["_serialized.bytes"]) - if '_serialized.decimal' in entity: - return Decimal(entity['_serialized.decimal']) + if "_serialized.decimal" in entity: + return Decimal(entity["_serialized.decimal"]) - if '_serialized.datetime' in entity: - return datetime.datetime.fromtimestamp(entity['_serialized.datetime'], tz=UTC) + if "_serialized.datetime" in entity: + return datetime.datetime.fromtimestamp(entity["_serialized.datetime"], tz=UTC) - if '_serialized.date' in entity: - return datetime.datetime.fromtimestamp(entity['_serialized.date'], tz=UTC).date() + if "_serialized.date" in entity: + return datetime.datetime.fromtimestamp(entity["_serialized.date"], tz=UTC).date() - if '_serialized.timedelta' in entity: - return datetime.timedelta(seconds=entity['_serialized.timedelta']) + if "_serialized.timedelta" in entity: + return datetime.timedelta(seconds=entity["_serialized.timedelta"]) return {k: self.deserialize_entity(ctxt, v) for k, v in entity.items()} diff --git a/stepik_plugins_client/utils.py b/stepik_plugins_client/utils.py index 462cc2b..60b4f7a 100644 --- a/stepik_plugins_client/utils.py +++ b/stepik_plugins_client/utils.py @@ -15,6 +15,6 @@ def truncate_data(value: Any, max_length: int = 100) -> Any: return type(value)((k, truncate_data(v, max_length=max_length)) for k, v in value.items()) if isinstance(value, str): - return value if len(value) <= max_length else value[:max_length] + '...truncated...' + return value if len(value) <= max_length else value[:max_length] + "...truncated..." return value