Skip to content

Commit 0666329

Browse files
authored
TFSCollections cleaned and with paths (#122)
1 parent c91ad06 commit 0666329

File tree

6 files changed

+412
-149
lines changed

6 files changed

+412
-149
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
# TFS-Pandas Changelog
22

3+
## Version 3.7.0
4+
5+
Minor API changes to the `TFSCollections`:
6+
- the old `write_to` and `get_filename` are renamed to `_write_to` and `_get_filename` as they
7+
could only be accessed internally (due to the input parameters not available to the user).
8+
This also means, that - in case they are overwritten by a user's implementation - they need to be renamed there!!
9+
10+
- The column which is set as index can now also be defined manually, by overwriting the attribute `INDEX`, which defaults to `"NAME"`.
11+
12+
- New Functions of `TFSCollection` Instances:
13+
- `get_filename(name)`: Returns the associated filename to the property with name `name`.
14+
- `get_path(name)`: Return the actual file path of the property `name`
15+
- `flush()`: Write the current state of the TFSDataFrames into their respective files.
16+
- `write_tfs(filename, data_frame)`: Write the `data_frame` to `self.directory` with the given `filename`.
17+
18+
- New Special Properties of `TFSCollection` Instances:
19+
- `defined_properties`: Tuple of strings of the defined properties on this instance.
20+
- `filenames` is a convenience wrapper for `get_filename()`:
21+
- When called (`filenames(exist: bool)`) returns a dictionary of the defined properties and their associated filenames.
22+
The `exist` boolean filters between existing files or filenames for all properties.
23+
- Can also be used either `filenames.name` or `filenames[name]` to call `get_filename(name)` on the instance.
24+
25+
- Moved the define-properties functions directly into the `Tfs`-attribute marker class.
26+
- Return of `None` for the `MaybeCall` class in case of attribute not found (instead of empty function, which didn't make sense).
27+
328
## Version 3.6.0
429

530
- Removed:

tests/test_collection.py

Lines changed: 238 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
import os
22
import pathlib
3-
43
import pytest
54
from pandas.testing import assert_frame_equal
65

76
from tfs import read_tfs
87
from tfs.collection import Tfs, TfsCollection
98
from tfs.frame import TfsDataFrame
109

11-
CURRENT_DIR = pathlib.Path(__file__).parent
12-
13-
14-
def test_tfscollection_getfilename_not_implemented():
15-
with pytest.raises(NotImplementedError):
16-
TfsCollection.get_filename("doesnt matter")
10+
INPUT_DIR = pathlib.Path(__file__).parent / "inputs"
1711

1812

1913
class CollectionTest(TfsCollection):
@@ -22,98 +16,277 @@ class CollectionTest(TfsCollection):
2216
filex = Tfs("file_x.tfs", two_planes=False)
2317
value = 10
2418

25-
def get_filename(self, template, plane=""):
19+
def _get_filename(self, template, plane=""):
2620
return template.format(plane)
2721

2822

29-
def test_read_pathlib_input(_input_dir_pathlib: pathlib.Path, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame):
30-
c = CollectionTest(_input_dir_pathlib)
31-
assert_frame_equal(_tfs_x, c.file_x)
32-
assert_frame_equal(_tfs_x, c.filex)
33-
# test that both capitalized and lowered plane keys are accepted
34-
assert_frame_equal(_tfs_x, c.file["x"])
35-
assert_frame_equal(_tfs_x, c.file["X"])
36-
assert_frame_equal(_tfs_y, c.file["y"])
37-
assert_frame_equal(_tfs_y, c.file["Y"])
38-
assert c.value == 10
23+
class TestRead:
24+
25+
def test_read_pathlib_input(self, _input_dir_pathlib: pathlib.Path, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame):
26+
c = CollectionTest(_input_dir_pathlib, allow_write=False)
27+
assert_frame_equal(_tfs_x, c.file_x)
28+
assert_frame_equal(_tfs_x, c.filex)
29+
# test that both capitalized and lowered plane keys are accepted
30+
assert_frame_equal(_tfs_x, c.file["x"])
31+
assert_frame_equal(_tfs_x, c.file["X"])
32+
assert_frame_equal(_tfs_y, c.file["y"])
33+
assert_frame_equal(_tfs_y, c.file["Y"])
34+
assert c.value == 10
35+
36+
def test_read_str_input(self, _input_dir_str: str, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame):
37+
c = CollectionTest(_input_dir_str, allow_write=False)
38+
assert_frame_equal(_tfs_x, c.file_x)
39+
assert_frame_equal(_tfs_x, c.filex)
40+
# test that both capitalized and lowered plane keys are accepted
41+
assert_frame_equal(_tfs_x, c.file["x"])
42+
assert_frame_equal(_tfs_x, c.file["X"])
43+
assert_frame_equal(_tfs_y, c.file["y"])
44+
assert_frame_equal(_tfs_y, c.file["Y"])
45+
assert c.value == 10
46+
47+
48+
class TestWrite:
49+
50+
def test_write(self, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame, tmp_path):
51+
c = CollectionTest(tmp_path)
52+
file_x_path = tmp_path / "nofile_x.tfs"
53+
assert not file_x_path.is_file()
54+
55+
c.nofile_x = _tfs_y # only assigns dataframe without writing (use _tfs_y so that we can set _tfs_x below)
56+
assert not file_x_path.is_file()
57+
assert_frame_equal(_tfs_y, c.nofile_x)
58+
59+
c.allow_write = True
60+
c.nofile_x = _tfs_x # should overwrite _tfs_y in buffer
61+
assert file_x_path.is_file()
62+
assert_frame_equal(_tfs_x, c.nofile_x)
63+
64+
tfs_x_loaded = _read_tfs(file_x_path)
65+
assert_frame_equal(_tfs_x, tfs_x_loaded)
66+
67+
c.nofile["y"] = _tfs_y
68+
file_y_path = tmp_path / "nofile_y.tfs"
69+
assert file_y_path.is_file()
70+
assert_frame_equal(_tfs_y, c.nofile["y"])
71+
assert_frame_equal(_tfs_y, c.nofile["Y"])
72+
73+
def test_write_tfs(self, _tfs_x: TfsDataFrame, tmp_path):
74+
c = CollectionTest(tmp_path)
75+
name = "nofile_x.tfs"
76+
assert not (tmp_path / name).is_file()
77+
c.write_tfs(name, _tfs_x)
78+
assert (tmp_path / name).is_file()
79+
80+
def test_write_to(self, _tfs_x: TfsDataFrame, tmp_path):
81+
class WriteToCollectionTest(TfsCollection):
82+
file = Tfs("file_{}.tfs")
83+
filex = Tfs("file_x.tfs", two_planes=False)
84+
85+
def _get_filename(self, template, plane=""):
86+
return template.format(plane)
87+
88+
def _write_to(self, df, template, plane=""):
89+
return f"out_{self._get_filename(template, plane)}", df
90+
91+
c = WriteToCollectionTest(tmp_path, allow_write=True)
92+
filepath = tmp_path / "out_file_x.tfs"
93+
94+
assert not filepath.exists()
95+
c.file_x = _tfs_x
96+
assert filepath.exists()
97+
98+
filepath.unlink()
99+
100+
assert not filepath.exists()
101+
c.filex = _tfs_x
102+
assert filepath.exists()
103+
104+
def test_buffer_flush(self, _input_dir_str: str, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame, tmp_path):
105+
c = CollectionTest(tmp_path, allow_write=True)
106+
107+
c.file_x = _tfs_x.copy()
108+
c.nofile_y = _tfs_y.copy()
109+
tfs_x = _tfs_x.drop(columns="NAME") # index reading below drops columns, TfsCollections does not
110+
tfs_y = _tfs_y.drop(columns="NAME")
111+
112+
c.file_x.loc["BPMSX.4L2.B1", "NUMBER"] = -199
113+
c.nofile_y.loc["BPMSX.4L2.B1", "NUMBER"] = -19
114+
115+
assert_frame_equal(tfs_x, read_tfs(c.get_path("file_x"), index=c.INDEX))
116+
assert_frame_equal(tfs_y, read_tfs(c.get_path("nofile_y"), index=c.INDEX))
117+
118+
c.flush()
119+
120+
tfs_x_after_flush = read_tfs(c.get_path("file_x"), index=c.INDEX)
121+
tfs_y_after_flush = read_tfs(c.get_path("nofile_y"), index=c.INDEX)
122+
with pytest.raises(AssertionError):
123+
assert_frame_equal(tfs_x, tfs_x_after_flush )
124+
125+
with pytest.raises(AssertionError):
126+
assert_frame_equal(tfs_y, tfs_y_after_flush)
127+
128+
assert tfs_x_after_flush.loc["BPMSX.4L2.B1", "NUMBER"] == -199
129+
assert tfs_y_after_flush.loc["BPMSX.4L2.B1", "NUMBER"] == -19
130+
131+
def test_buffer_flush_nowrite(self, _input_dir_str: str, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame, tmp_path):
132+
c = CollectionTest(tmp_path, allow_write=True)
133+
134+
c.file_x = _tfs_x.copy()
135+
c.nofile_y = _tfs_y.copy()
136+
tfs_x = _tfs_x.drop(columns="NAME") # index reading below drops columns, TfsCollections does not
137+
tfs_y = _tfs_y.drop(columns="NAME")
138+
139+
c.file_x.loc["BPMSX.4L2.B1", "NUMBER"] = -199
140+
c.nofile_y.loc["BPMSX.4L2.B1", "NUMBER"] = -19
141+
142+
assert_frame_equal(tfs_x, read_tfs(c.get_path("file_x"), index=c.INDEX))
143+
assert_frame_equal(tfs_y, read_tfs(c.get_path("nofile_y"), index=c.INDEX))
144+
145+
c.allow_write = False
146+
with pytest.raises(IOError):
147+
c.flush()
148+
149+
tfs_x_after_flush = read_tfs(c.get_path("file_x"), index=c.INDEX)
150+
tfs_y_after_flush = read_tfs(c.get_path("nofile_y"), index=c.INDEX)
151+
assert_frame_equal(tfs_x, tfs_x_after_flush)
152+
assert_frame_equal(tfs_y, tfs_y_after_flush)
153+
154+
155+
class TestFilenames:
156+
157+
def test_tfscollection_getfilename_not_implemented(self):
158+
with pytest.raises(NotImplementedError):
159+
TfsCollection._get_filename("doesnt matter")
160+
161+
def test_get_filename(self, _input_dir_pathlib: pathlib.Path):
162+
c = CollectionTest(_input_dir_pathlib, allow_write=False)
163+
assert c.get_filename("file_y") == "file_y.tfs"
164+
assert c.get_filename("filex") == "file_x.tfs"
165+
assert c.get_filename("nofile_x") == "nofile_x.tfs"
166+
167+
def test_get_filename_not_there(self, _input_dir_pathlib: pathlib.Path):
168+
c = CollectionTest(_input_dir_pathlib, allow_write=False)
169+
with pytest.raises(AttributeError):
170+
c.get_filename("doesn't matter either")
171+
172+
def test_filenames(self, _input_dir_pathlib: pathlib.Path):
173+
c = CollectionTest(_input_dir_pathlib, allow_write=False)
174+
assert c.filenames.file_y == "file_y.tfs"
175+
assert c.filenames.filex == "file_x.tfs"
176+
assert c.filenames.nofile_x == "nofile_x.tfs"
177+
178+
assert c.filenames["file_x"] == "file_x.tfs"
179+
assert c.filenames["nofile_y"] == "nofile_y.tfs"
180+
181+
exist_properties = "file_x", "file_y", "filex"
182+
not_exist_properties = "nofile_x", "nofile_y"
183+
exist_files = "file_x.tfs", "file_y.tfs"
184+
not_exist_files = "nofile_x.tfs", "nofile_y.tfs"
185+
186+
assert c.filenames()["file_x"] == "file_x.tfs"
187+
assert c.filenames()["nofile_y"] == "nofile_y.tfs"
188+
189+
assert all(f in c.filenames().keys() for f in exist_properties)
190+
assert all(f in c.filenames().keys() for f in not_exist_properties)
191+
assert all(f in c.filenames().values() for f in exist_files)
192+
assert all(f in c.filenames().values() for f in not_exist_files)
193+
194+
assert all(f in c.filenames(exist=True).keys() for f in exist_properties)
195+
assert all(f not in c.filenames(exist=True).keys() for f in not_exist_properties)
196+
assert all(f in c.filenames(exist=True).values() for f in exist_files)
197+
assert all(f not in c.filenames(exist=True).values() for f in not_exist_files)
198+
199+
def test_get_path(self, _input_dir_pathlib: pathlib.Path):
200+
c = CollectionTest(_input_dir_pathlib, allow_write=False)
201+
assert c.get_path("file_y") == _input_dir_pathlib / "file_y.tfs"
202+
assert c.get_path("filex") == _input_dir_pathlib / "file_x.tfs"
203+
assert c.get_path("nofile_x") == _input_dir_pathlib / "nofile_x.tfs"
204+
205+
206+
class TestOther:
207+
208+
def test_access_methods(self, _input_dir_pathlib: pathlib.Path):
209+
c = CollectionTest(_input_dir_pathlib, allow_write=False)
39210

211+
# Getting (partly tested in read-test as well)
212+
assert_frame_equal(c.file_x, c.file["x"])
213+
assert_frame_equal(c.file_x, c.file["X"])
214+
assert_frame_equal(c.file_x, c["file_x"])
40215

41-
def test_read_str_input(_input_dir_str: str, _tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame):
42-
c = CollectionTest(_input_dir_str)
43-
assert_frame_equal(_tfs_x, c.file_x)
44-
assert_frame_equal(_tfs_x, c.filex)
45-
# test that both capitalized and lowered plane keys are accepted
46-
assert_frame_equal(_tfs_x, c.file["x"])
47-
assert_frame_equal(_tfs_x, c.file["X"])
48-
assert_frame_equal(_tfs_y, c.file["y"])
49-
assert_frame_equal(_tfs_y, c.file["Y"])
50-
assert c.value == 10
216+
# Setting
217+
c.nofile_y = c.file_y
218+
assert_frame_equal(c.nofile_y, c.file_y)
51219

220+
c["nofile_y"] = c.file_x
221+
assert_frame_equal(c.nofile_y, c.file_x)
52222

53-
def test_write(_tfs_x: TfsDataFrame, _tfs_y: TfsDataFrame, tmp_path):
54-
c = CollectionTest(tmp_path)
55-
file_x_path = tmp_path / "nofile_x.tfs"
56-
assert not file_x_path.is_file()
223+
c.nofile["y"] = c.file_y
224+
assert_frame_equal(c.nofile_y, c.file_y)
57225

58-
c.nofile_x = _tfs_x # will not throw error, but does nothing
59-
assert not file_x_path.is_file()
226+
c.nofile["Y"] = c.file_x
227+
assert_frame_equal(c.nofile_y, c.file_x)
60228

61-
c.allow_write = True
62-
c.nofile_x = _tfs_x
63-
assert file_x_path.is_file()
64-
assert_frame_equal(_tfs_x, c.nofile_x)
229+
def test_index(self, _input_dir_pathlib: pathlib.Path, _tfs_x: TfsDataFrame):
230+
c = CollectionTest(_input_dir_pathlib)
231+
c.INDEX = "S"
232+
assert all(c.filex.index == _tfs_x["S"])
65233

66-
c.nofile["y"] = _tfs_y
67-
file_y_path = tmp_path / "nofile_y.tfs"
68-
assert file_y_path.is_file()
69-
assert_frame_equal(_tfs_y, c.nofile["y"])
70-
assert_frame_equal(_tfs_y, c.nofile["Y"])
234+
def test_defined_properties(self, _input_dir_pathlib: pathlib.Path):
235+
c = CollectionTest(_input_dir_pathlib)
236+
exist_properties = {"file_x", "file_y", "filex", "nofile_x", "nofile_y"}
237+
assert set(c.defined_properties) == exist_properties
71238

239+
def test_maybe(self, _input_dir_pathlib: pathlib.Path):
240+
def _test_fun(df, a, b):
241+
return df.BPMCOUNT, a + b
72242

73-
def test_maybe_pathlib_input(_input_dir_pathlib: pathlib.Path):
74-
c = CollectionTest(_input_dir_pathlib)
75-
c.maybe_call.nofile_x
76-
with pytest.raises(IOError):
77-
c.nofile_x
243+
c = CollectionTest(_input_dir_pathlib)
244+
res_no = c.maybe_call.nofile_x(_test_fun, 10, 20)
245+
assert res_no is None
78246

247+
res_file = c.maybe_call.file_x(_test_fun, 10, 20)
248+
assert res_file[0] == 9
249+
assert res_file[1] == 30
79250

80-
def test_maybe_str_input(_input_dir_str: str):
81-
c = CollectionTest(_input_dir_str)
82-
c.maybe_call.nofile_x
83-
with pytest.raises(IOError):
84-
c.nofile_x
251+
# same but with item:
252+
res_file = c.maybe_call.file["x"](_test_fun, 5, 8)
253+
assert res_file[0] == 9
254+
assert res_file[1] == 13
85255

256+
def test_buffer_clear(self, _dummy_collection):
257+
_dummy_collection._buffer["some_key"] = 5
258+
assert _dummy_collection._buffer["some_key"]
259+
_dummy_collection.clear()
260+
assert not _dummy_collection._buffer
86261

87-
def test_collection_buffer_clear(_dummy_collection):
88-
_dummy_collection._buffer["some_key"] = 5
89-
assert _dummy_collection._buffer["some_key"]
90-
_dummy_collection.clear()
91-
assert not _dummy_collection._buffer
262+
def test_no_attribute(self, _dummy_collection):
263+
with pytest.raises(AttributeError):
264+
_ = _dummy_collection.absent_attribute
92265

93266

94-
def test_tfs_collection_no_attribute(_dummy_collection):
95-
with pytest.raises(AttributeError):
96-
_ = _dummy_collection.absent_attribute
267+
def _read_tfs(path):
268+
""" Reads tfs like in _load_tfs() of the collection (here we know we have NAME in tfs). """
269+
return read_tfs(path).set_index("NAME", drop=False)
97270

98271

99272
@pytest.fixture()
100273
def _tfs_x() -> TfsDataFrame:
101-
return read_tfs(CURRENT_DIR / "inputs" / "file_x.tfs").set_index("NAME", drop=False)
274+
return _read_tfs(INPUT_DIR / "file_x.tfs")
102275

103276

104277
@pytest.fixture()
105278
def _tfs_y() -> TfsDataFrame:
106-
return read_tfs(CURRENT_DIR / "inputs" / "file_y.tfs").set_index("NAME", drop=False)
279+
return _read_tfs(INPUT_DIR / "file_y.tfs")
107280

108281

109282
@pytest.fixture()
110283
def _input_dir_pathlib() -> pathlib.Path:
111-
return CURRENT_DIR / "inputs"
284+
return INPUT_DIR
112285

113286

114287
@pytest.fixture()
115288
def _input_dir_str() -> str:
116-
return os.path.join(os.path.dirname(__file__), "inputs")
289+
return str(INPUT_DIR)
117290

118291

119292
@pytest.fixture()

0 commit comments

Comments
 (0)