Skip to content

Commit

Permalink
Merge pull request #1253 from locustio/remove-six-and-other-2.7-compa…
Browse files Browse the repository at this point in the history
…tibility-code

Remove six and other 2.7 compatibility code
  • Loading branch information
cyberw authored Feb 10, 2020
2 parents 30c3f1c + 5d6b63a commit 9ad03bd
Show file tree
Hide file tree
Showing 17 changed files with 53 additions and 105 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@ Open source licensed under the MIT license (see _LICENSE_ file for details).

## Supported Python Versions

Locust is supported on Python 2.7, 3.5, 3.6, 3.7, 3.8.
Locust is supported on Python 3.6, 3.7, 3.8.
5 changes: 2 additions & 3 deletions locust/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
import time

import requests
import six
from requests import Request, Response
from requests.auth import HTTPBasicAuth
from requests.exceptions import (InvalidSchema, InvalidURL, MissingSchema,
RequestException)

from six.moves.urllib.parse import urlparse, urlunparse
from urllib.parse import urlparse, urlunparse

from . import events
from .exception import CatchResponseError, ResponseError
Expand Down Expand Up @@ -245,7 +244,7 @@ def failure(self, exc):
if response.content == b"":
response.failure("No data")
"""
if isinstance(exc, six.string_types):
if isinstance(exc, str):
exc = CatchResponseError(exc)

events.request_failure.fire(
Expand Down
18 changes: 4 additions & 14 deletions locust/contrib/fasthttp.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
from __future__ import absolute_import

import re
import six
import socket
from base64 import b64encode
from six.moves.urllib.parse import urlparse, urlunparse
from urllib.parse import urlparse, urlunparse
from ssl import SSLError
from timeit import default_timer

if six.PY2:
from cookielib import CookieJar
class ConnectionRefusedError(Exception):
# ConnectionRefusedError doesn't exist in python 2, so we'll
# define a dummy class to avoid a NameError
pass
else:
from http.cookiejar import CookieJar
unicode = str
from http.cookiejar import CookieJar

import gevent
from gevent.timeout import Timeout
Expand Down Expand Up @@ -253,7 +244,6 @@ class FastResponse(CompatResponse):
def text(self):
"""
Returns the text content of the response as a decoded string
(unicode on python2)
"""
if self.content is None:
return None
Expand All @@ -262,7 +252,7 @@ def text(self):
self.encoding = 'utf-8'
else:
self.encoding = self.headers.get('content-type', '').partition("charset=")[2] or 'utf-8'
return unicode(self.content, self.encoding, errors='replace')
return str(self.content, self.encoding, errors='replace')

def raise_for_status(self):
"""Raise any connection errors that occured during the request"""
Expand Down Expand Up @@ -383,7 +373,7 @@ def failure(self, exc):
if response.content == "":
response.failure("No data")
"""
if isinstance(exc, six.string_types):
if isinstance(exc, str):
exc = CatchResponseError(exc)

