Permalink
Browse files

Add support for Python 3.

  • Loading branch information...
1 parent 96ace14 commit d0ec27a1b66b176bec339febc5d2d393d1fc1ffd @pmdarrow pmdarrow committed Dec 2, 2015
View
@@ -62,5 +62,5 @@ Open source licensed under the MIT license (see _LICENSE_ file for details).
## Supported Python Versions
-Locust requires **Python 2.6+**. It is not currently compatible with Python 3.x.
+Locust supports Python 2.6, 2.7 and 3.4.
View
@@ -1,4 +1,4 @@
-from core import HttpLocust, Locust, TaskSet, task
-from exception import InterruptTaskSet, ResponseError, RescheduleTaskImmediately
+from .core import HttpLocust, Locust, TaskSet, task
+from .exception import InterruptTaskSet, ResponseError, RescheduleTaskImmediately
__version__ = "0.7.5"
View
@@ -1,16 +1,17 @@
import re
import time
from datetime import timedelta
-from urlparse import urlparse, urlunparse
+from six.moves.urllib.parse import urlparse, urlunparse
+import six
import requests
from requests import Response, Request
from requests.auth import HTTPBasicAuth
from requests.exceptions import (RequestException, MissingSchema,
InvalidSchema, InvalidURL)
-import events
-from exception import CatchResponseError, ResponseError
+from . import events
+from .exception import CatchResponseError, ResponseError
absolute_http_url_regexp = re.compile(r"^https?://", re.I)
@@ -235,7 +236,7 @@ def failure(self, exc):
if response.content == "":
response.failure("No data")
"""
- if isinstance(exc, basestring):
+ if isinstance(exc, six.string_types):
exc = CatchResponseError(exc)
events.request_failure.fire(
View
@@ -1,5 +1,7 @@
import gevent
from gevent import monkey, GreenletExit
+import six
+from six.moves import xrange
monkey.patch_all(thread=False)
@@ -10,10 +12,10 @@
import traceback
import logging
-from clients import HttpSession
-import events
+from .clients import HttpSession
+from . import events
-from exception import LocustError, InterruptTaskSet, RescheduleTask, RescheduleTaskImmediately, StopLocust
+from .exception import LocustError, InterruptTaskSet, RescheduleTask, RescheduleTaskImmediately, StopLocust
logger = logging.getLogger(__name__)
@@ -103,7 +105,7 @@ def run(self):
except StopLocust:
pass
except (RescheduleTask, RescheduleTaskImmediately) as e:
- raise 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]
+ 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])
class HttpLocust(Locust):
@@ -146,7 +148,7 @@ def __new__(mcs, classname, bases, classDict):
if "tasks" in classDict and classDict["tasks"] is not None:
tasks = classDict["tasks"]
if isinstance(tasks, dict):
- tasks = list(tasks.iteritems())
+ tasks = six.iteritems(tasks)
for task in tasks:
if isinstance(task, tuple):
@@ -156,7 +158,7 @@ def __new__(mcs, classname, bases, classDict):
else:
new_tasks.append(task)
- for item in classDict.itervalues():
+ for item in six.itervalues(classDict):
if hasattr(item, "locust_task_weight"):
for i in xrange(0, item.locust_task_weight):
new_tasks.append(item)
@@ -165,6 +167,7 @@ def __new__(mcs, classname, bases, classDict):
return type.__new__(mcs, classname, bases, classDict)
+@six.add_metaclass(TaskSetMeta)
class TaskSet(object):
"""
Class defining a set of tasks that a Locust user will execute.
@@ -221,8 +224,6 @@ class ForumPage(TaskSet):
instantiated. Useful for nested TaskSet classes.
"""
- __metaclass__ = TaskSetMeta
-
def __init__(self, parent):
self._task_queue = []
self._time_start = time()
@@ -251,9 +252,9 @@ def run(self, *args, **kwargs):
self.on_start()
except InterruptTaskSet as e:
if e.reschedule:
- raise RescheduleTaskImmediately, e, sys.exc_info()[2]
+ six.reraise(RescheduleTaskImmediately, RescheduleTaskImmediately(e.reschedule), sys.exc_info()[2])
else:
- raise RescheduleTask, e, sys.exc_info()[2]
+ six.reraise(RescheduleTask, RescheduleTask(e.reschedule), sys.exc_info()[2])
while (True):
try:
@@ -273,9 +274,9 @@ def run(self, *args, **kwargs):
self.wait()
except InterruptTaskSet as e:
if e.reschedule:
- raise RescheduleTaskImmediately, e, sys.exc_info()[2]
+ six.reraise(RescheduleTaskImmediately, RescheduleTaskImmediately(e.reschedule), sys.exc_info()[2])
else:
- raise RescheduleTask, e, sys.exc_info()[2]
+ six.reraise(RescheduleTask, RescheduleTask(e.reschedule), sys.exc_info()[2])
except StopLocust:
raise
except GreenletExit:
@@ -294,7 +295,7 @@ def execute_next_task(self):
def execute_task(self, task, *args, **kwargs):
# check if the function is a method bound to the current locust, and if so, don't pass self as first argument
- if hasattr(task, "im_self") and task.__self__ == self:
+ if hasattr(task, "__self__") and task.__self__ == self:
# task is a bound method on self
task(*args, **kwargs)
elif hasattr(task, "tasks") and issubclass(task, TaskSet):
@@ -1,14 +1,15 @@
import inspect
+import six
-from core import Locust, TaskSet
-from log import console_logger
+from .core import Locust, TaskSet
+from .log import console_logger
def print_task_ratio(locusts, total=False, level=0, parent_ratio=1.0):
d = get_task_ratio_dict(locusts, total=total, parent_ratio=parent_ratio)
_print_task_ratio(d)
def _print_task_ratio(x, level=0):
- for k, v in x.iteritems():
+ for k, v in six.iteritems(x):
padding = 2*" "*level
ratio = v.get('ratio', 1)
console_logger.info(" %-10s %-50s" % (padding + "%-6.1f" % (ratio*100), padding + k))
@@ -30,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 ratio.iteritems())
+ ratio_percent = dict((k, float(v) / divisor) for k, v in six.iteritems(ratio))
task_dict = {}
- for locust, ratio in ratio_percent.iteritems():
+ for locust, ratio in six.iteritems(ratio_percent):
d = {"ratio":ratio}
if inspect.isclass(locust):
if issubclass(locust, Locust):
@@ -47,4 +48,4 @@ def get_task_ratio_dict(tasks, total=False, parent_ratio=1.0):
task_dict[locust.__name__] = d
- return task_dict
+ return task_dict
View
@@ -1,5 +1,5 @@
import locust
-import runners
+from . import runners
import gevent
import sys
@@ -10,13 +10,13 @@
import socket
from optparse import OptionParser
-import web
-from log import setup_logging, console_logger
-from stats import stats_printer, print_percentile_stats, print_error_report, print_stats
-from inspectlocust import print_task_ratio, get_task_ratio_dict
-from core import Locust, HttpLocust
-from runners import MasterLocustRunner, SlaveLocustRunner, LocalLocustRunner
-import events
+from . import web
+from .log import setup_logging, console_logger
+from .stats import stats_printer, print_percentile_stats, print_error_report, print_stats
+from .inspectlocust import print_task_ratio, get_task_ratio_dict
+from .core import Locust, HttpLocust
+from .runners import MasterLocustRunner, SlaveLocustRunner, LocalLocustRunner
+from . import events
_internals = [Locust, HttpLocust]
version = locust.__version__
@@ -338,7 +338,7 @@ def main():
logger = logging.getLogger(__name__)
if options.show_version:
- print "Locust %s" % (version,)
+ print("Locust %s" % (version,))
sys.exit(0)
locustfile = find_locustfile(options.locustfile)
@@ -409,7 +409,7 @@ def main():
try:
runners.locust_runner = SlaveLocustRunner(locust_classes, options)
main_greenlet = runners.locust_runner.greenlet
- except socket.error, e:
+ except socket.error as e:
logger.error("Failed to connect to the Locust master: %s", e)
sys.exit(-1)
@@ -1,9 +1,9 @@
import warnings
try:
- import zmqrpc as rpc
+ from . import zmqrpc as rpc
except ImportError:
warnings.warn("WARNING: Using pure Python socket RPC implementation instead of zmq. If running in distributed mode, this could cause a performance decrease. We recommend you to install the pyzmq python package when running in distributed mode.")
- import socketrpc as rpc
+ from . import socketrpc as rpc
from .protocol import Message
@@ -11,5 +11,5 @@ def serialize(self):
@classmethod
def unserialize(cls, data):
- msg = cls(*msgpack.loads(data))
+ msg = cls(*msgpack.loads(data, encoding='utf-8'))
return msg
View
@@ -10,11 +10,13 @@
import gevent
from gevent import GreenletExit
from gevent.pool import Group
+import six
+from six.moves import xrange
-import events
-from stats import global_stats
+from . import events
+from .stats import global_stats
-from rpc import rpc, Message
+from .rpc import rpc, Message
logger = logging.getLogger(__name__)
@@ -102,7 +104,7 @@ def hatch():
sleep_time = 1.0 / self.hatch_rate
while True:
if not bucket:
- logger.info("All locusts hatched: %s" % ", ".join(["%s: %d" % (name, count) for name, count in occurence_count.iteritems()]))
+ logger.info("All locusts hatched: %s" % ", ".join(["%s: %d" % (name, count) for name, count in six.iteritems(occurence_count)]))
events.hatch_complete.fire(user_count=self.num_clients)
return
@@ -225,7 +227,7 @@ def __init__(self, *args, **kwargs):
class SlaveNodesDict(dict):
def get_by_state(self, state):
- return [c for c in self.itervalues() if c.state == state]
+ return [c for c in six.itervalues(self) if c.state == state]
@property
def ready(self):
@@ -260,7 +262,7 @@ def on_quitting():
@property
def user_count(self):
- return sum([c.user_count for c in self.clients.itervalues()])
+ return sum([c.user_count for c in six.itervalues(self.clients)])
def start_hatching(self, locust_count, hatch_rate):
num_slaves = len(self.clients.ready) + len(self.clients.running)
@@ -270,7 +272,7 @@ def start_hatching(self, locust_count, hatch_rate):
return
self.num_clients = locust_count
- slave_num_clients = locust_count / (num_slaves or 1)
+ slave_num_clients = locust_count // (num_slaves or 1)
slave_hatch_rate = float(hatch_rate) / (num_slaves or 1)
remaining = locust_count % num_slaves
@@ -281,7 +283,7 @@ def start_hatching(self, locust_count, hatch_rate):
self.exceptions = {}
events.master_start_hatching.fire()
- for client in self.clients.itervalues():
+ for client in six.itervalues(self.clients):
data = {
"hatch_rate":slave_hatch_rate,
"num_clients":slave_num_clients,
@@ -305,7 +307,7 @@ def stop(self):
events.master_stop_hatching.fire()
def quit(self):
- for client in self.clients.itervalues():
+ for client in six.itervalues(self.clients):
self.server.send(Message("quit", None, None))
self.greenlet.kill(block=True)
@@ -332,7 +334,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 self.clients.itervalues())
+ count = sum(c.user_count for c in six.itervalues(self.clients))
events.hatch_complete.fire(user_count=count)
elif msg.type == "quit":
if msg.node_id in self.clients:
Oops, something went wrong.

0 comments on commit d0ec27a

Please sign in to comment.