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

Support Locust >=1 #71

Merged
merged 6 commits into from
Jun 19, 2020
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
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 <serhii.cherniavskyi@zalando.de>",
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