events.request_failure.fire(
Expand Down
23 changes: 10 additions & 13 deletions locust/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@

import gevent
import gevent.lock
import six

from gevent import GreenletExit, monkey
from six.moves import xrange

# The monkey patching must run before requests is imported, or else
# we'll get an infinite recursion when doing SSL/HTTPS requests.
Expand Down Expand Up @@ -186,7 +184,7 @@ def run(self, runner=None):
except StopLocust:
pass
except (RescheduleTask, RescheduleTaskImmediately) as e:
six.reraise(LocustError, LocustError("A task inside a Locust class' main TaskSet (`%s.task_set` of type `%s`) seems to have called interrupt() or raised an InterruptTaskSet exception. The interrupt() function is used to hand over execution to a parent TaskSet, and should never be called in the main TaskSet which a Locust class' task_set attribute points to." % (type(self).__name__, self.task_set.__name__)), sys.exc_info()[2])
raise LocustError("A task inside a Locust class' main TaskSet (`%s.task_set` of type `%s`) seems to have called interrupt() or raised an InterruptTaskSet exception. The interrupt() function is used to hand over execution to a parent TaskSet, and should never be called in the main TaskSet which a Locust class' task_set attribute points to." % (type(self).__name__, self.task_set.__name__)) from e
except GreenletExit as e:
if runner:
runner.state = STATE_CLEANUP
Expand Down Expand Up @@ -245,27 +243,26 @@ def __new__(mcs, classname, bases, classDict):
if "tasks" in classDict and classDict["tasks"] is not None:
tasks = classDict["tasks"]
if isinstance(tasks, dict):
tasks = six.iteritems(tasks)
tasks = tasks.items()

for task in tasks:
if isinstance(task, tuple):
task, count = task
for i in xrange(0, count):
for i in range(count):
new_tasks.append(task)
else:
new_tasks.append(task)

for item in six.itervalues(classDict):
for item in classDict.values():
if hasattr(item, "locust_task_weight"):
for i in xrange(0, item.locust_task_weight):
for i in range(0, item.locust_task_weight):
new_tasks.append(item)

classDict["tasks"] = new_tasks

return type.__new__(mcs, classname, bases, classDict)

@six.add_metaclass(TaskSetMeta)
class TaskSet(object):
class TaskSet(object, metaclass=TaskSetMeta):
"""
Class defining a set of tasks that a Locust user will execute.
Expand Down Expand Up @@ -388,9 +385,9 @@ def run(self, *args, **kwargs):
self.on_start()
except InterruptTaskSet as e:
if e.reschedule:
six.reraise(RescheduleTaskImmediately, RescheduleTaskImmediately(e.reschedule), sys.exc_info()[2])
raise RescheduleTaskImmediately(e.reschedule).with_traceback(sys.exc_info()[2])
else:
six.reraise(RescheduleTask, RescheduleTask(e.reschedule), sys.exc_info()[2])
raise RescheduleTask(e.reschedule).with_traceback(sys.exc_info()[2])

while (True):
try:
Expand All @@ -413,9 +410,9 @@ def run(self, *args, **kwargs):
self.wait()
except InterruptTaskSet as e:
if e.reschedule:
six.reraise(RescheduleTaskImmediately, RescheduleTaskImmediately(e.reschedule), sys.exc_info()[2])
raise RescheduleTaskImmediately(e.reschedule) from e
else:
six.reraise(RescheduleTask, RescheduleTask(e.reschedule), sys.exc_info()[2])
raise RescheduleTask(e.reschedule) from e
except StopLocust:
raise
except GreenletExit:
Expand Down
8 changes: 3 additions & 5 deletions locust/inspectlocust.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import inspect

import six

from .core import Locust, TaskSet
from .log import console_logger

Expand All @@ -11,7 +9,7 @@ def print_task_ratio(locusts, total=False, level=0, parent_ratio=1.0):
_print_task_ratio(d)

def _print_task_ratio(x, level=0):
for k, v in six.iteritems(x):
for k, v in x.items():
padding = 2*" "*level
ratio = v.get('ratio', 1)
console_logger.info(" %-10s %-50s" % (padding + "%-6.1f" % (ratio*100), padding + k))
Expand All @@ -33,10 +31,10 @@ def get_task_ratio_dict(tasks, total=False, parent_ratio=1.0):
ratio[task] += task.weight if hasattr(task, 'weight') else 1

# get percentage
ratio_percent = dict((k, float(v) / divisor) for k, v in six.iteritems(ratio))
ratio_percent = dict((k, float(v) / divisor) for k, v in ratio.items())

task_dict = {}
for locust, ratio in six.iteritems(ratio_percent):
for locust, ratio in ratio_percent.items():
d = {"ratio":ratio}
if inspect.isclass(locust):
if issubclass(locust, Locust):
Expand Down
12 changes: 2 additions & 10 deletions locust/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,16 +361,8 @@ def __import_locustfile__(filename, path):
"""
Loads the locust file as a module, similar to performing `import`
"""
try:
# Python 3 compatible
source = importlib.machinery.SourceFileLoader(os.path.splitext(locustfile)[0], path)
imported = source.load_module()
except AttributeError:
# Python 2.7 compatible
import imp
imported = imp.load_source(os.path.splitext(locustfile)[0], path)

return imported
source = importlib.machinery.SourceFileLoader(os.path.splitext(locustfile)[0], path)
return source.load_module()

# Start with making sure the current working dir is in the sys.path
sys.path.insert(0, os.getcwd())
Expand Down
15 changes: 6 additions & 9 deletions locust/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@

import gevent
import psutil
import six
from gevent import GreenletExit
from gevent.pool import Group

from six.moves import xrange

from . import events
from .rpc import Message, rpc
from .stats import global_stats
Expand Down Expand Up @@ -99,7 +96,7 @@ def weight_locusts(self, amount):
# create locusts depending on weight
percent = locust.weight / float(weight_sum)
num_locusts = int(round(amount * percent))
bucket.extend([locust for x in xrange(0, num_locusts)])
bucket.extend([locust for x in range(num_locusts)])
# used to keep track of the amount of rounding was done if we need
# to add/remove some instances from bucket
residuals[locust] = amount * percent - round(amount * percent)
Expand Down Expand Up @@ -132,7 +129,7 @@ def hatch():
while True:
if not bucket:
logger.info("All locusts hatched: %s (%i already running)" % (
", ".join(["%s: %d" % (name, count) for name, count in six.iteritems(occurrence_count)]),
", ".join(["%s: %d" % (name, count) for name, count in occurrence_count.items()]),
existing_count,
))
events.hatch_complete.fire(user_count=len(self.locusts))
Expand Down Expand Up @@ -327,11 +324,11 @@ def __init__(self, *args, **kwargs):

class SlaveNodesDict(dict):
def get_by_state(self, state):
return [c for c in six.itervalues(self) if c.state == state]
return [c for c in self.values() if c.state == state]

@property
def all(self):
return six.itervalues(self)
return self.values()

@property
def ready(self):
Expand Down Expand Up @@ -366,7 +363,7 @@ def on_quitting():

@property
def user_count(self):
return sum([c.user_count for c in six.itervalues(self.clients)])
return sum([c.user_count for c in self.clients.values()])

def cpu_log_warning(self):
warning_emitted = LocustRunner.cpu_log_warning(self)
Expand Down Expand Up @@ -472,7 +469,7 @@ def client_listener(self):
self.clients[msg.node_id].state = STATE_RUNNING
self.clients[msg.node_id].user_count = msg.data["count"]
if len(self.clients.hatching) == 0:
count = sum(c.user_count for c in six.itervalues(self.clients))
count = sum(c.user_count for c in self.clients.values())
events.hatch_complete.fire(user_count=count)
elif msg.type == "quit":
if msg.node_id in self.clients:
Expand Down
26 changes: 12 additions & 14 deletions locust/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
from itertools import chain

import gevent
import six
from six.moves import xrange

from . import events
from .exception import StopLocust
Expand Down Expand Up @@ -64,7 +62,7 @@ def calculate_response_time_percentile(response_times, num_requests, percent):
num_of_request = int((num_requests * percent))

processed_count = 0
for response_time in sorted(six.iterkeys(response_times), reverse=True):
for response_time in sorted(response_times.keys(), reverse=True):
processed_count += response_times[response_time]
if(num_requests - processed_count <= num_of_request):
return response_time
Expand Down Expand Up @@ -146,7 +144,7 @@ def reset_all(self):
"""
self.total.reset()
self.errors = {}
for r in six.itervalues(self.entries):
for r in self.entries.values():
r.reset()

def clear_all(self):
Expand All @@ -158,10 +156,10 @@ def clear_all(self):
self.errors = {}

def serialize_stats(self):
return [self.entries[key].get_stripped_report() for key in six.iterkeys(self.entries) if not (self.entries[key].num_requests == 0 and self.entries[key].num_failures == 0)]
return [self.entries[key].get_stripped_report() for key in self.entries.keys() if not (self.entries[key].num_requests == 0 and self.entries[key].num_failures == 0)]

def serialize_errors(self):
return dict([(k, e.to_dict()) for k, e in six.iteritems(self.errors)])
return dict([(k, e.to_dict()) for k, e in self.errors.items()])


class StatsEntry(object):
Expand Down Expand Up @@ -518,7 +516,7 @@ def get_current_response_time_percentile(self, percent):
# that it's ordered by preference by starting to add t-10, then t-11, t-9, t-12, t-8,
# and so on
acceptable_timestamps = []
for i in xrange(9):
for i in range(9):
acceptable_timestamps.append(t-CURRENT_RESPONSE_TIME_PERCENTILE_WINDOW-i)
acceptable_timestamps.append(t-CURRENT_RESPONSE_TIME_PERCENTILE_WINDOW+i)

Expand Down Expand Up @@ -575,7 +573,7 @@ def _cache_response_times(self, t):

if len(self.response_times_cache) > cache_size:
# only keep the latest 20 response_times dicts
for i in xrange(len(self.response_times_cache) - cache_size):
for i in range(len(self.response_times_cache) - cache_size):
self.response_times_cache.popitem(last=False)


Expand Down Expand Up @@ -639,7 +637,7 @@ def median_from_dict(total, count):
count is a dict {response_time: count}
"""
pos = (total - 1) / 2
for k in sorted(six.iterkeys(count)):
for k in sorted(count.keys()):
if pos < count[k]:
return k
pos -= count[k]
Expand Down Expand Up @@ -671,7 +669,7 @@ def on_slave_report(client_id, data):
global_stats.entries[request_key] = StatsEntry(global_stats, entry.name, entry.method)
global_stats.entries[request_key].extend(entry)

for error_key, error in six.iteritems(data["errors"]):
for error_key, error in data["errors"].items():
if error_key not in global_stats.errors:
global_stats.errors[error_key] = StatsError.from_dict(error)
else:
Expand Down Expand Up @@ -701,7 +699,7 @@ def on_slave_report(client_id, data):
def print_stats(stats, current=True):
console_logger.info((" %-" + str(STATS_NAME_WIDTH) + "s %7s %12s %7s %7s %7s | %7s %7s %7s") % ('Name', '# reqs', '# fails', 'Avg', 'Min', 'Max', 'Median', 'req/s', 'failures/s'))
console_logger.info("-" * (80 + STATS_NAME_WIDTH))
for key in sorted(six.iterkeys(stats.entries)):
for key in sorted(stats.entries.keys()):
r = stats.entries[key]
console_logger.info(r.to_string(current=current))
console_logger.info("-" * (80 + STATS_NAME_WIDTH))
Expand All @@ -728,7 +726,7 @@ def print_percentile_stats(stats):
'100%',
))
console_logger.info("-" * (90 + STATS_NAME_WIDTH))
for key in sorted(six.iterkeys(stats.entries)):
for key in sorted(stats.entries.keys()):
r = stats.entries[key]
if r.response_times:
console_logger.info(r.percentile())
Expand All @@ -744,7 +742,7 @@ def print_error_report():
console_logger.info("Error report")
console_logger.info(" %-18s %-100s" % ("# occurrences", "Error"))
console_logger.info("-" * (80 + STATS_NAME_WIDTH))
for error in six.itervalues(global_stats.errors):
for error in global_stats.errors.values():
console_logger.info(" %-18i %-100s" % (error.occurrences, error.to_name()))
console_logger.info("-" * (80 + STATS_NAME_WIDTH))
console_logger.info("")
Expand Down Expand Up @@ -777,7 +775,7 @@ def write_stat_csvs(base_filepath, stats_history_enabled=False):


def sort_stats(stats):
return [stats[key] for key in sorted(six.iterkeys(stats))]
return [stats[key] for key in sorted(stats.keys())]


def requests_csv():
Expand Down
Loading

0 comments on commit 9ad03bd

Please sign in to comment.