Skip to content

Commit 4290d88

Browse files
Centralize file-path missing/not-file message formatting
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 571a247 commit 4290d88

6 files changed

Lines changed: 105 additions & 33 deletions

File tree

hyperbrowser/client/file_utils.py

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,34 @@
1010
_DEFAULT_OPEN_ERROR_MESSAGE_PREFIX = "Failed to open file at path"
1111

1212

13+
def _normalize_error_prefix(prefix: object, *, default_prefix: str) -> str:
14+
normalized_default_prefix = default_prefix
15+
if not is_plain_string(normalized_default_prefix):
16+
normalized_default_prefix = _DEFAULT_OPEN_ERROR_MESSAGE_PREFIX
17+
else:
18+
try:
19+
stripped_default_prefix = normalized_default_prefix.strip()
20+
except Exception:
21+
stripped_default_prefix = _DEFAULT_OPEN_ERROR_MESSAGE_PREFIX
22+
if not is_plain_string(stripped_default_prefix) or not stripped_default_prefix:
23+
normalized_default_prefix = _DEFAULT_OPEN_ERROR_MESSAGE_PREFIX
24+
else:
25+
normalized_default_prefix = stripped_default_prefix
26+
if not is_plain_string(prefix):
27+
return normalized_default_prefix
28+
try:
29+
sanitized_prefix = "".join(
30+
"?" if ord(character) < 32 or ord(character) == 127 else character
31+
for character in prefix
32+
)
33+
stripped_prefix = sanitized_prefix.strip()
34+
except Exception:
35+
stripped_prefix = normalized_default_prefix
36+
if not is_plain_string(stripped_prefix) or not stripped_prefix:
37+
return normalized_default_prefix
38+
return stripped_prefix
39+
40+
1341
def format_file_path_for_error(
1442
file_path: object,
1543
*,
@@ -44,27 +72,25 @@ def format_file_path_for_error(
4472
return f"{sanitized_path[:truncated_length]}..."
4573

4674

47-
def build_open_file_error_message(file_path: object, *, prefix: str) -> str:
48-
normalized_prefix = prefix
49-
if not is_plain_string(normalized_prefix):
50-
normalized_prefix = _DEFAULT_OPEN_ERROR_MESSAGE_PREFIX
51-
else:
52-
try:
53-
sanitized_prefix = "".join(
54-
"?" if ord(character) < 32 or ord(character) == 127 else character
55-
for character in normalized_prefix
56-
)
57-
stripped_prefix = sanitized_prefix.strip()
58-
except Exception:
59-
stripped_prefix = _DEFAULT_OPEN_ERROR_MESSAGE_PREFIX
60-
if not is_plain_string(stripped_prefix) or not stripped_prefix:
61-
normalized_prefix = _DEFAULT_OPEN_ERROR_MESSAGE_PREFIX
62-
else:
63-
normalized_prefix = stripped_prefix
75+
def build_file_path_error_message(
76+
file_path: object,
77+
*,
78+
prefix: str,
79+
default_prefix: str,
80+
) -> str:
81+
normalized_prefix = _normalize_error_prefix(prefix, default_prefix=default_prefix)
6482
file_path_display = format_file_path_for_error(file_path)
6583
return f"{normalized_prefix}: {file_path_display}"
6684

6785

86+
def build_open_file_error_message(file_path: object, *, prefix: str) -> str:
87+
return build_file_path_error_message(
88+
file_path,
89+
prefix=prefix,
90+
default_prefix=_DEFAULT_OPEN_ERROR_MESSAGE_PREFIX,
91+
)
92+
93+
6894
def _normalize_file_path_text(file_path: Union[str, PathLike[str]]) -> str:
6995
try:
7096
normalized_path = os.fspath(file_path)

hyperbrowser/client/managers/extension_create_utils.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from hyperbrowser.exceptions import HyperbrowserError
44
from hyperbrowser.models.extension import CreateExtensionParams
55

6-
from ..file_utils import ensure_existing_file_path, format_file_path_for_error
6+
from ..file_utils import build_file_path_error_message, ensure_existing_file_path
77
from .serialization_utils import serialize_model_dump_to_dict
88

99

@@ -26,10 +26,17 @@ def normalize_extension_create_input(params: Any) -> Tuple[str, Dict[str, Any]]:
2626
)
2727
payload.pop("filePath", None)
2828

29-
file_path_display = format_file_path_for_error(raw_file_path)
3029
file_path = ensure_existing_file_path(
3130
raw_file_path,
32-
missing_file_message=f"Extension file not found at path: {file_path_display}",
33-
not_file_message=f"Extension file path must point to a file: {file_path_display}",
31+
missing_file_message=build_file_path_error_message(
32+
raw_file_path,
33+
prefix="Extension file not found at path",
34+
default_prefix="Extension file not found at path",
35+
),
36+
not_file_message=build_file_path_error_message(
37+
raw_file_path,
38+
prefix="Extension file path must point to a file",
39+
default_prefix="Extension file path must point to a file",
40+
),
3441
)
3542
return file_path, payload

hyperbrowser/client/managers/session_upload_utils.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
from hyperbrowser.type_utils import is_plain_string, is_string_subclass_instance
88

99
from ..file_utils import (
10+
build_file_path_error_message,
1011
build_open_file_error_message,
1112
ensure_existing_file_path,
12-
format_file_path_for_error,
1313
open_binary_file,
1414
)
1515

@@ -27,11 +27,18 @@ def normalize_upload_file_input(
2727
"file_input path is invalid",
2828
original_error=exc,
2929
) from exc
30-
file_path_display = format_file_path_for_error(raw_file_path)
3130
file_path = ensure_existing_file_path(
3231
raw_file_path,
33-
missing_file_message=f"Upload file not found at path: {file_path_display}",
34-
not_file_message=f"Upload file path must point to a file: {file_path_display}",
32+
missing_file_message=build_file_path_error_message(
33+
raw_file_path,
34+
prefix="Upload file not found at path",
35+
default_prefix="Upload file not found at path",
36+
),
37+
not_file_message=build_file_path_error_message(
38+
raw_file_path,
39+
prefix="Upload file path must point to a file",
40+
default_prefix="Upload file path must point to a file",
41+
),
3542
)
3643
return file_path, None
3744

tests/test_file_path_display_helper_import_boundary.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,34 @@
66
pytestmark = pytest.mark.architecture
77

88

9-
EXPECTED_FORMAT_FILE_PATH_IMPORTERS = (
9+
EXPECTED_FILE_PATH_ERROR_MESSAGE_IMPORTERS = (
1010
"hyperbrowser/client/managers/extension_create_utils.py",
1111
"hyperbrowser/client/managers/session_upload_utils.py",
1212
"tests/test_file_utils.py",
1313
)
1414

1515

16-
def _imports_format_file_path_for_error(module_text: str) -> bool:
16+
def _imports_build_file_path_error_message(module_text: str) -> bool:
1717
module_ast = ast.parse(module_text)
1818
for node in module_ast.body:
1919
if not isinstance(node, ast.ImportFrom):
2020
continue
21-
if any(alias.name == "format_file_path_for_error" for alias in node.names):
21+
if any(alias.name == "build_file_path_error_message" for alias in node.names):
2222
return True
2323
return False
2424

2525

26-
def test_format_file_path_for_error_imports_are_centralized():
26+
def test_build_file_path_error_message_imports_are_centralized():
2727
discovered_modules: list[str] = []
2828

2929
for module_path in sorted(Path("hyperbrowser").rglob("*.py")):
3030
module_text = module_path.read_text(encoding="utf-8")
31-
if _imports_format_file_path_for_error(module_text):
31+
if _imports_build_file_path_error_message(module_text):
3232
discovered_modules.append(module_path.as_posix())
3333

3434
for module_path in sorted(Path("tests").glob("test_*.py")):
3535
module_text = module_path.read_text(encoding="utf-8")
36-
if _imports_format_file_path_for_error(module_text):
36+
if _imports_build_file_path_error_message(module_text):
3737
discovered_modules.append(module_path.as_posix())
3838

39-
assert discovered_modules == list(EXPECTED_FORMAT_FILE_PATH_IMPORTERS)
39+
assert discovered_modules == list(EXPECTED_FILE_PATH_ERROR_MESSAGE_IMPORTERS)

tests/test_file_path_display_helper_usage.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
def test_file_path_error_messages_use_shared_display_helper():
1515
for module_path in FILE_PATH_DISPLAY_MODULES:
1616
module_text = Path(module_path).read_text(encoding="utf-8")
17-
assert "format_file_path_for_error(" in module_text
17+
assert "build_file_path_error_message(" in module_text
18+
assert "format_file_path_for_error(" not in module_text
1819
assert "ord(character) < 32" not in module_text

tests/test_file_utils.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import hyperbrowser.client.file_utils as file_utils
66
from hyperbrowser.client.file_utils import (
7+
build_file_path_error_message,
78
build_open_file_error_message,
89
ensure_existing_file_path,
910
format_file_path_for_error,
@@ -331,6 +332,36 @@ def test_build_open_file_error_message_uses_prefix_and_sanitized_path():
331332
assert message == "Failed to open upload file at path: bad?path.txt"
332333

333334

335+
def test_build_file_path_error_message_uses_prefix_and_sanitized_path():
336+
message = build_file_path_error_message(
337+
"bad\tpath.txt",
338+
prefix="Upload file not found at path",
339+
default_prefix="Upload file not found at path",
340+
)
341+
342+
assert message == "Upload file not found at path: bad?path.txt"
343+
344+
345+
def test_build_file_path_error_message_uses_default_for_non_string_prefix():
346+
message = build_file_path_error_message(
347+
"/tmp/path.txt",
348+
prefix=123, # type: ignore[arg-type]
349+
default_prefix="Upload file not found at path",
350+
)
351+
352+
assert message == "Upload file not found at path: /tmp/path.txt"
353+
354+
355+
def test_build_file_path_error_message_uses_open_default_when_default_prefix_invalid():
356+
message = build_file_path_error_message(
357+
"/tmp/path.txt",
358+
prefix=123, # type: ignore[arg-type]
359+
default_prefix=" ",
360+
)
361+
362+
assert message == "Failed to open file at path: /tmp/path.txt"
363+
364+
334365
def test_build_open_file_error_message_uses_default_prefix_for_non_string():
335366
message = build_open_file_error_message(
336367
"/tmp/path.txt",

0 commit comments

Comments
 (0)