drydock-cli 3.0.97__tar.gz → 3.0.99__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {drydock_cli-3.0.97/drydock_cli.egg-info → drydock_cli-3.0.99}/PKG-INFO +1 -1
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/tools/__init__.py +43 -8
- {drydock_cli-3.0.97 → drydock_cli-3.0.99/drydock_cli.egg-info}/PKG-INFO +1 -1
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock_cli.egg-info/SOURCES.txt +2 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/pyproject.toml +1 -1
- drydock_cli-3.0.99/tests/test_tool_arg_coercion.py +49 -0
- drydock_cli-3.0.99/tests/test_write_content_coerce.py +37 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/LICENSE +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/NOTICE +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/README.md +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/__init__.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/__main__.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/advisor.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/agent.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/bash_safety.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/builtin_skills/__init__.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/builtin_skills/rmf-categorize.md +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/builtin_skills/rmf-control.md +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/builtin_skills/rmf-poam.md +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/builtin_skills/rmf-review.md +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/builtin_skills/stig-assess.md +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/builtin_skills/stig-remediate.md +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/cci.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/cli.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/compaction.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/config.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/detect.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/extract.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/gittools.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/graphrag.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/guards.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/loop_detect.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/mcp.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/poam.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/providers.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/rmf.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/rmf_graph.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/skills.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/stig.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/tool_registry.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/tui/__init__.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/tui/app.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/tui/approval.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/tui/messages.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/tui/widgets.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/tuning.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock/web.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock_cli.egg-info/dependency_links.txt +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock_cli.egg-info/entry_points.txt +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock_cli.egg-info/requires.txt +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/drydock_cli.egg-info/top_level.txt +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/setup.cfg +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_advisor.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_approval.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_back_command.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_background.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_binary_output.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_output_bounding.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_process_group.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_safety.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_sanitize.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_shell.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_stdin.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_timeout_network.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_bash_timeout_param.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_cci.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_cli_agents.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_compact_command.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_compaction.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_config.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_config_migration.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_context_limit_config.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_detect.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_dispatch.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_e2e_connected.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_empty_response.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_extract.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_failure_loop.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_first_run_setup.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_gittools.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_graphify_example.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_graphrag.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_grep_and_read_robust.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_guards_and_tools.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_hallucinated_tools.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_leaked_tool_call.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_loop_detect.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_mcp.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_oneshot_unreachable.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_plan_autocontinue.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_poam.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_providers_unreachable.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_read_index.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_rmf.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_rmf_graph.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_rmf_stig_graph.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_runaway_repetition.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_server_probe.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_skills.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_stig.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_stop.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_streaming_newlines.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_subagent.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_system_prompt_help.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_todo.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_tool_arg_parsing.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_tools_undo.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_tui.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_tuning.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_viewimage.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_vision_input.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_web_tools.py +0 -0
- {drydock_cli-3.0.97 → drydock_cli-3.0.99}/tests/test_xccdf.py +0 -0
|
@@ -26,6 +26,36 @@ _DEFAULT_TIMEOUT = 120
|
|
|
26
26
|
_MAX_TIMEOUT = 1800 # 30 min hard ceiling — a single command shouldn't hang longer
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def _as_text(v, default: str = "") -> str:
|
|
30
|
+
"""Coerce a text-body arg (file content, replacement text) to a string. Local
|
|
31
|
+
models sometimes send it as a JSON array of lines → newline-join; None →
|
|
32
|
+
default; other scalars → str()."""
|
|
33
|
+
if isinstance(v, (list, tuple)):
|
|
34
|
+
return "\n".join(str(x) for x in v)
|
|
35
|
+
if v is None:
|
|
36
|
+
return default
|
|
37
|
+
return v if isinstance(v, str) else str(v)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _as_str_arg(v, default: str = "") -> str:
|
|
41
|
+
"""Coerce a scalar string arg (a path, a pattern) to a string. A single-value
|
|
42
|
+
list is unwrapped (models sometimes wrap a lone path/pattern in an array)."""
|
|
43
|
+
if isinstance(v, (list, tuple)):
|
|
44
|
+
return str(v[0]) if v else default
|
|
45
|
+
if v is None:
|
|
46
|
+
return default
|
|
47
|
+
return v if isinstance(v, str) else str(v)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _coerce_int(value, default: int):
|
|
51
|
+
"""Coerce a model-supplied int arg (offset/limit) that may arrive as a string
|
|
52
|
+
("5") or junk. Falls back to default on anything non-numeric."""
|
|
53
|
+
try:
|
|
54
|
+
return int(float(value))
|
|
55
|
+
except (TypeError, ValueError):
|
|
56
|
+
return default
|
|
57
|
+
|
|
58
|
+
|
|
29
59
|
def _coerce_timeout(value) -> int:
|
|
30
60
|
"""Make the model-supplied timeout robust: local models often send it as a
|
|
31
61
|
string ("10"), and a 0/negative/absurd value would make EVERY command time
|
|
@@ -585,13 +615,13 @@ def tool_viewimage(params: dict, config: dict) -> str:
|
|
|
585
615
|
|
|
586
616
|
|
|
587
617
|
def tool_read(params: dict, config: dict) -> str:
|
|
588
|
-
fp = _resolve_path(params
|
|
618
|
+
fp = _resolve_path(_as_str_arg(params.get("file_path")), config)
|
|
589
619
|
# Reading an image as text yields binary garbage — point at the vision tool.
|
|
590
620
|
if os.path.splitext(fp)[1].lower() in _IMAGE_EXTS and os.path.isfile(fp):
|
|
591
621
|
return (f"{params['file_path']} is an image — Read would return binary "
|
|
592
622
|
"garbage. Use the ViewImage tool to actually SEE it.")
|
|
593
|
-
limit = params.get("limit")
|
|
594
|
-
offset = params.get("offset", 0)
|
|
623
|
+
limit = None if params.get("limit") is None else _coerce_int(params.get("limit"), 2000)
|
|
624
|
+
offset = _coerce_int(params.get("offset", 0), 0)
|
|
595
625
|
try:
|
|
596
626
|
with open(fp, "r", encoding="utf-8", errors="replace") as f:
|
|
597
627
|
lines = f.readlines()
|
|
@@ -670,7 +700,10 @@ def tool_write(params: dict, config: dict) -> str:
|
|
|
670
700
|
fp = _resolve_path(str(fp).strip(), config)
|
|
671
701
|
if Path(fp).is_dir():
|
|
672
702
|
return f"Error: {fp} is a directory, not a file. Pass a file path."
|
|
673
|
-
content
|
|
703
|
+
# Local models sometimes send content as a JSON array of lines (or a number),
|
|
704
|
+
# which f.write() can't take — coerce instead of crashing (tools must return
|
|
705
|
+
# errors, never raise). A list → newline-joined lines.
|
|
706
|
+
content = _as_text(params.get("content", ""))
|
|
674
707
|
if has_conflict_markers(content):
|
|
675
708
|
return conflict_marker_refusal(fp)
|
|
676
709
|
prev = _snapshot(fp)
|
|
@@ -793,8 +826,10 @@ def tool_edit(params: dict, config: dict) -> str:
|
|
|
793
826
|
if not fp or not str(fp).strip():
|
|
794
827
|
return "Error: Edit needs a real file_path (the path was empty or blank)."
|
|
795
828
|
fp = _resolve_path(str(fp).strip(), config)
|
|
796
|
-
old
|
|
797
|
-
|
|
829
|
+
# Coerce non-string old/new (a JSON array of lines, a number) instead of
|
|
830
|
+
# crashing with a TypeError in the substring search / replace.
|
|
831
|
+
old = _as_text(params.get("old_string", ""))
|
|
832
|
+
new = _as_text(params.get("new_string", ""))
|
|
798
833
|
if has_conflict_markers(new):
|
|
799
834
|
return conflict_marker_refusal(fp)
|
|
800
835
|
# If a directory was passed, try to infer the intended file (avoids a
|
|
@@ -1098,8 +1133,8 @@ def tool_glob(params: dict, config: dict) -> str:
|
|
|
1098
1133
|
|
|
1099
1134
|
|
|
1100
1135
|
def tool_grep(params: dict, config: dict) -> str:
|
|
1101
|
-
pattern = params
|
|
1102
|
-
path = params.get("path") or config.get("cwd") or "."
|
|
1136
|
+
pattern = _as_str_arg(params.get("pattern"))
|
|
1137
|
+
path = _as_str_arg(params.get("path")) or config.get("cwd") or "."
|
|
1103
1138
|
include = params.get("include", "")
|
|
1104
1139
|
try:
|
|
1105
1140
|
cmd = ["grep", "-rn", "--color=never"]
|
|
@@ -99,6 +99,7 @@ tests/test_streaming_newlines.py
|
|
|
99
99
|
tests/test_subagent.py
|
|
100
100
|
tests/test_system_prompt_help.py
|
|
101
101
|
tests/test_todo.py
|
|
102
|
+
tests/test_tool_arg_coercion.py
|
|
102
103
|
tests/test_tool_arg_parsing.py
|
|
103
104
|
tests/test_tools_undo.py
|
|
104
105
|
tests/test_tui.py
|
|
@@ -106,4 +107,5 @@ tests/test_tuning.py
|
|
|
106
107
|
tests/test_viewimage.py
|
|
107
108
|
tests/test_vision_input.py
|
|
108
109
|
tests/test_web_tools.py
|
|
110
|
+
tests/test_write_content_coerce.py
|
|
109
111
|
tests/test_xccdf.py
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
# PyPI distribution name is drydock-cli (the established install name, continued
|
|
8
8
|
# from the retired v2 fork); the import package + CLI command stay `drydock`.
|
|
9
9
|
name = "drydock-cli"
|
|
10
|
-
version = "3.0.
|
|
10
|
+
version = "3.0.99"
|
|
11
11
|
description = "Drydock — a local, provider-agnostic terminal coding agent for local LLMs"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.11"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Model-supplied tool args that arrive as the wrong type (a JSON array of lines,
|
|
2
|
+
a number, a stringified int, a single-element-list-wrapped path) must be coerced,
|
|
3
|
+
never crash — tools return results, never raise. Systematic across the file tools."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from drydock.tools import (
|
|
7
|
+
_as_text, _as_str_arg, _coerce_int,
|
|
8
|
+
tool_edit, tool_read, tool_grep, tool_write,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_helpers():
|
|
13
|
+
assert _as_text(["a", "b"]) == "a\nb"
|
|
14
|
+
assert _as_text(None) == "" and _as_text(42) == "42" and _as_text("x") == "x"
|
|
15
|
+
assert _as_str_arg(["/p"]) == "/p" and _as_str_arg(None) == "" and _as_str_arg(5) == "5"
|
|
16
|
+
assert _coerce_int("5", 0) == 5 and _coerce_int("junk", 7) == 7 and _coerce_int(None, 3) == 3
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _cfg(tmp): return {"cwd": str(tmp)}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_edit_non_string_new(tmp_path):
|
|
23
|
+
f = tmp_path / "e.txt"; f.write_text("alpha beta\n")
|
|
24
|
+
out = tool_edit({"file_path": str(f), "old_string": "alpha", "new_string": ["x", "y"]}, _cfg(tmp_path))
|
|
25
|
+
assert "Error" not in out and f.read_text().startswith("x\ny")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_edit_int_new(tmp_path):
|
|
29
|
+
f = tmp_path / "e.txt"; f.write_text("beta\n")
|
|
30
|
+
out = tool_edit({"file_path": str(f), "old_string": "beta", "new_string": 99}, _cfg(tmp_path))
|
|
31
|
+
assert "99" in f.read_text() and "Error" not in out
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_read_list_path_and_str_offsets(tmp_path):
|
|
35
|
+
f = tmp_path / "r.txt"; f.write_text("line1\nline2\n")
|
|
36
|
+
out = tool_read({"file_path": [str(f)], "offset": "0", "limit": "5"}, _cfg(tmp_path))
|
|
37
|
+
assert "line1" in out
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_grep_list_pattern(tmp_path):
|
|
41
|
+
f = tmp_path / "g.txt"; f.write_text("needle here\n")
|
|
42
|
+
out = tool_grep({"pattern": ["needle"], "path": str(f)}, _cfg(tmp_path))
|
|
43
|
+
assert "needle" in out
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_write_list_content(tmp_path):
|
|
47
|
+
f = tmp_path / "w.txt"
|
|
48
|
+
tool_write({"file_path": str(f), "content": ["a", "b"]}, _cfg(tmp_path))
|
|
49
|
+
assert f.read_text() == "a\nb"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""tool_write must not raise on non-string content. Local models sometimes send
|
|
2
|
+
`content` as a JSON array of lines (or a number); f.write() would TypeError, and
|
|
3
|
+
tools must return a result, never raise. A list → newline-joined lines."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from drydock.tools import tool_write
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _write(tmp_path, content):
|
|
10
|
+
fp = tmp_path / "out.txt"
|
|
11
|
+
out = tool_write({"file_path": str(fp), "content": content}, {"cwd": str(tmp_path)})
|
|
12
|
+
return out, fp.read_text()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_list_content_joined(tmp_path):
|
|
16
|
+
out, text = _write(tmp_path, ["line1", "line2", "line3"])
|
|
17
|
+
assert "Error" not in out and text == "line1\nline2\nline3"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_int_content_stringified(tmp_path):
|
|
21
|
+
out, text = _write(tmp_path, 42)
|
|
22
|
+
assert "Error" not in out and text == "42"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_none_content_empty(tmp_path):
|
|
26
|
+
out, text = _write(tmp_path, None)
|
|
27
|
+
assert "Error" not in out and text == ""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_normal_string_unchanged(tmp_path):
|
|
31
|
+
out, text = _write(tmp_path, "hello\nworld")
|
|
32
|
+
assert text == "hello\nworld"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_list_of_numbers(tmp_path):
|
|
36
|
+
out, text = _write(tmp_path, [1, 2, 3])
|
|
37
|
+
assert text == "1\n2\n3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|