Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better error message when User.task is set instead of User.tasks #2142

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/tasksets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ For example, the following code will request URLs /1-/4 in order, and then repea
self.client.get("/1")
self.client.get("/2")

# you can still use the tasks property to specify a list of tasks
# you can still use the tasks attribute to specify a list of tasks
tasks = [function_task]

@task
Expand Down
2 changes: 1 addition & 1 deletion docs/writing-a-locustfile.rst
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ classes. Say for example, web users are three times more likely than mobile user
...

Also you can set the :py:attr:`fixed_count <locust.User.fixed_count>` attribute.
In this case the weight property will be ignored and the exact count users will be spawned.
In this case the weight attribute will be ignored and the exact count users will be spawned.
These users are spawned first. In the example below, only one instance of AdminUser
will be spawned, to make some specific work with more accurate control
of request count independently of total user count.
Expand Down
4 changes: 2 additions & 2 deletions locust/argument_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,12 +439,12 @@ def setup_parser_arguments(parser):
other_group.add_argument(
"--show-task-ratio",
action="store_true",
help="Print table of the User classes' task execution ratio. Use this with non-zero --user option if some classes define non-zero fixed_count property.",
help="Print table of the User classes' task execution ratio. Use this with non-zero --user option if some classes define non-zero fixed_count attribute.",
)
other_group.add_argument(
"--show-task-ratio-json",
action="store_true",
help="Print json data of the User classes' task execution ratio. Use this with non-zero --user option if some classes define non-zero fixed_count property.",
help="Print json data of the User classes' task execution ratio. Use this with non-zero --user option if some classes define non-zero fixed_count attribute.",
)
# optparse gives you --version but we have to do it ourselves to get -V too
other_group.add_argument(
Expand Down
2 changes: 1 addition & 1 deletion locust/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class HttpSession(requests.Session):
the methods for making requests (get, post, delete, put, head, options, patch, request)
can now take a *url* argument that's only the path part of the URL, in which case the host
part of the URL will be prepended with the HttpSession.base_url which is normally inherited
from a User class' host property.
from a User class' host attribute.

Each of the methods for making requests also takes two additional optional arguments which
are Locust specific and doesn't exist in python-requests. These are:
Expand Down
4 changes: 4 additions & 0 deletions locust/test/test_locust_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,17 @@ class MyUser(User):
self.assertRaisesRegex(Exception, "No tasks defined on MyTasks.*", l.run)
l.tasks = []
self.assertRaisesRegex(Exception, "No tasks defined on MyTasks.*", l.run)
MyTasks.task = object()
self.assertRaisesRegex(Exception, ".*but you have set a 'task' attribute.*", l.run)

def test_tasks_missing_from_user_gives_user_friendly_exception(self):
class MyUser(User):
wait_time = constant(0.5)

l = MyUser(self.environment)
self.assertRaisesRegex(Exception, "No tasks defined on MyUser.*", l.run)
MyUser.task = object()
self.assertRaisesRegex(Exception, ".*but you have set a 'task' attribute.*", l.run)

def test_task_decorator_ratio(self):
t1 = lambda l: None
Expand Down
2 changes: 1 addition & 1 deletion locust/test/test_runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -2219,7 +2219,7 @@ def my_task(self):
)
)
sleep(0.2)
# hearbeat received from two workers so they are active, for fake_client3 HEARTBEAT_DEAD_INTERNAL has been breached, so it will be removed from worker list
# heartbeat received from two workers so they are active, for fake_client3 HEARTBEAT_DEAD_INTERNAL has been breached, so it will be removed from worker list
self.assertEqual(0, len(master.clients.missing))
self.assertEqual(2, master.worker_count)
master.stop()
Expand Down
2 changes: 1 addition & 1 deletion locust/user/sequential_taskset.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def __init__(self, *args, **kwargs):
def get_next_task(self):
if not self.tasks:
raise LocustError(
"No tasks defined. use the @task decorator or set the tasks property of the SequentialTaskSet"
"No tasks defined. Use the @task decorator or set the 'tasks' attribute of the SequentialTaskSet"
)
task = self.tasks[self._task_index % len(self.tasks)]
self._task_index += 1
Expand Down
12 changes: 10 additions & 2 deletions locust/user/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,12 @@ def schedule_task(self, task_callable, first=False):

def get_next_task(self):
if not self.tasks:
if getattr(self, "task", None):
extra_message = ", but you have set a 'task' attribute - maybe you meant to set 'tasks'?"
else:
extra_message = "."
raise Exception(
f"No tasks defined on {self.__class__.__name__}. use the @task decorator or set the tasks property of the TaskSet"
f"No tasks defined on {self.__class__.__name__}{extra_message} use the @task decorator or set the 'tasks' attribute of the TaskSet"
)
return random.choice(self.tasks)

Expand Down Expand Up @@ -469,8 +473,12 @@ class DefaultTaskSet(TaskSet):

def get_next_task(self):
if not self.user.tasks:
if getattr(self.user, "task", None):
extra_message = ", but you have set a 'task' attribute on your class - maybe you meant to set 'tasks'?"
else:
extra_message = "."
raise Exception(
f"No tasks defined on {self.user.__class__.__name__}. use the @task decorator or set the tasks property of the User (or mark it as abstract = True if you only intend to subclass it)"
f"No tasks defined on {self.user.__class__.__name__}{extra_message} Use the @task decorator or set the 'tasks' attribute of the User (or mark it as abstract = True if you only intend to subclass it)"
)
return random.choice(self.user.tasks)

Expand Down