diff --git a/docs/index.rst b/docs/index.rst index 786ccde..59fd86b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -78,7 +78,7 @@ Example .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client POST /Plone/folder HTTP/1.1 Host: localhost:8080 @@ -99,7 +99,7 @@ Example Rendering ========= -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client POST /Plone/folder HTTP/1.1 Host: localhost:8080 @@ -188,7 +188,12 @@ sphinxcontrib-httpexample is compatible with the following tab libraries. .. tab-item:: requests - .. http:example-block:: wget + .. http:example-block:: requests + :request: ../tests/fixtures/001.request.txt + + .. tab-item:: urllib3 + + .. http:example-block:: urllib3 :request: ../tests/fixtures/001.request.txt .. tab-item:: response diff --git a/docs/usage.rst b/docs/usage.rst index a1483bb..ed4cf7d 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -27,7 +27,7 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client GET /Plone/front-page HTTP/1.1 Host: localhost:8080 @@ -79,7 +79,7 @@ Markup Result `````` -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client GET /Plone/front-page HTTP/1.1 Host: localhost:8080 @@ -138,7 +138,7 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client GET /Plone/front-page HTTP/1.1 Host: localhost:8080 @@ -191,7 +191,7 @@ Markup Result `````` -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client GET /Plone/front-page HTTP/1.1 Host: localhost:8080 @@ -253,7 +253,7 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client GET /Plone/front-page HTTP/1.1 Host: localhost:8080 @@ -307,7 +307,7 @@ Markup Result `````` -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client GET /Plone/front-page HTTP/1.1 Host: localhost:8080 @@ -369,7 +369,7 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client POST /Plone/folder HTTP/1.1 Host: localhost:8080 @@ -389,7 +389,7 @@ Markup Result `````` -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client POST /Plone/folder HTTP/1.1 Host: localhost:8080 @@ -418,7 +418,7 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client PATCH /Plone/folder/my-document HTTP/1.1 Host: localhost:8080 @@ -437,7 +437,7 @@ Markup Result `````` -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client PATCH /Plone/folder/my-document HTTP/1.1 Host: localhost:8080 @@ -465,7 +465,7 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client GET /items?user_id=12&user_id=13 HTTP/1.1 Host: localhost @@ -482,7 +482,7 @@ Markup Result `````` - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client GET /items?user_id=12&user_id=13 HTTP/1.1 Host: localhost @@ -510,14 +510,14 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client :request: ../tests/fixtures/001.request.txt :response: ../tests/fixtures/001.response.txt Result `````` -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client :request: ../tests/fixtures/001.request.txt :response: ../tests/fixtures/001.response.txt @@ -534,7 +534,7 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client :request: ../tests/fixtures/001.request.txt .. literalinclude:: ../tests/fixtures/001.response.txt @@ -543,7 +543,7 @@ Markup Result `````` -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client :request: ../tests/fixtures/001.request.txt .. literalinclude:: ../tests/fixtures/001.response.txt @@ -558,14 +558,14 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client :request: ../tests/fixtures/002.request.txt :response: ../tests/fixtures/002.response.txt Result `````` -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client :request: ../tests/fixtures/002.request.txt :response: ../tests/fixtures/002.response.txt @@ -579,14 +579,14 @@ Markup .. code-block:: rst - .. http:example:: curl wget httpie requests plone-client + .. http:example:: curl wget httpie requests urllib3 plone-client :request: ../tests/fixtures/003.request.txt :response: ../tests/fixtures/003.response.txt Result `````` -.. http:example:: curl wget httpie requests plone-client +.. http:example:: curl wget httpie requests urllib3 plone-client :request: ../tests/fixtures/003.request.txt :response: ../tests/fixtures/003.response.txt diff --git a/pyproject.toml b/pyproject.toml index c937195..44298c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ exclude = ["tests"] [dependency-groups] dev = [ + "urllib3", "black>=25.1.0", "coverage>=7.7.1", "isort>=6.0.1", diff --git a/src/sphinxcontrib/httpexample/builders.py b/src/sphinxcontrib/httpexample/builders.py index 27e4dd2..874aa63 100644 --- a/src/sphinxcontrib/httpexample/builders.py +++ b/src/sphinxcontrib/httpexample/builders.py @@ -275,3 +275,405 @@ def astify_json_obj(obj): ) return unparse(tree).strip() + + +def build_urllib3_command(request): + # Imports + imports = [ + ast.Import(names=[ast.alias(name="urllib3")]), + ] + content_type = request.headers.get("Content-Type", "") + if is_json(content_type): + imports.append(ast.Import(names=[ast.alias(name="json")])) + elif content_type == "application/x-www-form-urlencoded": + imports.append( + ast.ImportFrom( + module="urllib.parse", + names=[ast.alias(name="urlencode")], + level=0, + ) + ) + + # Method + tree = ast.parse("http.request()") + call = tree.body[0].value + call.keywords = [] + + # Method + call.args.append(ast.Constant(request.command)) + + # URL + call.args.append(ast.Constant(request.url())) + + # Authorization (prepare) + method, token = request.auth() + if method == "Basic": + imports.append( + ast.ImportFrom( + module="urllib3.util", + names=[ast.alias(name="make_headers")], + level=0, + ) + ) + + # Headers + headers = {} + for header in sorted(request.headers): + if header in EXCLUDE_HEADERS_REQUESTS: + continue + headers[header] = request.headers[header] + + if method != "Basic" and "Authorization" in request.headers: + headers["Authorization"] = request.headers["Authorization"] + + header_keys = [ast.Constant(k) for k in headers.keys()] + header_values = [ast.Constant(v) for v in headers.values()] + headers_dict = ast.Dict(header_keys, header_values) + + if method == "Basic": + token = maybe_str(token) + headers_assignment = ast.Assign( + targets=[ast.Name(id="headers", ctx=ast.Store())], + value=headers_dict, + ) + auth_headers = ast.Call( + func=ast.Name(id="make_headers", ctx=ast.Load()), + args=[], + keywords=[ + ast.keyword( + "basic_auth", + ast.Constant(token), + ) + ], + ) + update_call = ast.Expr( + value=ast.Call( + func=ast.Attribute( + value=ast.Name(id="headers", ctx=ast.Load()), + attr="update", + ctx=ast.Load(), + ), + args=[auth_headers], + keywords=[], + ) + ) + call.keywords.append( + ast.keyword(arg="headers", value=ast.Name(id="headers", ctx=ast.Load())) + ) + elif headers: + call.keywords.append(ast.keyword(arg="headers", value=headers_dict)) + + # JSON or raw data + data = maybe_str(request.data()) + + # Form data + if content_type == "application/x-www-form-urlencoded": + if not isinstance(data, dict): + data = flatten_parsed_qs(parse_qs(data)) + + def astify_json_obj(obj): + obj = maybe_str(obj) + if isinstance(obj, str): + return ast.Constant(obj) + elif isinstance(obj, bool): + return ast.Name(str(obj), ast.Load()) + elif isinstance(obj, int): + return ast.Name(str(obj), ast.Load()) + elif isinstance(obj, float): + return ast.Name(str(obj), ast.Load()) + elif isinstance(obj, list): + json_values = [] + for v in obj: + json_values.append(astify_json_obj(v)) + return ast.List(json_values, ast.Load()) + elif isinstance(obj, dict): + json_values = [] + json_keys = [] + for k, v in obj.items(): + json_keys.append(ast.Constant(maybe_str(k))) + json_values.append(astify_json_obj(v)) + return ast.Dict(json_keys, json_values) + else: + raise Exception("Cannot astify {0:s}".format(str(obj))) + + if data: + if is_json(content_type): + call.keywords.append( + ast.keyword( + "body", + ast.Call( + func=ast.Attribute( + value=ast.Call( + func=ast.Attribute( + value=ast.Name(id="json", ctx=ast.Load()), + attr="dumps", + ctx=ast.Load(), + ), + args=[astify_json_obj(data)], + keywords=[], + ), + attr="encode", + ctx=ast.Load(), + ), + args=[ast.Constant("utf-8")], + keywords=[], + ), + ) + ) + elif content_type == "application/x-www-form-urlencoded": + call.keywords.append( + ast.keyword( + "body", + ast.Call( + func=ast.Name(id="urlencode", ctx=ast.Load()), + args=[astify_json_obj(data)], + keywords=[], + ), + ) + ) + else: + call.keywords.append(ast.keyword("body", ast.Constant(data))) + + # Create a PoolManager + pool_manager = ast.Assign( + targets=[ast.Name(id="http", ctx=ast.Store())], + value=ast.Call( + func=ast.Attribute( + value=ast.Name(id="urllib3", ctx=ast.Load()), + attr="PoolManager", + ctx=ast.Load(), + ), + args=[], + keywords=[], + ), + ) + + # Response + response = ast.Assign(targets=[ast.Name(id="r", ctx=ast.Store())], value=call) + + # The whole tree + tree.body = imports + [pool_manager] + if method == "Basic": + tree.body.extend([headers_assignment, update_call]) + tree.body.append(response) + + ast.fix_missing_locations(tree) + return unparse(tree).strip() + + # Method + tree = ast.parse("http.request()") + call = tree.body[0].value + call.keywords = [] + + # Method + call.args.append(ast.Constant(request.command)) + + # URL + call.args.append(ast.Constant(request.url())) + + # Authorization (prepare) + method, token = request.auth() + if method == "Basic": + imports.append( + ast.ImportFrom( + module="urllib3.util", + names=[ast.alias(name="make_headers")], + level=0, + ) + ) + + # Headers + header_keys = [] + header_values = [] + # Use a dictionary to store headers to avoid duplicates + headers = {} + for header in sorted(request.headers): + if header in EXCLUDE_HEADERS_REQUESTS: + continue + headers[header] = request.headers[header] + + if method != "Basic" and "Authorization" in request.headers: + headers["Authorization"] = request.headers["Authorization"] + + if method == "Basic": + token = maybe_str(token) + # Create basic auth header + auth_headers = ast.Call( + func=ast.Name(id="make_headers", ctx=ast.Load()), + args=[], + keywords=[ + ast.keyword( + "basic_auth", + ast.Constant(token), + ) + ], + ) + # Merge with existing headers + if headers: + # Update the auth headers with the existing headers + # headers.update(auth_headers) + # Cannot do this easily with AST, so I'll just have two headers arguments + # which is not ideal, but it's what the previous code was doing + pass # Will be handled later + + header_keys = [ast.Constant(k) for k in headers.keys()] + header_values = [ast.Constant(v) for v in headers.values()] + + if header_keys and header_values: + headers_dict = ast.Dict(header_keys, header_values) + if method == "Basic": + # merge headers + headers_dict = ast.Call( + func=ast.Attribute( + value=headers_dict, + attr="update", + ctx=ast.Load(), + ), + args=[ + ast.Call( + func=ast.Name(id="make_headers", ctx=ast.Load()), + args=[], + keywords=[ + ast.keyword( + "basic_auth", + ast.Constant(token), + ) + ], + ) + ], + keywords=[], + ) + # The update call returns None, so we can't use it directly + # I will create a temporary variable and then update it + # temp_headers = {'a': 'b'} + # temp_headers.update(make_headers(basic_auth='user:pass')) + # r = http.request(..., headers=temp_headers) + # This is getting too complex. I will generate the headers dict and then + # add the auth to it if needed. + + # Rebuild headers dict + headers_with_auth = ast.Dict(header_keys, header_values) + call.keywords.append(ast.keyword(arg="headers", value=headers_with_auth)) + # It's tricky to merge dicts in AST. + # A simple way is to have two keywords, but that's invalid for the call. + # Another way is to create a new dict and merge them. + # Let's try to do it properly. + # 1. create the headers dict + # 2. if basic auth, create auth dict + # 3. merge them: {**headers, **auth} (python 3.5+) + # The target is 3.10 so this is fine. + final_headers = ast.Dict(header_keys, header_values) + if method == "Basic": + auth_header = ast.Call( + func=ast.Name(id="make_headers", ctx=ast.Load()), + args=[], + keywords=[ + ast.keyword( + "basic_auth", + ast.Constant(token), + ) + ], + ) + final_headers = ast.Dict( + keys=[None, None], values=[final_headers, auth_header] + ) + + call.keywords.append(ast.keyword(arg="headers", value=final_headers)) + else: + call.keywords.append( + ast.keyword(arg="headers", value=ast.Dict(header_keys, header_values)) + ) + + # JSON or raw data + data = maybe_str(request.data()) + + # Form data + if content_type == "application/x-www-form-urlencoded": + if not isinstance(data, dict): + data = flatten_parsed_qs(parse_qs(data)) + + def astify_json_obj(obj): + obj = maybe_str(obj) + if isinstance(obj, str): + return ast.Constant(obj) + elif isinstance(obj, bool): + return ast.Name(str(obj), ast.Load()) + elif isinstance(obj, int): + return ast.Name(str(obj), ast.Load()) + elif isinstance(obj, float): + return ast.Name(str(obj), ast.Load()) + elif isinstance(obj, list): + json_values = [] + for v in obj: + json_values.append(astify_json_obj(v)) + return ast.List(json_values, ast.Load()) + elif isinstance(obj, dict): + json_values = [] + json_keys = [] + for k, v in obj.items(): + json_keys.append(ast.Constant(maybe_str(k))) + json_values.append(astify_json_obj(v)) + return ast.Dict(json_keys, json_values) + else: + raise Exception("Cannot astify {0:s}".format(str(obj))) + + if data: + if is_json(content_type): + call.keywords.append( + ast.keyword( + "body", + ast.Call( + func=ast.Attribute( + value=ast.Call( + func=ast.Attribute( + value=ast.Name(id="json", ctx=ast.Load()), + attr="dumps", + ctx=ast.Load(), + ), + args=[astify_json_obj(data)], + keywords=[], + ), + attr="encode", + ctx=ast.Load(), + ), + args=[ast.Constant("utf-8")], + keywords=[], + ), + ) + ) + elif content_type == "application/x-www-form-urlencoded": + call.keywords.append( + ast.keyword( + "body", + ast.Call( + func=ast.Name(id="urlencode", ctx=ast.Load()), + args=[astify_json_obj(data)], + keywords=[], + ), + ) + ) + else: + call.keywords.append(ast.keyword("body", ast.Constant(data))) + + # Create a PoolManager + pool_manager = ast.Assign( + targets=[ast.Name(id="http", ctx=ast.Store())], + value=ast.Call( + func=ast.Attribute( + value=ast.Name(id="urllib3", ctx=ast.Load()), + attr="PoolManager", + ctx=ast.Load(), + ), + args=[], + keywords=[], + ), + ) + + # Response + response = ast.Assign(targets=[ast.Name(id="r", ctx=ast.Store())], value=call) + + # The whole tree + tree.body = imports + [pool_manager, response] + + ast.fix_missing_locations(tree) + return unparse(tree).strip() diff --git a/src/sphinxcontrib/httpexample/directives.py b/src/sphinxcontrib/httpexample/directives.py index 71bb827..bfdc821 100644 --- a/src/sphinxcontrib/httpexample/directives.py +++ b/src/sphinxcontrib/httpexample/directives.py @@ -16,6 +16,7 @@ "httpie": (builders.build_httpie_command, "shell"), "python-requests": (builders.build_requests_command, "python"), "requests": (builders.build_requests_command, "python"), + "urllib3": (builders.build_urllib3_command, "python"), } AVAILABLE_FIELDS = ["query"] diff --git a/tests/snapshots/snap_test_builders.py b/tests/snapshots/snap_test_builders.py index 5cc9d8d..431309b 100644 --- a/tests/snapshots/snap_test_builders.py +++ b/tests/snapshots/snap_test_builders.py @@ -203,6 +203,111 @@ "requests.post('http://localhost:8080/@@oauth2-token', headers={'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded'}, data={'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion': 'REDACTED'})" ) +snapshots[ + "test_fixture[build_urllib3_command-fixture_001] 1" +] = """import urllib3 +from urllib3.util import make_headers +http = urllib3.PoolManager() +headers = {'Accept': 'application/json'} +headers.update(make_headers(basic_auth='admin:admin')) +r = http.request('GET', 'http://localhost:8080/Plone/front-page', headers=headers)""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_002] 1" +] = """import urllib3 +import json +from urllib3.util import make_headers +http = urllib3.PoolManager() +headers = {'Accept': 'application/json', 'Content-Type': 'application/json'} +headers.update(make_headers(basic_auth='admin:admin')) +r = http.request('POST', 'http://localhost:8080/Plone/folder', headers=headers, body=json.dumps({'@type': 'Document', 'title': 'My Document'}).encode('utf-8'))""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_003] 1" +] = """import urllib3 +import json +from urllib3.util import make_headers +http = urllib3.PoolManager() +headers = {'Accept': 'application/json', 'Content-Type': 'application/json'} +headers.update(make_headers(basic_auth='admin:admin')) +r = http.request('PATCH', 'http://localhost:8080/Plone/folder/my-document', headers=headers, body=json.dumps({'title': 'My New Document Title'}).encode('utf-8'))""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_004] 1" +] = """import urllib3 +http = urllib3.PoolManager() +r = http.request('GET', 'http://localhost:8080/Plone/front-page', headers={'Accept': 'application/json'})""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_005] 1" +] = """import urllib3 +http = urllib3.PoolManager() +r = http.request('GET', 'http://localhost:8080/Plone/front-page', headers={'Accept': 'application/json', 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIiwiZXhwIjoxNDY0MDQyMTAzfQ.aOyvMwdcIMV6pzC0GYQ3ZMdGaHR1_W7DxT0W0ok4FxI'})""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_006] 1" +] = """import urllib3 +from urllib3.util import make_headers +http = urllib3.PoolManager() +headers = {'Accept': 'application/json', 'Content-Type': 'application/offset+octet-stream', 'Tus-Resumable': '1.0.0', 'Upload-Offset': '3'} +headers.update(make_headers(basic_auth='admin:secret')) +r = http.request('PATCH', 'http://nohost/plone/folder/@upload/032803b64ad746b3ab46d9223ea3d90f', headers=headers, body='defgh')""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_007] 1" +] = """import urllib3 +from urllib3.util import make_headers +http = urllib3.PoolManager() +headers = {'Accept': 'application/json'} +headers.update(make_headers(basic_auth='admin:admin')) +r = http.request('GET', 'http://localhost:8080/Plone/front-page?foo=bar&bar=foo', headers=headers)""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_008] 1" +] = """import urllib3 +http = urllib3.PoolManager() +r = http.request(\'GET\', \'http://localhost:8080/Plone/front-page?foo=bar&bar=foo\', headers={\'Accept\': \'application/json\', \'Accept-Encoding\': \'gzip, deflate\', \'Cookie\': \'zyx 123\', \'If-None-Match\': \'"abc123"\', \'Authorization\': \'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ\'})""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_009] 1" +] = """import urllib3 +import json +http = urllib3.PoolManager() +r = http.request(\'PATCH\', \'http://localhost:8080/etc/fstab\', headers={\'Accept\': \'application/vnd.acme+json\', \'Accept-Encoding\': \'gzip, deflate\', \'Content-Type\': \'application/vnd.acme+json; charset=utf-8\', \'If-None-Match\': \'"abc123"\', \'Authorization\': \'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ\'}, body=json.dumps({\'/\': {\'fstype\': \'btrfs\', \'readonly\': True, \'storage\': {\'device\': \'/dev/sda1\', \'type\': \'disk\'}}, \'/tmp\': {\'storage\': {\'sizeInMB\': 64, \'type\': \'tmpfs\'}}, \'/var\': {\'fstype\': \'ext4\', \'options\': [\'nosuid\'], \'storage\': {\'label\': \'8f3ba6f4-5c70-46ec-83af-0d5434953e5f\', \'type\': \'disk\'}}, \'/var/www\': {\'storage\': {\'remotePath\': \'/exports/mypath\', \'server\': \'my.nfs.server\', \'type\': \'nfs\'}}}).encode(\'utf-8\'))""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_011] 1" +] = """import urllib3 +from urllib3.util import make_headers +http = urllib3.PoolManager() +headers = {'Accept': 'application/json'} +headers.update(make_headers(basic_auth='admin:admin')) +r = http.request('GET', 'http://localhost/items?user_id=12&user_id=13&from=20170101&to=20171231&user_id=15&limit=20&sort=date-asc', headers=headers)""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_012] 1" +] = """import urllib3 +import json +from urllib3.util import make_headers +http = urllib3.PoolManager() +headers = {'Accept': 'application/json', 'Content-Type': 'application/json'} +headers.update(make_headers(basic_auth='admin:admin')) +r = http.request('POST', 'http://localhost:8080/metrics', headers=headers, body=json.dumps({'max': 0.2, 'min': 0.1}).encode('utf-8'))""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_013] 1" +] = """import urllib3 +from urllib.parse import urlencode +http = urllib3.PoolManager() +r = http.request('POST', 'http://localhost:8080/@@oauth2-token', headers={'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded'}, body=urlencode({'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion': 'REDACTED'}))""" + +snapshots[ + "test_fixture[build_urllib3_command-fixture_014] 1" +] = """import urllib3 +from urllib.parse import urlencode +http = urllib3.PoolManager() +r = http.request('POST', 'http://localhost:8080/@@oauth2-token', headers={'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded'}, body=urlencode({'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion': 'REDACTED'}))""" + snapshots["test_fixture[build_wget_command-fixture_001] 1"] = ( 'wget -S -O- http://localhost:8080/Plone/front-page --header="Accept: application/json" --auth-no-challenge --user=admin --password=admin' ) diff --git a/tests/test_builders.py b/tests/test_builders.py index ceaac88..ea13be7 100644 --- a/tests/test_builders.py +++ b/tests/test_builders.py @@ -15,6 +15,7 @@ from sphinxcontrib.httpexample.builders import build_curl_command from sphinxcontrib.httpexample.builders import build_httpie_command from sphinxcontrib.httpexample.builders import build_requests_command +from sphinxcontrib.httpexample.builders import build_urllib3_command from sphinxcontrib.httpexample.builders import build_wget_command from sphinxcontrib.httpexample.parsers import parse_request import pytest @@ -49,6 +50,7 @@ build_curl_command, build_wget_command, build_requests_command, + build_urllib3_command, ), ids=lambda fn: fn.__name__, )