Skip to content

Commit bb4b8d1

Browse files
committed
fix: use absolute paths for fail file placement
Fail files are now correctly placed in the same directory as the input file, even when running from different working directories. - Use Path.resolve() to get absolute path of input file - Apply fix in importer.py for main fail file output - Apply fix in writer.py for relational failures - Add test to verify fail file uses absolute path
1 parent fcb4bc5 commit bb4b8d1

File tree

3 files changed

+66
-4
lines changed

3 files changed

+66
-4
lines changed

src/odoo_data_flow/importer.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ def run_import( # noqa: C901
151151

152152
file_to_process = filename
153153
if fail:
154-
fail_path = Path(filename).parent / _get_fail_filename(model, False)
154+
# Use absolute path to find fail file in same directory as input file
155+
fail_path = Path(filename).resolve().parent / _get_fail_filename(model, False)
155156
line_count = _count_lines(str(fail_path))
156157
if line_count <= 1:
157158
Console().print(
@@ -211,7 +212,10 @@ def run_import( # noqa: C901
211212

212213
final_deferred = deferred_fields or import_plan.get("deferred_fields", [])
213214
final_uid_field = unique_id_field or import_plan.get("unique_id_field") or "id"
214-
fail_output_file = str(Path(filename).parent / _get_fail_filename(model, fail))
215+
# Use absolute path to ensure fail file goes to same directory as input file
216+
# even when running from different working directories
217+
input_file_dir = Path(filename).resolve().parent
218+
fail_output_file = str(input_file_dir / _get_fail_filename(model, fail))
215219

216220
if fail:
217221
log.info("Single-record batching enabled for this import strategy.")

src/odoo_data_flow/lib/writer.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ def write_relational_failures_to_csv(
2424
if not failed_records:
2525
return
2626

27-
fail_filename = f"{Path(original_filename).stem}_relations_fail.csv"
28-
fail_filepath = Path(original_filename).parent / fail_filename
27+
# Use absolute path to ensure fail file goes to same directory as input file
28+
# even when running from different working directories
29+
original_path = Path(original_filename).resolve()
30+
fail_filename = f"{original_path.stem}_relations_fail.csv"
31+
fail_filepath = original_path.parent / fail_filename
2932

3033
try:
3134
file_exists = fail_filepath.exists()

tests/test_importer.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,61 @@ def test_get_fail_filename_recovery_mode(self) -> None:
3838
assert any(char.isdigit() for char in filename)
3939

4040

41+
class TestFailFilePath:
42+
"""Tests for fail file path resolution."""
43+
44+
@patch("odoo_data_flow.importer.import_threaded.import_data")
45+
@patch("odoo_data_flow.importer._run_preflight_checks")
46+
def test_fail_file_uses_absolute_path(
47+
self,
48+
mock_preflight: MagicMock,
49+
mock_import_data: MagicMock,
50+
tmp_path: Path,
51+
) -> None:
52+
"""Test that fail file path is resolved to absolute path."""
53+
# Create a subdirectory to simulate environment folder
54+
env_dir = tmp_path / "data" / "uat"
55+
env_dir.mkdir(parents=True)
56+
source_file = env_dir / "res_partner.csv"
57+
source_file.write_text("id,name\n1,Test\n")
58+
59+
mock_preflight.return_value = True
60+
mock_import_data.return_value = (True, {"total_records": 1})
61+
62+
# Run import with relative-style path
63+
run_import(
64+
config="dummy.conf",
65+
filename=str(source_file),
66+
model="res.partner",
67+
deferred_fields=None,
68+
auto_defer=False,
69+
unique_id_field=None,
70+
no_preflight_checks=False,
71+
headless=False,
72+
worker=1,
73+
batch_size=10,
74+
skip=0,
75+
fail=False,
76+
separator=";",
77+
ignore=None,
78+
context="{}",
79+
encoding="utf-8",
80+
o2m=False,
81+
groupby=None,
82+
)
83+
84+
# Verify the fail_file path passed to import_data is absolute
85+
# and in the same directory as the input file
86+
call_args = mock_import_data.call_args
87+
fail_file_arg = call_args.kwargs.get("fail_file") or call_args[1].get(
88+
"fail_file"
89+
)
90+
assert fail_file_arg is not None
91+
fail_path = Path(fail_file_arg)
92+
assert fail_path.is_absolute()
93+
assert fail_path.parent == env_dir
94+
95+
4196
class TestRunImport:
4297
"""Tests for the main run_import orchestrator function."""
4398

0 commit comments

Comments
 (0)