Skip to content

Commit

Permalink
Merge pull request #71 from zalando-incubator/support-locust-1.0
Browse files Browse the repository at this point in the history
Support Locust >=1
  • Loading branch information
thilp authored Jun 19, 2020
2 parents f567220 + 68efe54 commit d31e34e
Show file tree
Hide file tree
Showing 9 changed files with 336 additions and 146 deletions.
31 changes: 31 additions & 0 deletions docs/Changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,37 @@ The format is based on `Keep a Changelog`_, and this project adheres to
:local:
:depth: 1

.. _v1.3.0:

v1.3.0
======

- Release date: 2020-06-18 10:00

- Diff__.

__ https://github.com/zalando-incubator/transformer/compare/v1.2.7...v1.3.0

Added
-----

Generated locustfiles now support both current major versions of Locust
(``<1`` and ``~1``).

.. note::

Please note that this support will most likely be *only temporary*, so all
users of Transformer are kindly encouraged to upgrade their Locust (or tell us
what's blocking them from upgrading).

Since Transformer follows `semantic versioning`_, dropping support for some
versions of Locust will only be done in a major Transformer version, most
likely 2.0 (not yet planned).
If you rely on a pre-1.0 Locust, you can therefore continue upgrading
Transformer by following its *minor* updates
(e.g. ``pip install -U har-transformer=~1``).


.. _v1.2.7:

v1.2.7
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
author = "the Zalando maintainers"

# The short X.Y version
version = "1.2"
version = "1.3"
# The full version, including alpha/beta/rc tags
release = "1.2.7"
release = "1.3.0"


# -- General configuration ---------------------------------------------------
Expand Down
293 changes: 199 additions & 94 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "har-transformer"
version = "1.2.7"
version = "1.3.0"
description = "A tool to convert HAR files into a locustfile."
authors = [
"Serhii Cherniavskyi <[email protected]>",
Expand Down Expand Up @@ -42,7 +42,7 @@ requests = "^2.21"
docs = ["sphinx", "sphinx-autodoc-typehints", "sphinx-issues"]

[tool.poetry.dev-dependencies]
locustio = "^0.14.6"
locust = "^1.0.2"
pytest-cov = "*"
pytest-mock = "^1.10"
black = {version = "*",allow-prereleases = true}
Expand Down
4 changes: 3 additions & 1 deletion tests/functional/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@


def test_version():
expected_pattern = re.compile(r"\b [0-9]+ \. [0-9]+ \. [0-9]+ \b", re.X)
expected_pattern = re.compile(
r"\b [0-9]+ \. [0-9]+ \. [0-9]+ ([ab] [0-9]+)? \b", re.X
)
actual = (
subprocess.run(["transformer", "--version"], check=True, stdout=subprocess.PIPE)
.stdout.strip()
Expand Down
60 changes: 40 additions & 20 deletions tests/transformer/test_locust.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,35 @@ def test_it_renders_a_locustfile_template(self):
# File automatically generated by Transformer v{__version__}:
# https://github.com/zalando-incubator/Transformer
import re
import sys
from distutils.version import LooseVersion
from locust import __version__
if LooseVersion(__version__) >= LooseVersion('1.0.0'):
print(f'Sorry! You have locust=={{__version__}},', "but Transformer doesn't support locust>=0.99 yet.", 'Please try again with a less recent Locust version', '(e.g. "pip install \\'locustio==0.14.6\\'")', 'while we are working on a long-term solution. 😊', file=sys.stderr)
exit(1)
from locust import HttpLocust
from locust import TaskSequence
from locust import TaskSet
from locust import seq_task
from locust import task
LOCUST_MAJOR_VERSION = LooseVersion(__version__).version[0]
if LOCUST_MAJOR_VERSION >= 1:
from locust import HttpUser
from locust import SequentialTaskSet
from locust import TaskSet
from locust import task
HttpLocust = HttpUser
TaskSequence = SequentialTaskSet
def seq_task(_):
return task
else:
from locust import HttpLocust
from locust import TaskSequence
from locust import TaskSet
from locust import seq_task
from locust import task
class ScenarioGroup(TaskSet):
@task(1)
class SomeScenario(TaskSequence):
@seq_task(1)
def some_task(self):
response = self.client.get(url='some_url', name='some_url', timeout=$TIMEOUT, allow_redirects=False, headers={{'a': 'b'}})
class LocustForScenarioGroup(HttpLocust):
task_set = ScenarioGroup
if LOCUST_MAJOR_VERSION >= 1:
tasks = [ScenarioGroup]
else:
task_set = ScenarioGroup
weight = 2
min_wait = 0
max_wait = 10
Expand Down Expand Up @@ -92,25 +102,35 @@ def plugin_change_task_name(t: Task2) -> Task2:
# File automatically generated by Transformer v{__version__}:
# https://github.com/zalando-incubator/Transformer
import re
import sys
from distutils.version import LooseVersion
from locust import __version__
if LooseVersion(__version__) >= LooseVersion('1.0.0'):
print(f'Sorry! You have locust=={{__version__}},', "but Transformer doesn't support locust>=0.99 yet.", 'Please try again with a less recent Locust version', '(e.g. "pip install \\'locustio==0.14.6\\'")', 'while we are working on a long-term solution. 😊', file=sys.stderr)
exit(1)
from locust import HttpLocust
from locust import TaskSequence
from locust import TaskSet
from locust import seq_task
from locust import task
LOCUST_MAJOR_VERSION = LooseVersion(__version__).version[0]
if LOCUST_MAJOR_VERSION >= 1:
from locust import HttpUser
from locust import SequentialTaskSet
from locust import TaskSet
from locust import task
HttpLocust = HttpUser
TaskSequence = SequentialTaskSet
def seq_task(_):
return task
else:
from locust import HttpLocust
from locust import TaskSequence
from locust import TaskSet
from locust import seq_task
from locust import task
class ScenarioGroup(TaskSet):
@task(1)
class SomeScenario(TaskSequence):
@seq_task(1)
def some_task(self):
response = self.client.get(url='some_url', name='changed_name', timeout=$TIMEOUT, allow_redirects=False, headers={{'a': 'b'}})
class LocustForScenarioGroup(HttpLocust):
task_set = ScenarioGroup
if LOCUST_MAJOR_VERSION >= 1:
tasks = [ScenarioGroup]
else:
task_set = ScenarioGroup
weight = 2
min_wait = 0
max_wait = 10
Expand Down
2 changes: 1 addition & 1 deletion transformer/builders_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def ascii_text(min_size: int = 0, max_size: Optional[int] = 5) -> SearchStrategy


def ascii_inline_text(
min_size: int = 0, max_size: Optional[int] = 5
min_size: int = 0, max_size: Optional[int] = 3
) -> SearchStrategy[str]:
"""Similar to ascii_text, but does not generate multiline strings."""
return text(_ascii_inline, min_size=min_size, max_size=max_size)
Expand Down
62 changes: 36 additions & 26 deletions transformer/locust.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,21 @@ def locust_classes(scenarios: Sequence[Scenario]) -> List[py.Class]:
classes = []
for scenario in scenarios:
taskset = locust_taskset(scenario)
is_post_1 = py.BinaryOp(py.Symbol("LOCUST_MAJOR_VERSION"), ">=", py.Literal(1))
tasks = py.IfElse(
[
(
is_post_1,
[py.Assignment("tasks", py.Literal([py.Symbol(taskset.name)]))],
)
],
[py.Assignment("task_set", py.Symbol(taskset.name))],
)
locust_class = py.Class(
name=f"LocustFor{taskset.name}",
superclasses=["HttpLocust"],
statements=[
py.Assignment("task_set", py.Symbol(taskset.name)),
tasks,
py.Assignment("weight", py.Literal(scenario.weight)),
py.Assignment("min_wait", py.Literal(LOCUST_MIN_WAIT_DELAY)),
py.Assignment("max_wait", py.Literal(LOCUST_MAX_WAIT_DELAY)),
Expand All @@ -99,29 +109,32 @@ def locust_classes(scenarios: Sequence[Scenario]) -> List[py.Class]:
return classes


def locust_version_guard() -> py.Program:
cond = py.BinaryOp(
py.FunctionCall("LooseVersion", [py.Symbol("__version__")]),
">=",
py.FunctionCall("LooseVersion", [py.Literal("1.0.0")]),
)
print_call = py.FunctionCall(
"print",
[
py.FString("Sorry! You have locust=={__version__},"),
py.Literal("but Transformer doesn't support locust>=0.99 yet."),
py.Literal("Please try again with a less recent Locust version"),
py.Literal("""(e.g. "pip install 'locustio==0.14.6'")"""),
py.Literal("while we are working on a long-term solution. 😊"),
],
{"file": py.Symbol("sys.stderr")},
)
abort_call = py.FunctionCall("exit", [py.Literal(1)])
def locust_detected_version() -> py.Program:
return [
py.Import(["sys"]),
py.Import(["LooseVersion"], source="distutils.version"),
py.Import(["__version__"], source="locust"),
py.IfElse([(cond, [py.Standalone(print_call), py.Standalone(abort_call)])]),
py.OpaqueBlock("LOCUST_MAJOR_VERSION = LooseVersion(__version__).version[0]"),
]


def locust_imports() -> py.Program:
is_post_1 = py.BinaryOp(py.Symbol("LOCUST_MAJOR_VERSION"), ">=", py.Literal(1),)
imports_pre_1 = [
py.Import(
["HttpLocust", "TaskSequence", "TaskSet", "seq_task", "task"],
source="locust",
)
]
imports_post_1 = [
py.Import(
["HttpUser", "SequentialTaskSet", "TaskSet", "task"], source="locust",
),
py.Assignment("HttpLocust", py.Symbol("HttpUser")),
py.Assignment("TaskSequence", py.Symbol("SequentialTaskSet")),
py.Function("seq_task", ["_"], [py.Return(py.Symbol("task"))]),
]
return [
py.IfElse([(is_post_1, imports_post_1)], imports_pre_1),
]


Expand All @@ -139,11 +152,8 @@ def locust_program(scenarios: Sequence[Scenario]) -> py.Program:

return [
py.Import(["re"], comments=[LOCUSTFILE_COMMENT]),
*locust_version_guard(),
py.Import(
["HttpLocust", "TaskSequence", "TaskSet", "seq_task", "task"],
source="locust",
),
*locust_detected_version(),
*locust_imports(),
*locust_classes(scenarios),
*global_code_blocks.values(),
]
Expand Down
22 changes: 22 additions & 0 deletions transformer/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,28 @@ def __repr__(self) -> str:
)


class Return(Statement):
"""The return statement."""

def __init__(self, value: Expression, comments: Sequence[str] = ()) -> None:
super().__init__(comments)
self.value = value

def lines(self, indent_level: int = 0, comments: bool = True) -> List[Line]:
line = Line(f"return {self.value}", indent_level)
if comments:
return self.attach_comment(line)
return [line]

def __eq__(self, o: object) -> bool:
return super().__eq__(o) and self.value == cast(self.__class__, o).value

def __repr__(self) -> str:
return "{}(value={!r}, comments={!r})".format(
self.__class__.__qualname__, self.value, self.comments
)


_T = TypeVar("_T")


Expand Down

0 comments on commit d31e34e

Please sign in to comment.