Skip to content

Commit

Permalink
feat: change requests to httpx (#557)
Browse files Browse the repository at this point in the history
  • Loading branch information
taconi committed May 4, 2023
1 parent 544443b commit c208df5
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 34 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Changed
- Change requests to httpx. [#208](https://github.com/scanapi/scanapi/issues/208)

## [2.9.0] - 2023-03-16
### Feature
Expand Down
107 changes: 101 additions & 6 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ scanapi = "scanapi:main"

[tool.poetry.dependencies]
python = ">=3.7.13,<4.0.0"
requests = "2.28.2"
appdirs = "^1.4.4"
curlify2 = "^1.0.1"
MarkupSafe = "2.0.1"
rich = "12.2.0"
PyYAML = "~6.0"
Jinja2 = "~3.1.0"
click = "8.0.3"
httpx = "^0.24.0"

[tool.poetry.dev-dependencies]
pytest = "7.2.2"
Expand Down
4 changes: 2 additions & 2 deletions scanapi/tree/endpoint_node.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
from itertools import chain

from requests import RequestException
from httpx import CookieConflict, HTTPError, InvalidURL, StreamError

from scanapi.errors import InvalidKeyError
from scanapi.evaluators import SpecEvaluator
Expand Down Expand Up @@ -171,7 +171,7 @@ def run(self):
for request in self._get_requests():
try:
yield request.run()
except RequestException as e:
except (CookieConflict, HTTPError, InvalidURL, StreamError) as e:
error_message = (
f"\nError to make request `{request.full_url_path}`. "
f"\n{str(e)}\n"
Expand Down
10 changes: 6 additions & 4 deletions scanapi/tree/request_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,19 +155,21 @@ def run(self):
url = self.full_url_path
console.print(f"\n- Making request {method} {url}", highlight=False)

options = self.options
verify = options.pop("verify", True)
kwargs = dict(
headers=self.headers,
params=self.params,
json=self.body,
allow_redirects=False,
**self.options,
follow_redirects=False,
**options,
)

if not self._content_type_is_json(kwargs["headers"]):
kwargs["data"] = kwargs.pop("json")

session = session_with_retry(self.retry)
response = session.request(method, url, **kwargs)
with session_with_retry(self.retry, verify) as session:
response = session.request(method, url, **kwargs)

extras = dict(self.endpoint.spec_vars)
extras["response"] = response
Expand Down
32 changes: 16 additions & 16 deletions scanapi/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from httpx import Client, HTTPTransport

from scanapi.errors import InvalidKeyError, MissingMandatoryKeyError
from scanapi.tree.tree_keys import MAX_RETRIES_KEY
Expand Down Expand Up @@ -58,19 +56,21 @@ def _validate_required_keys(keys, required_keys, scope):
raise MissingMandatoryKeyError(missing_keys, scope)


def session_with_retry(retry_configuration):
"""Instantiate a requests session with the retry configuration if provided
by instantiating an HTTPAdapter mounting it into the mounting it into the
`requests.Session`.
"""
session = requests.Session()
def session_with_retry(retry_configuration, verify=True):
"""Instantiate a requests session.
if not retry_configuration:
return session
Args:
retry_configuration [dict]: The retry configuration
for a request. (Available for version >= 2.2.0).
verify [bool]: SSL certificates used to verify the
identity of requested hosts
retry = Retry(total=retry_configuration.get(MAX_RETRIES_KEY, 0))
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)
Returns:
[httpx.Client]: Client
"""
retry_configuration = retry_configuration or {}
retries = retry_configuration.get(MAX_RETRIES_KEY, 0)

return session
return Client(
transport=HTTPTransport(retries=retries), timeout=None, verify=verify
)
6 changes: 2 additions & 4 deletions tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,11 @@ class TestSessionWithRetry:
def test_should_not_mount_custom_adapters(self):
session = session_with_retry({})

assert session.adapters["http://"].max_retries.total == 0
assert session.adapters["https://"].max_retries.total == 0
assert session._transport._pool._retries == 0

@mark.context("there is a retry configuration")
@mark.it("should mount custom adapters")
def test_should_mount_custom_adapters(self):
session = session_with_retry({"max_retries": 7})

assert session.adapters["http://"].max_retries.total == 7
assert session.adapters["https://"].max_retries.total == 7
assert session._transport._pool._retries == 7
2 changes: 1 addition & 1 deletion tests/unit/tree/request_node/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_calls_request(self, mock_session, mock_time_sleep):
headers=request.headers,
params=request.params,
json=request.body,
allow_redirects=False,
follow_redirects=False,
verify=False,
timeout=2.3,
)
Expand Down

0 comments on commit c208df5

Please sign in to comment.