def test_with_kwargs(self): assert (str( py.FunctionCall("f", named_args={ "a": py.Literal(2), "bc": py.Literal("x") })) == "f(a=2, bc='x')")
def locust_classes(scenarios: Sequence[Scenario]) -> List[py.Class]: """ Transforms scenarios into all Python classes needed by Locust (TaskSet and Locust classes). The only missing parts before a fully functional locustfile are: - integrating all necessary set-up/tear-down statements: - Python imports, - apply global plugins, - etc. - serializing everything via transformer.python. """ classes = [] for scenario in scenarios: taskset = locust_taskset(scenario) locust_class = py.Class( name=f"LocustFor{taskset.name}", superclasses=["HttpLocust"], statements=[ py.Assignment("task_set", py.Symbol(taskset.name)), 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)), ], ) classes.append(taskset) classes.append(locust_class) return classes
def lreq_to_expr(lr: LocustRequest) -> py.FunctionCall: # TODO: Remove me once LocustRequest no longer exists. # See https://github.com/zalando-incubator/Transformer/issues/11. url = _peel_off_repr(lr.url) name = _peel_off_repr(lr.name) if lr.name else url args: Dict[str, py.Expression] = OrderedDict( url=url, name=name, timeout=py.Literal(TIMEOUT), allow_redirects=py.Literal(False), ) if lr.headers: args["headers"] = py.Literal(lr.headers) if lr.method is HttpMethod.POST: if lr.post_data: rpd = RequestsPostData.from_har_post_data(lr.post_data) args.update(rpd.as_kwargs()) elif lr.method in (HttpMethod.PUT, HttpMethod.PATCH): if lr.post_data: rpd = RequestsPostData.from_har_post_data(lr.post_data) args.update(rpd.as_kwargs()) args.setdefault("params", py.Literal([])) cast(py.Literal, args["params"]).value.extend( _params_from_name_value_dicts( [dataclasses.asdict(q) for q in lr.query])) elif lr.method not in NOOP_HTTP_METHODS: raise ValueError(f"unsupported HTTP method: {lr.method!r}") method = lr.method.name.lower() return py.FunctionCall(name=f"self.client.{method}", named_args=args)
def test_lines_with_nested_body(self, level: int): x = py.Line.INDENT_UNIT f = py.Function( "func", params=[], statements=[ py.Assignment("a", py.Literal(2)), py.IfElse( [(py.Literal(True), [py.Assignment("b", py.Literal(3))])], [ py.Assignment("b", py.Literal(4)), py.Assignment("c", py.Literal(1)), ], ), ], ) assert [str(l) for l in f.lines(level)] == [ x * level + "def func():", x * (level + 1) + "a = 2", x * (level + 1) + "if True:", x * (level + 2) + "b = 3", x * (level + 1) + "else:", x * (level + 2) + "b = 4", x * (level + 2) + "c = 1", ]
def test_lines_for_single_if(self, level: int): x = py.Line.INDENT_UNIT assert [ str(l) for l in py.IfElse([( py.BinaryOp(py.Symbol("t"), "is", py.Literal(None)), [py.Assignment("t", py.Literal(1))], )]).lines(level) ] == [x * level + "if t is None:", x * (level + 1) + "t = 1"]
def test_with_positional_and_kwargs(self): assert (str( py.FunctionCall( "m.f", [py.Literal(True), py.FunctionCall("g", [py.Symbol("f")])], { "a": py.Literal(2), "bc": py.Literal("x") }, )) == "m.f(True, g(f), a=2, bc='x')")
def test_nested(self): assert (str( py.BinaryOp( py.Literal(2), "+", py.BinaryOp( py.BinaryOp(py.Literal(3), "-", py.Literal(4)), "*", py.Literal(5), ), )) == "2 + ((3 - 4) * 5)")
def test_it_accepts_both_params_and_text(self, mime: str, kwarg, val): expected_fields = { "params": py.Literal([(b"n", b"v")]), kwarg: py.Literal(val), } assert RequestsPostData.from_har_post_data( { "mimeType": mime, "text": "{}", "params": [{"name": "n", "value": "v"}], } ) == RequestsPostData(**expected_fields)
def test_it_supports_fstring_urls(self): url = "http://abc.{tld}" r = LocustRequest(method=HttpMethod.GET, url=f"f'{url}'", headers={"a": "b"}) assert lreq_to_expr(r) == py.FunctionCall( name="self.client.get", named_args={ "url": py.FString(url), "name": py.FString(url), "headers": py.Literal({"a": "b"}), "timeout": py.Literal(TIMEOUT), "allow_redirects": py.Literal(False), }, )
def test_class_with_fields(self, level: int): c = py.Class( "A", statements=[ py.Assignment("a", py.Literal(2)), py.Assignment("b", py.Literal(3)), ], ) x = py.Line.INDENT_UNIT assert [str(l) for l in c.lines(level)] == [ x * level + "class A:", x * (level + 1) + "a = 2", x * (level + 1) + "b = 3", ]
def _from_har_post_data(post_data: dict) -> RequestsPostData: mime_k = "mimeType" try: mime: str = post_data[mime_k] except KeyError: raise ValueError(f"missing {mime_k!r} field") from None rpd = RequestsPostData() # The "text" and "params" fields are supposed to be mutually # exclusive (according to the HAR spec) but nobody respects that. # Often, both text and params are provided for x-www-form-urlencoded. text_k, params_k = "text", "params" if text_k not in post_data and params_k not in post_data: raise ValueError(f"should contain {text_k!r} or {params_k!r}") _extract_text(mime, post_data, text_k, rpd) try: params = _params_from_post_data(params_k, post_data) if params is not None: rpd.params = py.Literal(params) except (KeyError, UnicodeEncodeError, TypeError) as err: raise ValueError("unreadable params field") from err return rpd
def _peel_off_repr(s: str) -> py.Literal: """ Reverse the effect of LocustRequest's repr() calls on url and name. """ if s.startswith("f"): return py.FString(eval(s[1:], {}, {})) return py.Literal(eval(s, {}, {}))
def test_init_with_no_condition_raises_error(self): with pytest.raises(ValueError): py.IfElse([]) with pytest.raises(ValueError): py.IfElse([], else_block=[]) with pytest.raises(ValueError): py.IfElse([], else_block=[py.Assignment("a", py.Literal(2))])
def test_wraps_int_into_literal(self): def f(x: int) -> py.Literal: return py.Literal(x * 2) ev = py.ExpressionView(name="hello", target=lambda: 7, converter=f) assert ev.converter(ev.target()) == py.Literal(14) assert str(ev) == "14"
def _extract_text(mime: str, post_data: dict, text_k: str, rpd: RequestsPostData) -> None: text = post_data.get(text_k) if mime == JSON_MIME_TYPE: if text is None: raise ValueError( f"missing {text_k!r} field for {JSON_MIME_TYPE} content") try: rpd.json = py.Literal(json.loads(text)) except JSONDecodeError as err: raise ValueError(f"unreadable JSON from field {text_k!r}") from err elif text is not None: # Probably application/x-www-form-urlencoded. try: rpd.data = py.Literal(text.encode()) except UnicodeEncodeError as err: raise ValueError( f"cannot encode the {text_k!r} field in UTF-8") from err
def test_repr(self): cond = py.Literal(True) if_true = [(cond, [py.Assignment("x", py.Symbol("a"))])] stmt = py.IfElse(condition_blocks=if_true, comments=["hi"]) assert ( repr(stmt) == f"IfElse(condition_blocks={if_true!r}, else_block=None, comments=['hi'])" )
def test_it_supports_urlencoded_post_requests(self): url = "http://abc.de" r = LocustRequest.from_request( Request( timestamp=MagicMock(), method=HttpMethod.POST, url=urlparse(url), har_entry={"entry": "data"}, headers={"a": "b"}, post_data={ "mimeType": "application/x-www-form-urlencoded", "params": [{"name": "x", "value": "y"}], "text": "z=7", }, ) ) assert lreq_to_expr(r) == py.FunctionCall( name="self.client.post", named_args={ "url": py.Literal(url), "name": py.Literal(url), "headers": py.Literal({"a": "b"}), "timeout": py.Literal(TIMEOUT), "allow_redirects": py.Literal(False), "data": py.Literal(b"z=7"), "params": py.Literal([(b"x", b"y")]), }, )
def test_it_supports_patch_requests_with_payload(self): url = "http://abc.de" r = LocustRequest.from_request( Request( timestamp=MagicMock(), method=HttpMethod.PATCH, url=urlparse(url), har_entry={"entry": "data"}, headers={"a": "b"}, query=[QueryPair("c", "d")], post_data={ "mimeType": "application/json", "params": [{"name": "x", "value": "y"}], "text": """{"z": 7}""", }, ) ) assert lreq_to_expr(r) == py.FunctionCall( name="self.client.patch", named_args={ "url": py.Literal(url), "name": py.Literal(url), "headers": py.Literal({"a": "b"}), "timeout": py.Literal(TIMEOUT), "allow_redirects": py.Literal(False), "json": py.Literal({"z": 7}), "params": py.Literal([(b"x", b"y"), (b"c", b"d")]), }, )
def test_it_supports_json_post_requests(self): url = "http://abc.de" r = Request( timestamp=MagicMock(), method=HttpMethod.POST, url=urlparse(url), har_entry={"entry": "data"}, headers={"a": "b"}, post_data={ "mimeType": "application/json", "params": [{"name": "x", "value": "y"}], "text": """{"z": 7}""", }, ) assert req_to_expr(r) == py.FunctionCall( name="self.client.post", named_args={ "url": py.Literal(url), "name": py.Literal(url), "headers": py.Literal({"a": "b"}), "timeout": py.Literal(TIMEOUT), "allow_redirects": py.Literal(False), "json": py.Literal({"z": 7}), "params": py.Literal([(b"x", b"y")]), }, )
def test_lines_for_if_elif_else_with_no_statements(self, level: int): x = py.Line.INDENT_UNIT assert [ str(l) for l in py.IfElse( [ (py.BinaryOp(py.Symbol("t"), "is", py.Literal(None)), []), (py.Literal(False), []), (py.Literal(True), []), ], [], ).lines(level) ] == [ x * level + "if t is None:", x * (level + 1) + "pass", x * level + "elif False:", x * (level + 1) + "pass", x * level + "elif True:", x * (level + 1) + "pass", ]
def test_it_uses_the_custom_name_if_provided(self): url = "http://abc.de" name = "my-req" r = Request( name=name, timestamp=MagicMock(), method=HttpMethod.GET, url=urlparse(url), har_entry={"entry": "data"}, ) assert req_to_expr(r) == py.FunctionCall( name="self.client.get", named_args={ "url": py.Literal(url), "name": py.Literal(name), "timeout": py.Literal(TIMEOUT), "allow_redirects": py.Literal(False), }, )
def test_with_a_class(self, level: int): c = py.Class("C", superclasses=[], statements=[py.Assignment("a: int", py.Literal(1))]) d = py.Decoration("task", c) x = py.Line.INDENT_UNIT assert [str(l) for l in d.lines(level)] == [ x * level + "@task", *[str(l) for l in c.lines(level)], ]
def test_it_supports_empty_post_requests(self): url = "http://abc.de" r = Request( timestamp=MagicMock(), method=HttpMethod.POST, url=urlparse(url), har_entry={"entry": "data"}, headers={"a": "b"}, post_data=None, ) assert req_to_expr(r) == py.FunctionCall( name="self.client.post", named_args={ "url": py.Literal(url), "name": py.Literal(url), "headers": py.Literal({"a": "b"}), "timeout": py.Literal(TIMEOUT), "allow_redirects": py.Literal(False), }, )
def test_it_supports_get_requests(self): url = "http://abc.de" r = Request( timestamp=MagicMock(), method=HttpMethod.GET, url=urlparse(url), har_entry={"entry": "data"}, headers={"a": "b"}, query=[QueryPair("x", "y")], # query is currently ignored for GET ) assert req_to_expr(r) == py.FunctionCall( name="self.client.get", named_args={ "url": py.Literal(url), "name": py.Literal(url), "headers": py.Literal({"a": "b"}), "timeout": py.Literal(TIMEOUT), "allow_redirects": py.Literal(False), }, )
def test_lines_with_hidden_comments(self, level: int): x = py.Line.INDENT_UNIT cond = py.Literal(True) if_true = py.Assignment("x", py.Symbol("a"), comments=["tx", "ty"]) if_false = py.Assignment("x", py.Symbol("b"), comments=["fx", "fy"]) stmt = py.IfElse([(cond, [if_true])], [if_false], comments=["1", "2"]) assert [str(l) for l in stmt.lines(level, comments=False)] == [ x * level + "if True:", *[str(l) for l in if_true.lines(level + 1, comments=False)], x * level + "else:", *[str(l) for l in if_false.lines(level + 1, comments=False)], ]
def req_to_expr(r: Request) -> py.FunctionCall: url = py.Literal(str(r.url.geturl())) args: Dict[str, py.Expression] = OrderedDict( url=url, name=py.Literal(r.name) if r.name else url, timeout=py.Literal(TIMEOUT), allow_redirects=py.Literal(False), ) if r.headers: args["headers"] = py.Literal(r.headers) if r.method is HttpMethod.POST: if r.post_data: rpd = RequestsPostData.from_har_post_data(r.post_data) args.update(rpd.as_kwargs()) elif r.method in (HttpMethod.PUT, HttpMethod.PATCH): if r.post_data: rpd = RequestsPostData.from_har_post_data(r.post_data) args.update(rpd.as_kwargs()) args.setdefault("params", py.Literal([])) cast(py.Literal, args["params"]).value.extend( _params_from_name_value_dicts( [dataclasses.asdict(q) for q in r.query])) elif r.method not in NOOP_HTTP_METHODS: raise ValueError(f"unsupported HTTP method: {r.method!r}") method = r.method.name.lower() return py.FunctionCall(name=f"self.client.{method}", named_args=args)
def test_lines_for_if_elif(self, level: int): x = py.Line.INDENT_UNIT assert [ str(l) for l in py.IfElse([ ( py.BinaryOp(py.Symbol("t"), "is", py.Literal(None)), [py.Assignment("t", py.Literal(1))], ), ( py.Literal(False), [ py.Assignment("t", py.Literal(2)), py.Standalone(py.FunctionCall("f", [py.Symbol("t")])), ], ), ]).lines(level) ] == [ x * level + "if t is None:", x * (level + 1) + "t = 1", x * level + "elif False:", x * (level + 1) + "t = 2", x * (level + 1) + "f(t)", ]
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), ]
def test_it_supports_patch_requests_without_payload(self): url = "http://abc.de" r = Request( timestamp=MagicMock(), method=HttpMethod.PATCH, url=urlparse(url), har_entry={"entry": "data"}, headers={"a": "b"}, query=[QueryPair("c", "d")], post_data=None, ) assert req_to_expr(r) == py.FunctionCall( name="self.client.patch", named_args={ "url": py.Literal(url), "name": py.Literal(url), "headers": py.Literal({"a": "b"}), "timeout": py.Literal(TIMEOUT), "allow_redirects": py.Literal(False), "params": py.Literal([(b"c", b"d")]), }, )
def f(x: int) -> py.Literal: return py.Literal(x * 2)