From 8c64e916699539ea09cf9e250d630270e87b8716 Mon Sep 17 00:00:00 2001 From: Lars Holmberg Date: Wed, 17 Apr 2024 23:59:27 +0200 Subject: [PATCH] Remove python 3.8 compatibility code (#2679) * Remove code that was only needed for Python 3.8 compatibility (changes mostly generated using pyupgrade) * Re-enable some test cases that didnt work on 3.8 * re-organize imports using ruff --- examples/rest.py | 10 ++++------ locust/clients.py | 2 +- locust/contrib/fasthttp.py | 3 ++- locust/dispatch.py | 21 +++------------------ locust/event.py | 3 ++- locust/main.py | 27 ++++++--------------------- locust/runners.py | 4 +--- locust/stats.py | 8 ++++---- locust/test/mock_logging.py | 2 +- locust/test/test_main.py | 4 +--- locust/user/task.py | 2 +- 11 files changed, 26 insertions(+), 60 deletions(-) diff --git a/examples/rest.py b/examples/rest.py index 185fb2e8ed..0b1f0c5379 100644 --- a/examples/rest.py +++ b/examples/rest.py @@ -40,12 +40,10 @@ def t(self): # use a trailing comma to append the response text to the custom message assert resp.js["data"]["foo"] == 2, "my custom error message with response text," - # this only works in python 3.8 and up, so it is commented out: - # if sys.version_info >= (3, 8): - # with self.rest("", "/post", json={"foo": 1}) as resp: - # # assign and assert in one line - # assert (foo := resp.js["foo"]) - # print(f"the number {foo} is awesome") + with self.rest("", "/post", json={"foo": 1}) as resp: + # assign and assert in one line + assert (foo := resp.js["foo"]) + print(f"the number {foo} is awesome") # rest() catches most exceptions, so any programming mistakes you make automatically marks the request as a failure # and stores the callstack in the failure message diff --git a/locust/clients.py b/locust/clients.py index 2b551edfb8..ffec80c0a7 100644 --- a/locust/clients.py +++ b/locust/clients.py @@ -2,8 +2,8 @@ import re import time +from collections.abc import Generator from contextlib import contextmanager -from typing import Generator from urllib.parse import urlparse, urlunparse import requests diff --git a/locust/contrib/fasthttp.py b/locust/contrib/fasthttp.py index f47c084981..88e6caf4d4 100644 --- a/locust/contrib/fasthttp.py +++ b/locust/contrib/fasthttp.py @@ -12,11 +12,12 @@ import time import traceback from base64 import b64encode +from collections.abc import Generator from contextlib import contextmanager from http.cookiejar import CookieJar from json.decoder import JSONDecodeError from ssl import SSLError -from typing import Any, Callable, Generator, cast +from typing import Any, Callable, cast from urllib.parse import urlparse, urlunparse import gevent diff --git a/locust/dispatch.py b/locust/dispatch.py index d8c676a9e8..d4fd9ce895 100644 --- a/locust/dispatch.py +++ b/locust/dispatch.py @@ -1,14 +1,13 @@ from __future__ import annotations import contextlib -import functools import itertools import math -import sys import time from collections import defaultdict +from collections.abc import Generator, Iterator from operator import attrgetter -from typing import TYPE_CHECKING, Generator, Iterator +from typing import TYPE_CHECKING import gevent from roundrobin import smooth @@ -18,20 +17,6 @@ from locust.runners import WorkerNode -def compatible_math_gcd(*args: int) -> int: - """ - This function is a workaround for the fact that `math.gcd` in: - - 3.5 <= Python < 3.9 doesn't accept more than two arguments. - - 3.9 <= Python can accept more than two arguments. - See more at https://docs.python.org/3.9/library/math.html#math.gcd - """ - if (3, 5) <= sys.version_info < (3, 9): - return functools.reduce(math.gcd, args) - elif sys.version_info >= (3, 9): - return math.gcd(*args) - raise NotImplementedError("This function is only implemented for Python from 3.5") - - # To profile line-by-line, uncomment the code below (i.e. `import line_profiler ...`) and # place `@profile` on the functions/methods you wish to profile. Then, in the unit test you are # running, use `from locust.dispatch import profile; profile.print_stats()` at the end of the unit test. @@ -406,7 +391,7 @@ def _get_order_of_magnitude(n: float) -> int: max_order_of_magnitude = _get_order_of_magnitude(min(abs(u[1]) for u in users)) weights = tuple(int(u[1] * max_order_of_magnitude) for u in users) - greatest_common_divisor = compatible_math_gcd(*weights) + greatest_common_divisor = math.gcd(*weights) normalized_values = [ ( user[0].__name__, diff --git a/locust/event.py b/locust/event.py index 98ed70c776..81818e7803 100644 --- a/locust/event.py +++ b/locust/event.py @@ -3,8 +3,9 @@ import logging import time import traceback +from collections.abc import Generator from contextlib import contextmanager -from typing import Any, Generator +from typing import Any from . import log from .exception import InterruptTaskSet, RescheduleTask, RescheduleTaskImmediately, StopUser diff --git a/locust/main.py b/locust/main.py index 849aeda1a2..aec37b5375 100644 --- a/locust/main.py +++ b/locust/main.py @@ -250,12 +250,8 @@ def sigint_handler(_signal, _frame): # nothing more to do, just wait for the children to exit for child_pid in children: _, child_status = os.waitpid(child_pid, 0) - try: - if sys.version_info >= (3, 9): - child_exit_code = os.waitstatus_to_exitcode(child_status) - exit_code = max(exit_code, child_exit_code) - except AttributeError: - pass # dammit python 3.8... + child_exit_code = os.waitstatus_to_exitcode(child_status) + exit_code = max(exit_code, child_exit_code) sys.exit(exit_code) else: options.master = True @@ -270,12 +266,8 @@ def kill_workers(children): try: _, child_status = os.waitpid(child_pid, os.WNOHANG) children.remove(child_pid) - try: - if sys.version_info >= (3, 9): - child_exit_code = os.waitstatus_to_exitcode(child_status) - exit_code = max(exit_code, child_exit_code) - except AttributeError: - pass # dammit python 3.8... + child_exit_code = os.waitstatus_to_exitcode(child_status) + exit_code = max(exit_code, child_exit_code) except OSError as e: if e.errno == errno.EINTR: time.sleep(0.1) @@ -291,12 +283,8 @@ def kill_workers(children): pass # never mind, process was already dead for child_pid in children: _, child_status = os.waitpid(child_pid, 0) - try: - if sys.version_info >= (3, 9): - child_exit_code = os.waitstatus_to_exitcode(child_status) - exit_code = max(exit_code, child_exit_code) - except AttributeError: - pass # dammit python 3.8... + child_exit_code = os.waitstatus_to_exitcode(child_status) + exit_code = max(exit_code, child_exit_code) if exit_code > 1: logging.error(f"Bad response code from worker children: {exit_code}") # ensure master doesnt finish until output from workers has arrived @@ -356,9 +344,6 @@ def kill_workers(children): See https://github.com/locustio/locust/wiki/Installation#increasing-maximum-number-of-open-files-limit for more info.""" ) - if sys.version_info < (3, 9): - logger.warning("Python 3.8 support is deprecated and will be removed soon") - # create locust Environment locustfile_path = None if not locustfile else os.path.basename(locustfile) diff --git a/locust/runners.py b/locust/runners.py index 939ce50ce3..1961bce98f 100644 --- a/locust/runners.py +++ b/locust/runners.py @@ -14,7 +14,7 @@ import traceback from abc import abstractmethod from collections import defaultdict -from collections.abc import MutableMapping +from collections.abc import Iterator, MutableMapping, ValuesView from operator import ( itemgetter, methodcaller, @@ -24,10 +24,8 @@ TYPE_CHECKING, Any, Callable, - Iterator, NoReturn, TypedDict, - ValuesView, cast, ) from uuid import uuid4 diff --git a/locust/stats.py b/locust/stats.py index 208d5938cc..0cfd7efc21 100644 --- a/locust/stats.py +++ b/locust/stats.py @@ -10,6 +10,10 @@ import time from abc import abstractmethod from collections import OrderedDict, defaultdict, namedtuple +from collections import ( + OrderedDict as OrderedDictType, +) +from collections.abc import Iterable from copy import copy from html import escape from itertools import chain @@ -19,16 +23,12 @@ TYPE_CHECKING, Any, Callable, - Iterable, NoReturn, Protocol, TypedDict, TypeVar, cast, ) -from typing import ( - OrderedDict as OrderedDictType, -) import gevent diff --git a/locust/test/mock_logging.py b/locust/test/mock_logging.py index 756c479c51..1a57d3393c 100644 --- a/locust/test/mock_logging.py +++ b/locust/test/mock_logging.py @@ -5,7 +5,7 @@ from typing import List, Union, Dict from types import TracebackType -LogMessage = List[Union[str, Dict[str, TracebackType]]] +LogMessage = list[Union[str, dict[str, TracebackType]]] class MockedLoggingHandler(logging.Handler): diff --git a/locust/test/test_main.py b/locust/test/test_main.py index 9981d65f7a..a71f8ef24e 100644 --- a/locust/test/test_main.py +++ b/locust/test/test_main.py @@ -483,7 +483,6 @@ def my_task(self): self.assertIn("Shutting down (exit code 0)", stderr) self.assertEqual(0, proc.returncode) - @unittest.skipIf(sys.version_info < (3, 9), reason="dies in 3.8 on GH and I cant be bothered to investigate it") def test_default_headless_spawn_options_with_shape(self): content = MOCK_LOCUSTFILE_CONTENT + textwrap.dedent( """ @@ -2225,7 +2224,6 @@ def mytask(self): self.assertNotIn("Traceback", stderr) self.assertIn("INFO/locust.runners: sys.exit(42) called", stderr) - if sys.version_info >= (3, 9): - self.assertEqual(status_code, 42) + self.assertEqual(status_code, 42) self.assertNotIn("Traceback", master_stderr) self.assertIn("failed to send heartbeat, setting state to missing", master_stderr) diff --git a/locust/user/task.py b/locust/user/task.py index 6fa8591b93..eabc125ad1 100644 --- a/locust/user/task.py +++ b/locust/user/task.py @@ -25,7 +25,7 @@ logger = logging.getLogger(__name__) -TaskT = TypeVar("TaskT", Callable[..., None], Type["TaskSet"]) +TaskT = TypeVar("TaskT", Callable[..., None], type["TaskSet"]) LOCUST_STATE_RUNNING, LOCUST_STATE_WAITING, LOCUST_STATE_STOPPING = ["running", "waiting", "stopping"]