diff --git a/pyproject.toml b/pyproject.toml index ea90f90dd..13eecfd94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,14 +94,60 @@ pythonpath = [ [tool.ruff] select = [ + "B", # bugbear + "C4", # comprehensions + "COM", # trailing comma + "DTZ", # naive datetime "E", # style errors + "EXE", # shebang "F", # flakes + "FLY", # string formatting + "G", # logging format "I", # import sorting + "ICN", # import conventions + "INT", # gettext + "ISC", # string concatenation + "N", # naming + "PERF", # performance + "PGH", # pygrep-hooks + "PIE", # miscellaneous + "PL", # pylint + "PYI", # typing stubs + "Q", # quotes + "RSE", # raise + "RUF", # Ruff + "S", # security + "SLF", # self + "SLOT", # slots + "SIM", # simplify + "T10", # debugger + "TID", # tidy imports + "TRY", # try + "UP", # upgrade + "W", # style warnings + "YTT", # sys.version ] ignore = [ + "C408", # Unnecessary `dict` call (rewrite as a literal) + "COM812", # Trailing comma missing "E402", # Module level import not at top of file "E501", # Line too long "E731", # Do not assign a `lambda` expression, use a `def` + "PERF203", # `try`-`except` within a loop incurs performance overhead + "PLR0911", # Too many return statements + "PLR0912", # Too many branches + "PLR0913", # Too many arguments in function definition + "PLR0915", # Too many statements + "PLR2004", # Magic value used in comparison, consider replacing with a constant variable + "RUF001", # String contains ambiguous character + "S101", # Use of `assert` detected + "S113", # Probable use of requests call without timeout + "S603", # `subprocess` call: check for execution of untrusted input + "S606", # Starting a process without a shell + "S607", # Starting a process with a partial executable path + "SIM117", # Use a single `with` statement with multiple contexts instead of nested `with` statements + "TRY003", # Avoid specifying long messages outside the exception class + "TRY400", # Use `logging.exception` instead of `logging.error` ] src = [ "tools", diff --git a/tools/deploy b/tools/deploy index e95e97d7f..e5bc34a2e 100755 --- a/tools/deploy +++ b/tools/deploy @@ -112,7 +112,7 @@ def upload(options: argparse.Namespace) -> None: if not os.path.exists(file_path): print(f"upload: Could not find bot package at {file_path}.") sys.exit(1) - files = {"file": open(file_path, "rb")} + files = {"file": open(file_path, "rb")} # noqa: SIM115 headers = {"key": options.token} url = urllib.parse.urljoin(options.server, "bots/upload") response = requests.post(url, files=files, headers=headers) diff --git a/zulip/integrations/bridge_with_matrix/matrix_bridge.py b/zulip/integrations/bridge_with_matrix/matrix_bridge.py index fb0ece546..51a2e2490 100755 --- a/zulip/integrations/bridge_with_matrix/matrix_bridge.py +++ b/zulip/integrations/bridge_with_matrix/matrix_bridge.py @@ -353,7 +353,7 @@ async def handle_media(self, msg: str) -> Tuple[Optional[List[Dict[str, Any]]], continue try: - with urllib.request.urlopen(self.server_url + result["url"]) as response: + with urllib.request.urlopen(self.server_url + result["url"]) as response: # noqa: S310 file_content: bytes = response.read() mimetype: str = response.headers.get_content_type() except Exception: diff --git a/zulip/integrations/google/get-google-credentials b/zulip/integrations/google/get-google-credentials index 5bd45a3a5..bb97e5f69 100755 --- a/zulip/integrations/google/get-google-credentials +++ b/zulip/integrations/google/get-google-credentials @@ -14,7 +14,7 @@ flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args() SCOPES = "https://www.googleapis.com/auth/calendar.readonly" # This file contains the information that google uses to figure out which application is requesting # this client's data. -CLIENT_SECRET_FILE = "client_secret.json" +CLIENT_SECRET_FILE = "client_secret.json" # noqa: S105 APPLICATION_NAME = "Zulip Calendar Bot" HOME_DIR = os.path.expanduser("~") diff --git a/zulip/integrations/google/google-calendar b/zulip/integrations/google/google-calendar index 87299b7fa..227a42ae7 100755 --- a/zulip/integrations/google/google-calendar +++ b/zulip/integrations/google/google-calendar @@ -27,7 +27,7 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "../../")) import zulip SCOPES = "https://www.googleapis.com/auth/calendar.readonly" -CLIENT_SECRET_FILE = "client_secret.json" +CLIENT_SECRET_FILE = "client_secret.json" # noqa: S105 APPLICATION_NAME = "Zulip" HOME_DIR = os.path.expanduser("~") diff --git a/zulip/integrations/rss/rss-bot b/zulip/integrations/rss/rss-bot index f49342687..96e3d24f8 100755 --- a/zulip/integrations/rss/rss-bot +++ b/zulip/integrations/rss/rss-bot @@ -156,7 +156,7 @@ def strip_tags(html: str) -> str: def compute_entry_hash(entry: Dict[str, Any]) -> str: entry_time = entry.get("published", entry.get("updated")) entry_id = entry.get("id", entry.get("link")) - return hashlib.md5((entry_id + str(entry_time)).encode()).hexdigest() + return hashlib.md5((entry_id + str(entry_time)).encode()).hexdigest() # noqa: S324 def unwrap_text(body: str) -> str: diff --git a/zulip/integrations/zephyr/check-mirroring b/zulip/integrations/zephyr/check-mirroring index 995bf1dd4..b6477fdf3 100755 --- a/zulip/integrations/zephyr/check-mirroring +++ b/zulip/integrations/zephyr/check-mirroring @@ -64,7 +64,7 @@ if options.sharded: for stream, test in test_streams: if stream == "message": continue - assert hashlib.sha1(stream.encode("utf-8")).hexdigest().startswith(test) + assert hashlib.sha1(stream.encode("utf-8")).hexdigest().startswith(test) # noqa: S324 else: test_streams = [ ("message", "p"), diff --git a/zulip/integrations/zephyr/zephyr_ctypes.py b/zulip/integrations/zephyr/zephyr_ctypes.py index 2e7201676..5918f8b6f 100644 --- a/zulip/integrations/zephyr/zephyr_ctypes.py +++ b/zulip/integrations/zephyr/zephyr_ctypes.py @@ -31,7 +31,7 @@ # --- glibc/sysdeps/unix/sysv/linux/bits/socket.h --- -class sockaddr(Structure): +class sockaddr(Structure): # noqa: N801 _fields_ = ( ("sa_family", sa_family_t), ("sa_data", c_char * 14), @@ -44,11 +44,11 @@ class sockaddr(Structure): in_addr_t = c_uint32 -class in_addr(Structure): +class in_addr(Structure): # noqa: N801 _fields_ = (("s_addr", in_addr_t),) -class sockaddr_in(Structure): +class sockaddr_in(Structure): # noqa: N801 _fields_ = ( ("sin_family", sa_family_t), ("sin_port", in_port_t), @@ -57,11 +57,11 @@ class sockaddr_in(Structure): ) -class in6_addr(Structure): +class in6_addr(Structure): # noqa: N801 _fields_ = (("s6_addr", c_uint8 * 16),) -class sockaddr_in6(Structure): +class sockaddr_in6(Structure): # noqa: N801 _fields_ = ( ("sin6_family", sa_family_t), ("sin6_port", in_port_t), @@ -95,7 +95,7 @@ class _ZTimeval(Structure): ) -class ZUnique_Id_t(Structure): +class ZUnique_Id_t(Structure): # noqa: N801 _fields_ = ( ("zuid_addr", in_addr), ("tv", _ZTimeval), @@ -113,7 +113,7 @@ class _ZSenderSockaddr(Union): ) -class ZNotice_t(Structure): +class ZNotice_t(Structure): # noqa: N801 _fields_ = ( ("z_packet", c_char_p), ("z_version", c_char_p), @@ -146,7 +146,7 @@ class ZNotice_t(Structure): ) -class ZSubscription_t(Structure): +class ZSubscription_t(Structure): # noqa: N801 _fields_ = ( ("zsub_recipient", c_char_p), ("zsub_class", c_char_p), diff --git a/zulip/integrations/zephyr/zephyr_mirror_backend.py b/zulip/integrations/zephyr/zephyr_mirror_backend.py index d07aa7d9b..0d9f228e4 100755 --- a/zulip/integrations/zephyr/zephyr_mirror_backend.py +++ b/zulip/integrations/zephyr/zephyr_mirror_backend.py @@ -259,7 +259,7 @@ def update_subscriptions() -> None: classes_to_subscribe = set() for stream in public_streams: zephyr_class = stream - if options.shard is not None and not hashlib.sha1( + if options.shard is not None and not hashlib.sha1( # noqa: S324 zephyr_class.encode("utf-8") ).hexdigest().startswith(options.shard): # This stream is being handled by a different zephyr_mirror job. diff --git a/zulip/zulip/__init__.py b/zulip/zulip/__init__.py index 7bf300dad..8c2c49e2c 100644 --- a/zulip/zulip/__init__.py +++ b/zulip/zulip/__init__.py @@ -128,7 +128,7 @@ def fail(self) -> None: # Exponential growth with ratio sqrt(2); compute random delay # between x and 2x where x is growing exponentially delay_scale = int(2 ** (self.number_of_retries / 2.0 - 1)) + 1 - delay = min(delay_scale + random.randint(1, delay_scale), self.delay_cap) + delay = min(delay_scale + random.randint(1, delay_scale), self.delay_cap) # noqa: S311 message = f"Sleeping for {delay}s [max {delay_scale * 2}] before retrying." try: logger.warning(message) diff --git a/zulip/zulip/examples/upload-file b/zulip/zulip/examples/upload-file index caf7445d6..74f31f649 100755 --- a/zulip/zulip/examples/upload-file +++ b/zulip/zulip/examples/upload-file @@ -23,7 +23,7 @@ options = parser.parse_args() client = zulip.init_from_options(options) if options.file_path: - file: IO[Any] = open(options.file_path, "rb") + file: IO[Any] = open(options.file_path, "rb") # noqa: SIM115 else: file = StringIO("This is a test file.") file.name = "test.txt" diff --git a/zulip_bots/zulip_bots/bots/idonethis/idonethis.py b/zulip_bots/zulip_bots/bots/idonethis/idonethis.py index f80621d6a..8c7fec52f 100644 --- a/zulip_bots/zulip_bots/bots/idonethis/idonethis.py +++ b/zulip_bots/zulip_bots/bots/idonethis/idonethis.py @@ -148,7 +148,7 @@ def create_entry(message: str) -> str: class IDoneThisHandler: def initialize(self, bot_handler: BotHandler) -> None: - global api_key, default_team + global api_key, default_team # noqa: PLW0603 self.config_info = bot_handler.get_config_info("idonethis") if "api_key" in self.config_info: api_key = self.config_info["api_key"] diff --git a/zulip_bots/zulip_bots/bots/mention/test_mention.py b/zulip_bots/zulip_bots/bots/mention/test_mention.py index c4b601a04..b61ad7a11 100644 --- a/zulip_bots/zulip_bots/bots/mention/test_mention.py +++ b/zulip_bots/zulip_bots/bots/mention/test_mention.py @@ -27,14 +27,14 @@ def test_help_query(self) -> None: def test_get_account_id(self) -> None: bot_test_instance = MentionHandler() - bot_test_instance.access_token = "TEST" + bot_test_instance.access_token = "TEST" # noqa: S105 with self.mock_http_conversation("get_account_id"): self.assertEqual(bot_test_instance.get_account_id(), "TEST") def test_get_alert_id(self) -> None: bot_test_instance = MentionHandler() - bot_test_instance.access_token = "TEST" + bot_test_instance.access_token = "TEST" # noqa: S105 bot_test_instance.account_id = "TEST" with self.mock_http_conversation("get_alert_id"): @@ -42,7 +42,7 @@ def test_get_alert_id(self) -> None: def test_get_mentions(self) -> None: bot_test_instance = MentionHandler() - bot_test_instance.access_token = "TEST" + bot_test_instance.access_token = "TEST" # noqa: S105 bot_test_instance.account_id = "TEST" with self.mock_http_conversation("get_mentions"): diff --git a/zulip_bots/zulip_bots/bots/tictactoe/tictactoe.py b/zulip_bots/zulip_bots/bots/tictactoe/tictactoe.py index 4aa1fd9fc..abc54f13b 100644 --- a/zulip_bots/zulip_bots/bots/tictactoe/tictactoe.py +++ b/zulip_bots/zulip_bots/bots/tictactoe/tictactoe.py @@ -104,7 +104,7 @@ def computer_move(self, board: Any, player_number: Any) -> Any: board[1][1] = 2 # If user played first in the center, the computer should move in the corner. It doesn't matter which corner. else: - location = random.choice(corner_locations) + location = random.choice(corner_locations) # noqa: S311 row = location[0] col = location[1] board[row][col] = 2 @@ -156,16 +156,16 @@ def computer_move(self, board: Any, player_number: Any) -> Any: blank_set = set(blanks) blank_list = list(blank_set) if blank_list == []: - location = random.choice(blank_locations) + location = random.choice(blank_locations) # noqa: S311 else: - location = random.choice(blank_list) + location = random.choice(blank_list) # noqa: S311 row = location[0] col = location[1] board[row][col] = 2 return board else: - location = random.choice(blank_locations) + location = random.choice(blank_locations) # noqa: S311 row = location[0] col = location[1] board[row][col] = 2 diff --git a/zulip_bots/zulip_bots/bots/xkcd/xkcd.py b/zulip_bots/zulip_bots/bots/xkcd/xkcd.py index f30bca47e..a10196a7e 100644 --- a/zulip_bots/zulip_bots/bots/xkcd/xkcd.py +++ b/zulip_bots/zulip_bots/bots/xkcd/xkcd.py @@ -109,7 +109,7 @@ def fetch_xkcd_query(mode: int, comic_id: Optional[str] = None) -> Dict[str, str raise XkcdServerError latest_id = latest.json()["num"] - random_id = random.randint(1, latest_id) + random_id = random.randint(1, latest_id) # noqa: S311 url = XKCD_TEMPLATE_URL % (str(random_id),) elif mode == XkcdBotCommand.COMIC_ID: # Fetch specific comic strip by id number. diff --git a/zulip_bots/zulip_bots/game_handler.py b/zulip_bots/zulip_bots/game_handler.py index ef0b72c4e..0c1b66755 100644 --- a/zulip_bots/zulip_bots/game_handler.py +++ b/zulip_bots/zulip_bots/game_handler.py @@ -839,7 +839,7 @@ def __init__( self.stream = stream self.model = deepcopy(self.game_adapter.model()) self.board = self.model.current_board - self.turn = random.randrange(0, len(players)) - 1 + self.turn = random.randrange(0, len(players)) - 1 # noqa: S311 self.current_draw: Dict[str, bool] = {} self.current_messages: List[str] = [] self.is_changing_subject = False diff --git a/zulip_bots/zulip_bots/lib.py b/zulip_bots/zulip_bots/lib.py index 8676e1b3c..acdb5fa14 100644 --- a/zulip_bots/zulip_bots/lib.py +++ b/zulip_bots/zulip_bots/lib.py @@ -366,7 +366,7 @@ def open(self, filepath: str) -> IO[str]: filepath = os.path.normpath(filepath) abs_filepath = os.path.join(self._root_dir, filepath) if abs_filepath.startswith(self._root_dir): - return open(abs_filepath) + return open(abs_filepath) # noqa: SIM115 else: raise PermissionError( f'Cannot open file "{abs_filepath}". Bots may only access ' diff --git a/zulip_bots/zulip_bots/request_test_lib.py b/zulip_bots/zulip_bots/request_test_lib.py index 72d7163da..12c22f809 100644 --- a/zulip_bots/zulip_bots/request_test_lib.py +++ b/zulip_bots/zulip_bots/request_test_lib.py @@ -30,9 +30,9 @@ def get_response( mock_result.headers.update(http_headers) mock_result.encoding = get_encoding_from_headers(mock_result.headers) if is_raw_response: - mock_result._content = http_response.encode() # type: ignore[attr-defined] # This modifies a "hidden" attribute. + mock_result._content = http_response.encode() # type: ignore[attr-defined] # This modifies a "hidden" attribute. # noqa: SLF001 else: - mock_result._content = json.dumps(http_response).encode() + mock_result._content = json.dumps(http_response).encode() # noqa: SLF001 return mock_result def assert_called_with_fields( diff --git a/zulip_botserver/tests/test_server.py b/zulip_botserver/tests/test_server.py index 3911f9c12..14141e709 100644 --- a/zulip_botserver/tests/test_server.py +++ b/zulip_botserver/tests/test_server.py @@ -51,7 +51,7 @@ def test_successful_request(self) -> None: message={"content": "@**test** test message"}, bot_email="helloworld-bot@zulip.com", trigger="mention", - token="abcd1234", + token="abcd1234", # noqa: S106 ), expected_response="beep boop", check_success=True, @@ -79,7 +79,7 @@ def test_successful_request_from_two_bots(self) -> None: message={"content": "@**test** test message"}, bot_email="helloworld-bot@zulip.com", trigger="mention", - token="abcd1234", + token="abcd1234", # noqa: S106 ), expected_response="beep boop", bots_config=bots_config, @@ -119,7 +119,7 @@ def test_wrong_bot_token(self) -> None: message={"content": "@**test** test message"}, bot_email="helloworld-bot@zulip.com", trigger="mention", - token="wrongtoken", + token="wrongtoken", # noqa: S106 ), check_success=False, ) @@ -149,7 +149,7 @@ def test_wrong_bot_credentials( message={"content": "@**test** test message"}, bot_email="helloworld-bot@zulip.com", trigger="mention", - token="abcd1234", + token="abcd1234", # noqa: S106 ), bots_config=bots_config, ) diff --git a/zulip_botserver/zulip_botserver/server.py b/zulip_botserver/zulip_botserver/server.py index e81c54987..1c61f39ec 100755 --- a/zulip_botserver/zulip_botserver/server.py +++ b/zulip_botserver/zulip_botserver/server.py @@ -221,7 +221,7 @@ def handle_bot() -> str: def main() -> None: options = parse_args() - global bots_config + global bots_config # noqa: PLW0603 if options.use_env_vars: bots_config = read_config_from_env_vars(options.bot_name)