Skip to content

Commit

Permalink
5.7.10 - Prefer 'cat' for async reads
Browse files Browse the repository at this point in the history
  • Loading branch information
vkottler committed Nov 2, 2024
1 parent 404a397 commit 71edea3
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:
- run: |
mk python-release owner=vkottler \
repo=runtimepy version=5.7.9
repo=runtimepy version=5.7.10
if: |
matrix.python-version == '3.12'
&& matrix.system == 'ubuntu-latest'
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
=====================================
generator=datazen
version=3.1.4
hash=9fbafffa1da655b349a05dfa48addf0b
hash=1e8298e9f6423ee6f9836f5ec04cc3ab
=====================================
-->

# runtimepy ([5.7.9](https://pypi.org/project/runtimepy/))
# runtimepy ([5.7.10](https://pypi.org/project/runtimepy/))

[![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)
Expand Down
2 changes: 1 addition & 1 deletion local/variables/package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
major: 5
minor: 7
patch: 9
patch: 10
entry: runtimepy
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"

[project]
name = "runtimepy"
version = "5.7.9"
version = "5.7.10"
description = "A framework for implementing Python services."
readme = "README.md"
requires-python = ">=3.12"
Expand Down
4 changes: 2 additions & 2 deletions runtimepy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.4
# hash=5d30f53776c61076df3ffeca2fdb30a1
# hash=01934a9c90fba4488654f035acc8b84e
# =====================================

"""
Expand All @@ -10,7 +10,7 @@

DESCRIPTION = "A framework for implementing Python services."
PKG_NAME = "runtimepy"
VERSION = "5.7.9"
VERSION = "5.7.10"

# runtimepy-specific content.
METRICS_NAME = "metrics"
Expand Down
13 changes: 5 additions & 8 deletions runtimepy/net/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from typing import Any, Optional, TextIO, Union

# third-party
import aiofiles
from vcorelib import DEFAULT_ENCODING
from vcorelib.io import IndentedFileWriter, JsonObject
from vcorelib.paths import Pathlike, find_file, normalize
Expand All @@ -26,7 +25,7 @@
from runtimepy.net.server.html import HtmlApp, HtmlApps, get_html, html_handler
from runtimepy.net.server.json import encode_json, json_handler
from runtimepy.net.tcp.http import HttpConnection
from runtimepy.util import normalize_root, path_has_part
from runtimepy.util import normalize_root, path_has_part, read_binary

MIMETYPES_INIT = False

Expand Down Expand Up @@ -158,10 +157,9 @@ async def render_markdown_file(
) -> bytes:
"""Render a markdown file as HTML and return the result."""

async with aiofiles.open(path, mode="r") as path_fd:
return self.render_markdown(
await path_fd.read(), response, **kwargs
)
return self.render_markdown(
(await read_binary(path)).decode(), response, **kwargs
)

async def try_file(
self, path: PathMaybeQuery, response: ResponseHeader
Expand Down Expand Up @@ -195,8 +193,7 @@ async def try_file(
self.logger.info("Serving '%s' (MIME: %s)", candidate, mime)

# Return the file data.
async with aiofiles.open(candidate, mode="rb") as path_fd:
result = await path_fd.read()
result = await read_binary(candidate)

break

Expand Down
27 changes: 26 additions & 1 deletion runtimepy/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,39 @@
"""

# built-in
import asyncio
from os import sep
from pathlib import Path
from typing import Iterator, Union
from shutil import which
from typing import Iterator, Optional, Union

# third-party
import aiofiles
from vcorelib.paths import normalize

ROOT_PATH = Path(sep)
USE_CAT: Optional[bool] = None


async def read_binary(path: Path, use_aiofiles: bool = False) -> bytes:
"""An async wrapper for reading file contents."""

global USE_CAT # pylint: disable=global-statement
if USE_CAT is None:
USE_CAT = which("cat") is not None

# Avoid ballooning a thread pool with one-off reads.
if USE_CAT and not use_aiofiles:
proc = await asyncio.create_subprocess_exec(
"cat", str(path), stdout=asyncio.subprocess.PIPE
)
result, _ = await proc.communicate()

else:
async with aiofiles.open(path, mode="rb") as path_fd:
result = await path_fd.read()

return result


def normalize_root(*src_parts: Union[str, Path]) -> Path:
Expand Down
20 changes: 20 additions & 0 deletions tests/test_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
Test the 'util' module.
"""

# third-party
from pytest import mark

# module under test
from runtimepy.util import read_binary

# internal
from tests.resources import resource


@mark.asyncio
async def test_read_binary():
"""Test 'read_binary' invocations."""

assert await read_binary(resource("test.txt"))
assert await read_binary(resource("test.txt"), use_aiofiles=True)

0 comments on commit 71edea3

Please sign in to comment.