From 33a5445ee8b94c103f237369e30819af03d9fa08 Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Sun, 24 Sep 2023 18:15:03 +0300 Subject: [PATCH] added `S`, `RET`, `RUF` ruff checks Signed-off-by: Alexander Piskun --- benchmarks/aa_overhead_common.py | 2 +- examples/as_app/talk_bot/src/main.py | 13 +++++------- examples/as_app/talk_bot_multi/src/main.py | 8 ++++---- nc_py_api/_misc.py | 8 ++++---- nc_py_api/files/__init__.py | 4 ++-- nc_py_api/files/files.py | 7 ++----- nc_py_api/talk_bot.py | 2 +- nc_py_api/weather_status.py | 4 ++-- pyproject.toml | 8 ++++---- tests/actual_tests/files_test.py | 2 +- tests/actual_tests/talk_test.py | 24 ++++++++++++++-------- 11 files changed, 42 insertions(+), 40 deletions(-) diff --git a/benchmarks/aa_overhead_common.py b/benchmarks/aa_overhead_common.py index b1bf1162..7331b76a 100644 --- a/benchmarks/aa_overhead_common.py +++ b/benchmarks/aa_overhead_common.py @@ -69,6 +69,6 @@ def measure_overhead(measure, title: str): def os_id(): if sys.platform.lower() == "darwin": return "macOS" - elif sys.platform.lower() == "win32": + if sys.platform.lower() == "win32": return "Windows" return "Linux" diff --git a/examples/as_app/talk_bot/src/main.py b/examples/as_app/talk_bot/src/main.py index 606ef34c..a2cc7af8 100644 --- a/examples/as_app/talk_bot/src/main.py +++ b/examples/as_app/talk_bot/src/main.py @@ -20,7 +20,7 @@ def convert_currency(amount, from_currency, to_currency): base_url = "https://api.exchangerate-api.com/v4/latest/" # Fetch latest exchange rates - response = requests.get(base_url + from_currency) + response = requests.get(base_url + from_currency, timeout=60) data = response.json() if "rates" in data: @@ -30,12 +30,9 @@ def convert_currency(amount, from_currency, to_currency): if from_currency in rates and to_currency in rates: conversion_rate = rates[to_currency] / rates[from_currency] - converted_amount = amount * conversion_rate - return converted_amount - else: - raise ValueError("Invalid currency!") - else: - raise ValueError("Unable to fetch exchange rates!") + return amount * conversion_rate + raise ValueError("Invalid currency!") + raise ValueError("Unable to fetch exchange rates!") def currency_talk_bot_process_request(message: talk_bot.TalkBotMessage): @@ -55,7 +52,7 @@ def currency_talk_bot_process_request(message: talk_bot.TalkBotMessage): CURRENCY_BOT.send_message(f"{r.group(2)} {r.group(3)} is equal to {converted_amount} {r.group(4)}", message) except Exception as e: # In production, it is better to write to log, than in the chat ;) - CURRENCY_BOT.send_message(f"Exception: {str(e)}", message) + CURRENCY_BOT.send_message(f"Exception: {e}", message) @APP.post("/currency_talk_bot") diff --git a/examples/as_app/talk_bot_multi/src/main.py b/examples/as_app/talk_bot_multi/src/main.py index 5f6ac372..c1f423e8 100644 --- a/examples/as_app/talk_bot_multi/src/main.py +++ b/examples/as_app/talk_bot_multi/src/main.py @@ -20,10 +20,10 @@ @APP.post("/bot1") async def bot1(message: Annotated[talk_bot.TalkBotMessage, Depends(talk_bot_app)]): if message.object_name != "message": - return + return None r = re.search(r"@bot\sone.*", message.object_content["message"], re.IGNORECASE) if r is None and re.search(r"@bots!", message.object_content["message"], re.IGNORECASE) is None: - return + return None BOT1.send_message("I am here, my Lord!", message) return requests.Response() @@ -31,10 +31,10 @@ async def bot1(message: Annotated[talk_bot.TalkBotMessage, Depends(talk_bot_app) @APP.post("/bot2") async def bot2(message: Annotated[talk_bot.TalkBotMessage, Depends(talk_bot_app)]): if message.object_name != "message": - return + return None r = re.search(r"@bot\stwo.*", message.object_content["message"], re.IGNORECASE) if r is None and re.search(r"@bots!", message.object_content["message"], re.IGNORECASE) is None: - return + return None BOT2.send_message("I am here, my Lord!", message) return requests.Response() diff --git a/nc_py_api/_misc.py b/nc_py_api/_misc.py index 71a81244..66dfc6f1 100644 --- a/nc_py_api/_misc.py +++ b/nc_py_api/_misc.py @@ -2,10 +2,10 @@ For internal use, prototypes can change between versions. """ +import secrets from base64 import b64decode from datetime import datetime, timezone -from random import choice -from string import ascii_lowercase, ascii_uppercase, digits +from string import ascii_letters, digits from typing import Callable, Union from ._exceptions import NextcloudMissingCapabilities @@ -65,8 +65,8 @@ def check_capabilities(capabilities: Union[str, list[str]], srv_capabilities: di def random_string(size: int) -> str: """Generates a random ASCII string of the given size.""" - letters = ascii_lowercase + ascii_uppercase + digits - return "".join(choice(letters) for _ in range(size)) + char_string = ascii_letters + digits + return "".join(secrets.choice(char_string) for _ in range(size)) def nc_iso_time_to_datetime(iso8601_time: str) -> datetime: diff --git a/nc_py_api/files/__init__.py b/nc_py_api/files/__init__.py index cce549f6..905cb68f 100644 --- a/nc_py_api/files/__init__.py +++ b/nc_py_api/files/__init__.py @@ -129,11 +129,11 @@ def __str__(self): if self.info.is_version: return ( f"File version: `{self.name}` for FileID={self.file_id}" - f" last modified at {str(self.info.last_modified)} with {self.info.content_length} bytes size." + f" last modified at {self.info.last_modified} with {self.info.content_length} bytes size." ) return ( f"{'Dir' if self.is_dir else 'File'}: `{self.name}` with id={self.file_id}" - f" last modified at {str(self.info.last_modified)} and {self.info.permissions} permissions." + f" last modified at {self.info.last_modified} and {self.info.permissions} permissions." ) def __eq__(self, other): diff --git a/nc_py_api/files/files.py b/nc_py_api/files/files.py index 66844b9f..ee3dc6b8 100644 --- a/nc_py_api/files/files.py +++ b/nc_py_api/files/files.py @@ -6,8 +6,6 @@ from io import BytesIO from json import dumps, loads from pathlib import Path -from random import choice -from string import ascii_lowercase, digits from typing import Optional, Union from urllib.parse import unquote from xml.etree import ElementTree @@ -16,7 +14,7 @@ from httpx import Response from .._exceptions import NextcloudException, NextcloudExceptionNotFound, check_error -from .._misc import clear_from_params_empty, require_capabilities +from .._misc import clear_from_params_empty, random_string, require_capabilities from .._session import NcSessionBasic from . import FsNode, SystemTag from .sharing import _FilesSharingAPI @@ -694,8 +692,7 @@ def __download2stream(self, path: str, fp, **kwargs) -> None: fp.write(data_chunk) def __upload_stream(self, path: str, fp, **kwargs) -> FsNode: - _rnd_folder = "".join(choice(digits + ascii_lowercase) for _ in range(64)) - _dav_path = self._dav_get_obj_path(self._session.user, _rnd_folder, root_path="/uploads") + _dav_path = self._dav_get_obj_path(self._session.user, random_string(64), root_path="/uploads") response = self._session.dav("MKCOL", _dav_path) check_error(response.status_code) try: diff --git a/nc_py_api/talk_bot.py b/nc_py_api/talk_bot.py index da63e8e6..9c274b66 100644 --- a/nc_py_api/talk_bot.py +++ b/nc_py_api/talk_bot.py @@ -202,7 +202,7 @@ def _sign_send_request(self, method: str, url_suffix: str, data: dict, data_to_s def get_bot_secret(callback_url: str) -> typing.Union[bytes, None]: """Returns the bot's secret from an environment variable or from the application's configuration on the server.""" - sha_1 = hashlib.sha1() + sha_1 = hashlib.sha1(usedforsecurity=False) string_to_hash = os.environ["APP_ID"] + "_" + callback_url sha_1.update(string_to_hash.encode("UTF-8")) secret_key = sha_1.hexdigest() diff --git a/nc_py_api/weather_status.py b/nc_py_api/weather_status.py index b5ee27dd..f78a5026 100644 --- a/nc_py_api/weather_status.py +++ b/nc_py_api/weather_status.py @@ -67,8 +67,8 @@ def set_location( ) -> bool: """Sets the user's location on the Nextcloud server. - :param latitude: north–south position of a point on the surface of the Earth. - :param longitude: east–west position of a point on the surface of the Earth. + :param latitude: north-south position of a point on the surface of the Earth. + :param longitude: east-west position of a point on the surface of the Earth. :param address: city, index(*optional*) and country, e.g. "Paris, 75007, France" """ require_capabilities("weather_status.enabled", self._session.capabilities) diff --git a/pyproject.toml b/pyproject.toml index fe8d3202..bb3f8e99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,8 +104,8 @@ preview = true [tool.ruff] line-length = 120 target-version = "py39" -select = ["A", "B", "C", "D", "E", "F", "G", "I", "UP", "SIM", "Q", "W"] -extend-ignore = ["D107", "D105", "D203", "D213", "D401", "I001"] +select = ["A", "B", "C", "D", "E", "F", "G", "I", "S", "SIM", "Q", "RET", "RUF", "UP" , "W"] +extend-ignore = ["D107", "D105", "D203", "D213", "D401", "I001", "RUF100"] [tool.ruff.per-file-ignores] "nc_py_api/__init__.py" = ["F401"] @@ -114,8 +114,8 @@ extend-ignore = ["D107", "D105", "D203", "D213", "D401", "I001"] [tool.ruff.extend-per-file-ignores] "benchmarks/**/*.py" = ["D"] "docs/**/*.py" = ["D"] -"examples/**/*.py" = ["D"] -"tests/**/*.py" = ["D", "E402", "UP"] +"examples/**/*.py" = ["D", "S106"] +"tests/**/*.py" = ["D", "E402", "S", "UP"] [tool.ruff.mccabe] max-complexity = 16 diff --git a/tests/actual_tests/files_test.py b/tests/actual_tests/files_test.py index a3c58ea0..46f464b4 100644 --- a/tests/actual_tests/files_test.py +++ b/tests/actual_tests/files_test.py @@ -48,7 +48,7 @@ def test_list_user_root_self_exclude(nc): user_root = nc.files.listdir() user_root_with_self = nc.files.listdir(exclude_self=False) assert len(user_root_with_self) == 1 + len(user_root) - self_res = [i for i in user_root_with_self if not i.user_path][0] + self_res = next(i for i in user_root_with_self if not i.user_path) for i in user_root: assert self_res != i assert self_res.has_extra diff --git a/tests/actual_tests/talk_test.py b/tests/actual_tests/talk_test.py index 9446d86d..e6449147 100644 --- a/tests/actual_tests/talk_test.py +++ b/tests/actual_tests/talk_test.py @@ -101,11 +101,11 @@ def test_get_conversations_include_status(nc, nc_client): try: conversations = nc.talk.get_user_conversations(include_status=False) assert conversations - first_conv = [i for i in conversations if i.conversation_id == conversation.conversation_id][0] + first_conv = next(i for i in conversations if i.conversation_id == conversation.conversation_id) assert not first_conv.status_type conversations = nc.talk.get_user_conversations(include_status=True) assert conversations - first_conv = [i for i in conversations if i.conversation_id == conversation.conversation_id][0] + first_conv = next(i for i in conversations if i.conversation_id == conversation.conversation_id) assert first_conv.status_type == "away" finally: nc.talk.leave_conversation(conversation.token) @@ -194,7 +194,7 @@ def test_list_bots(nc, nc_app): if nc_app.talk.bots_available is False: pytest.skip("Need Talk bots support") nc_app.register_talk_bot("/some_url", "some bot name", "some desc") - registered_bot = [i for i in nc.talk.list_bots() if i.bot_name == "some bot name"][0] + registered_bot = next(i for i in nc.talk.list_bots() if i.bot_name == "some bot name") assert isinstance(registered_bot.bot_id, int) assert registered_bot.url.find("/some_url") != -1 assert registered_bot.description == "some desc" @@ -218,11 +218,15 @@ def test_chat_bot_receive_message(nc_app): talk_bot_inst.enabled_handler(True, nc_app) conversation = nc_app.talk.create_conversation(talk.ConversationType.GROUP, "admin") try: - coverage_bot = [i for i in nc_app.talk.list_bots() if i.url.endswith("/talk_bot_coverage")][0] - c_bot_info = [i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id][0] + coverage_bot = next(i for i in nc_app.talk.list_bots() if i.url.endswith("/talk_bot_coverage")) + c_bot_info = next( + i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id + ) assert c_bot_info.state == 0 nc_app.talk.enable_bot(conversation, coverage_bot) - c_bot_info = [i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id][0] + c_bot_info = next( + i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id + ) assert c_bot_info.state == 1 with pytest.raises(ValueError): nc_app.talk.send_message("Here are the msg!") @@ -234,10 +238,14 @@ def test_chat_bot_receive_message(nc_app): msg_from_bot = messages[-1] break assert msg_from_bot - c_bot_info = [i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id][0] + c_bot_info = next( + i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id + ) assert c_bot_info.state == 1 nc_app.talk.disable_bot(conversation, coverage_bot) - c_bot_info = [i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id][0] + c_bot_info = next( + i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id + ) assert c_bot_info.state == 0 finally: nc_app.talk.delete_conversation(conversation.token)