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

Use unasync for tests #3520

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions httpx/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,7 @@ def _send_single_request(self, request: Request) -> Response:
transport = self._transport_for_url(request.url)
start = time.perf_counter()

if not isinstance(request.stream, SyncByteStream):
if not isinstance(request.stream, SyncByteStream): # pragma: no cover
raise RuntimeError(
"Attempted to send an async request with a sync Client instance."
)
Expand Down Expand Up @@ -1721,7 +1721,7 @@ async def _send_single_request(self, request: Request) -> Response:
transport = self._transport_for_url(request.url)
start = time.perf_counter()

if not isinstance(request.stream, AsyncByteStream):
if not isinstance(request.stream, AsyncByteStream): # pragma: no cover
raise RuntimeError(
"Attempted to send a sync request with an AsyncClient instance."
)
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ text = "\n---\n\n[Full changelog](https://github.com/encode/httpx/blob/master/CH
pattern = 'src="(docs/img/.*?)"'
replacement = 'src="https://raw.githubusercontent.com/encode/httpx/master/\1"'

[tool.ruff]
exclude = ["tests/client/sync"]

[tool.ruff.lint]
select = ["E", "F", "I", "B", "PIE"]
ignore = ["B904", "B028"]
Expand Down Expand Up @@ -129,5 +132,5 @@ markers = [
]

[tool.coverage.run]
omit = ["venv/*"]
omit = ["venv/*", "tests/client/sync/*"]
include = ["httpx/*", "tests/*"]
1 change: 1 addition & 0 deletions scripts/check
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ set -x
${PREFIX}ruff format $SOURCE_FILES --diff
${PREFIX}mypy $SOURCE_FILES
${PREFIX}ruff check $SOURCE_FILES
${PREFIX}python scripts/unasync.py --check
1 change: 1 addition & 0 deletions scripts/lint
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ set -x

${PREFIX}ruff check --fix $SOURCE_FILES
${PREFIX}ruff format $SOURCE_FILES
${PREFIX}python scripts/unasync.py
92 changes: 92 additions & 0 deletions scripts/unasync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!venv/bin/python
import os
import re
import sys
from pprint import pprint

SUBS = [
# httpx specific
("AsyncByteStream", "SyncByteStream"),
("async_auth_flow", "sync_auth_flow"),
("handle_async_request", "handle_request"),
# general
("AsyncIterator", "Iterator"),
("from anyio import Lock", "from threading import Lock"),
("Async([A-Z][A-Za-z0-9_]*)", r"\2"),
("async def", "def"),
("async with", "with"),
("async for", "for"),
("await ", ""),
("aclose", "close"),
("aread", "read"),
("__aenter__", "__enter__"),
("__aexit__", "__exit__"),
("__aiter__", "__iter__"),
("@pytest.mark.anyio", ""),
]
COMPILED_SUBS = [
(re.compile(r"(^|\b)" + regex + r"($|\b)"), repl) for regex, repl in SUBS
]

USED_SUBS = set()


def unasync_line(line):
for index, (regex, repl) in enumerate(COMPILED_SUBS):
old_line = line
line = re.sub(regex, repl, line)
if old_line != line:
USED_SUBS.add(index)
return line


def unasync_file(in_path, out_path):
with open(in_path, "r") as in_file:
with open(out_path, "w", newline="") as out_file:
for line in in_file.readlines():
line = unasync_line(line)
out_file.write(line)


def unasync_file_check(in_path, out_path):
with open(in_path, "r") as in_file:
with open(out_path, "r") as out_file:
for in_line, out_line in zip(in_file.readlines(), out_file.readlines()):
expected = unasync_line(in_line)
if out_line != expected:
print(f"unasync mismatch between {in_path!r} and {out_path!r}")
print(f"Async code: {in_line!r}")
print(f"Expected sync code: {expected!r}")
print(f"Actual sync code: {out_line!r}")
sys.exit(1)


def unasync_dir(in_dir, out_dir, check_only=False):
for dirpath, dirnames, filenames in os.walk(in_dir):
for filename in filenames:
if not filename.endswith(".py"):
continue
rel_dir = os.path.relpath(dirpath, in_dir)
in_path = os.path.normpath(os.path.join(in_dir, rel_dir, filename))
out_path = os.path.normpath(os.path.join(out_dir, rel_dir, filename))
print(in_path, "->", out_path)
if check_only:
unasync_file_check(in_path, out_path)
else:
unasync_file(in_path, out_path)


def main():
check_only = "--check" in sys.argv
unasync_dir("tests/client/async", "tests/client/sync", check_only=check_only)

if len(USED_SUBS) != len(SUBS):
unused_subs = [SUBS[i] for i in range(len(SUBS)) if i not in USED_SUBS]

print("These patterns were not used:")
pprint(unused_subs)
exit(1)


if __name__ == "__main__":
main()
Empty file added tests/client/async/__init__.py
Empty file.
Loading