Skip to content

Commit 32c4dad

Browse files
authored
Merge pull request #272 from Oxid15/handle_external_removes
Handle external removes
2 parents fbdd303 + e0d2fb1 commit 32c4dad

File tree

3 files changed

+139
-22
lines changed

3 files changed

+139
-22
lines changed

cascade/repos/repo.py

+29-14
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,32 @@ def __init__(
8383
}
8484

8585
if "lines" in kwargs:
86-
raise ValueError(
87-
"lines was removed in 0.14.0, consider using add_line method instead"
88-
)
86+
raise ValueError("lines was removed in 0.14.0, consider using add_line method instead")
8987

9088
self.sync_meta()
9189

90+
def _new_line_name(self):
91+
n = len(self)
92+
index_name = f"{n:0>5d}"
93+
if n == 0:
94+
return index_name
95+
96+
line_names = self.get_line_names()
97+
98+
# Since lines are always sorted
99+
# Latest will be the max
100+
latest_numeric_name = None
101+
for name in line_names[::-1]:
102+
if name.isnumeric():
103+
latest_numeric_name = name
104+
break
105+
106+
if latest_numeric_name:
107+
latest_line_num = int(latest_numeric_name)
108+
return f"{latest_line_num + 1:0>5d}"
109+
else:
110+
return index_name
111+
92112
def add_line(
93113
self,
94114
name: Optional[str] = None,
@@ -139,14 +159,11 @@ def add_line(
139159
_description_
140160
"""
141161
if name is None:
142-
name = f"{len(self):0>5d}"
143-
if name in self.get_line_names():
144-
# Name can appear in the repo if the user manually
145-
# removed the lines from the middle of the repo
146-
147-
# This will be handled strictly
148-
# until it will become clear that some solution needed
149-
raise RuntimeError(f"Line {name} already exists in {self}")
162+
name = self._new_line_name()
163+
assert name not in self._lines, (
164+
f"Tried creating line name `{name}` that is already in repo."
165+
" This is unexpected, if you see this, please fill a GitHub issue"
166+
)
150167

151168
folder = os.path.join(self._root, name)
152169
if meta_fmt is None:
@@ -236,9 +253,7 @@ def load_obj_meta(self, obj: str) -> Meta:
236253
continue
237254
else:
238255
return meta
239-
raise FileNotFoundError(
240-
f"Failed to find the object {obj} in the repo at {self._root}"
241-
)
256+
raise FileNotFoundError(f"Failed to find the object {obj} in the repo at {self._root}")
242257

243258
def _update_lines(self) -> None:
244259
for name in sorted(os.listdir(self._root)):

cascade/tests/lines/test_model_line.py

+28-4
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
sys.path.append(os.path.dirname(MODULE_PATH))
2626

2727
from cascade.base import MetaHandler, default_meta_format
28+
from cascade.lines import ModelLine
2829
from cascade.models import BasicModel
2930
from cascade.repos import Repo
30-
from cascade.lines import ModelLine
3131
from cascade.tests.conftest import DummyModel
3232

3333

@@ -63,19 +63,42 @@ def test_change_of_format(tmp_path_str, ext):
6363
assert len(glob.glob(os.path.join(tmp_path_str, "meta.*"))) == 1
6464

6565

66-
def test_same_index_check(model_line):
66+
def test_external_remove_first(model_line):
67+
for _ in range(5):
68+
model_line.save(BasicModel())
69+
70+
shutil.rmtree(os.path.join(model_line.get_root(), "00000"))
71+
72+
model_line = ModelLine(model_line.get_root())
73+
model_line.save(BasicModel())
74+
75+
assert os.path.exists(os.path.join(model_line.get_root(), "00005"))
76+
77+
78+
def test_external_remove_middle(model_line):
6779
for _ in range(5):
6880
model_line.save(BasicModel())
6981

70-
shutil.rmtree(os.path.join(model_line.get_root(), "00001"))
7182
shutil.rmtree(os.path.join(model_line.get_root(), "00002"))
72-
shutil.rmtree(os.path.join(model_line.get_root(), "00003"))
7383

84+
model_line = ModelLine(model_line.get_root())
7485
model_line.save(BasicModel())
7586

7687
assert os.path.exists(os.path.join(model_line.get_root(), "00005"))
7788

7889

90+
def test_external_remove_last(model_line):
91+
for _ in range(5):
92+
model_line.save(BasicModel())
93+
94+
shutil.rmtree(os.path.join(model_line.get_root(), "00004"))
95+
96+
model_line = ModelLine(model_line.get_root())
97+
model_line.save(BasicModel())
98+
99+
assert os.path.exists(os.path.join(model_line.get_root(), "00004"))
100+
101+
79102
# TODO: write tests for exceptions
80103
def test_load_model_meta_slug(model_line, dummy_model):
81104
dummy_model.evaluate()
@@ -184,6 +207,7 @@ def test_model_names(tmp_path_str):
184207
line = ModelLine(tmp_path_str)
185208
assert line.get_model_names() == ["00000", "00001", "00002"]
186209

210+
187211
# TODO: write test for restoring line from repo
188212

189213

cascade/tests/repos/test_model_repo.py renamed to cascade/tests/repos/test_repo.py

+82-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import glob
1515
import os
16+
import shutil
1617
import sys
1718

1819
import pytest
@@ -297,13 +298,90 @@ def test_auto_line_name(tmp_path_str, ext):
297298

298299
names = repo.get_line_names()
299300

300-
assert names == ["00000", "test", "00002", "00003"]
301+
assert set(names) == {"00000", "test", "00001", "00002"}
301302

302-
repo = Repo(str(tmp_path_str), meta_fmt=ext)
303303

304-
names = repo.get_line_names()
304+
def test_external_remove_first(tmp_path_str):
305+
repo = Repo(str(tmp_path_str))
306+
repo.add_line()
307+
repo.add_line()
308+
repo.add_line()
309+
repo.add_line()
310+
311+
shutil.rmtree(os.path.join(repo.get_root(), "00000"))
312+
313+
repo = Repo(str(tmp_path_str))
314+
repo.add_line()
315+
316+
assert repo.get_line_names() == [
317+
"00001",
318+
"00002",
319+
"00003",
320+
"00004",
321+
]
322+
323+
324+
def test_external_remove_middle(tmp_path_str):
325+
repo = Repo(str(tmp_path_str))
326+
repo.add_line()
327+
repo.add_line()
328+
repo.add_line()
329+
repo.add_line()
330+
331+
shutil.rmtree(os.path.join(repo.get_root(), "00001"))
332+
333+
repo = Repo(str(tmp_path_str))
334+
repo.add_line()
335+
336+
assert repo.get_line_names() == [
337+
"00000",
338+
"00002",
339+
"00003",
340+
"00004",
341+
]
342+
343+
344+
def test_external_remove_last(tmp_path_str):
345+
repo = Repo(str(tmp_path_str))
346+
repo.add_line()
347+
repo.add_line()
348+
repo.add_line()
349+
repo.add_line()
350+
351+
shutil.rmtree(os.path.join(repo.get_root(), "00003"))
352+
353+
repo = Repo(str(tmp_path_str))
354+
repo.add_line()
355+
356+
assert repo.get_line_names() == [
357+
"00000",
358+
"00001",
359+
"00002",
360+
"00003",
361+
]
362+
363+
364+
def test_mixed_formats(tmp_path_str):
365+
repo = Repo(str(tmp_path_str))
366+
repo.add_line("00000")
367+
repo.add_line("00001")
368+
repo.add_line("a")
369+
repo.add_line("b")
370+
repo.add_line("c")
371+
372+
shutil.rmtree(os.path.join(repo.get_root(), "b"))
373+
374+
repo = Repo(str(tmp_path_str))
375+
repo.add_line()
305376

306-
assert names == ["00000", "00002", "00003", "test"]
377+
# I do not care about order here
378+
assert set(repo.get_line_names()) == {
379+
"00000",
380+
"00001",
381+
"00002",
382+
"a",
383+
"c",
384+
}
307385

308386

309387
@pytest.mark.skip

0 commit comments

Comments
 (0)