-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
run conversion: Use context manager only in append mode #1180
Changes from 14 commits
f207b42
6f49746
926eac1
44d8a62
7465000
e3267f0
e2692b8
bad29af
a786946
980ca72
52080dc
1ffdb8e
4a7848e
1a0d767
97a7ff1
4cfd603
4484090
c0f02f8
4c40aa1
9397ab6
37740c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
from .tools.nwb_helpers import ( | ||
HDF5BackendConfiguration, | ||
ZarrBackendConfiguration, | ||
configure_and_write_nwbfile, | ||
configure_backend, | ||
get_default_backend_configuration, | ||
get_default_nwbfile_metadata, | ||
|
@@ -243,35 +244,54 @@ def run_conversion( | |
" use Converter.add_to_nwbfile." | ||
) | ||
|
||
backend = _resolve_backend(backend, backend_configuration) | ||
no_nwbfile_provided = nwbfile is None # Otherwise, variable reference may mutate later on inside the context | ||
|
||
appending_to_in_memory_nwbfile = nwbfile is not None | ||
file_initially_exists = Path(nwbfile_path).exists() if nwbfile_path is not None else False | ||
append_mode = file_initially_exists and not overwrite | ||
appending_to_disk_nwbfile = file_initially_exists and not overwrite | ||
|
||
if metadata is None: | ||
metadata = self.get_metadata() | ||
|
||
self.validate_metadata(metadata=metadata, append_mode=append_mode) | ||
self.validate_metadata(metadata=metadata, append_mode=appending_to_disk_nwbfile) | ||
self.validate_conversion_options(conversion_options=conversion_options) | ||
|
||
self.temporally_align_data_interfaces(metadata=metadata, conversion_options=conversion_options) | ||
|
||
with make_or_load_nwbfile( | ||
nwbfile_path=nwbfile_path, | ||
nwbfile=nwbfile, | ||
metadata=metadata, | ||
overwrite=overwrite, | ||
backend=backend, | ||
verbose=getattr(self, "verbose", False), | ||
) as nwbfile_out: | ||
if no_nwbfile_provided: | ||
if not appending_to_disk_nwbfile: | ||
|
||
if appending_to_in_memory_nwbfile: | ||
self.add_to_nwbfile(nwbfile=nwbfile, metadata=metadata, conversion_options=conversion_options) | ||
else: | ||
nwbfile = self.create_nwbfile(metadata=metadata, conversion_options=conversion_options) | ||
|
||
configure_and_write_nwbfile( | ||
nwbfile=nwbfile, | ||
output_filepath=nwbfile_path, | ||
backend=backend, | ||
backend_configuration=backend_configuration, | ||
) | ||
|
||
else: # We are only using the context in append mode, see issue #1143 | ||
|
||
if appending_to_disk_nwbfile: | ||
raise ValueError( | ||
"Cannot append to an existing file while also providing an in-memory NWBFile. " | ||
"Either set overwrite=True to replace the existing file, or remove the nwbfile parameter to append to the existing file on disk." | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as with basedatainterface |
||
|
||
backend = _resolve_backend(backend, backend_configuration) | ||
with make_or_load_nwbfile( | ||
nwbfile_path=nwbfile_path, | ||
nwbfile=nwbfile, | ||
metadata=metadata, | ||
overwrite=overwrite, | ||
backend=backend, | ||
verbose=getattr(self, "verbose", False), | ||
) as nwbfile_out: | ||
self.add_to_nwbfile(nwbfile=nwbfile_out, metadata=metadata, conversion_options=conversion_options) | ||
|
||
if backend_configuration is None: | ||
backend_configuration = self.get_default_backend_configuration(nwbfile=nwbfile_out, backend=backend) | ||
if backend_configuration is None: | ||
backend_configuration = self.get_default_backend_configuration(nwbfile=nwbfile_out, backend=backend) | ||
|
||
configure_backend(nwbfile=nwbfile_out, backend_configuration=backend_configuration) | ||
configure_backend(nwbfile=nwbfile_out, backend_configuration=backend_configuration) | ||
|
||
def temporally_align_data_interfaces( | ||
self, metadata: Optional[dict] = None, conversion_options: Optional[dict] = None | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -131,23 +131,13 @@ def test_run_conversion_with_backend(self, setup_interface, tmp_path, backend): | |
io.read() | ||
|
||
@pytest.mark.parametrize("backend", ["hdf5", "zarr"]) | ||
def test_run_conversion_with_backend_configuration(self, setup_interface, tmp_path, backend): | ||
def test_create_backend_configuration(self, setup_interface, tmp_path, backend): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those tests were not doing anything. See the bug described here: And if we were to make them work they would be duplicating a test that is already run in two other places. So I changed the name to reflect what the test was actually doing but I would be fine with removing them entirely. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this test be checking that run_conversion runs properly when you pass a custom backend_configuration? How is this redundant with the other tests? What am I missing here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you are right, it is not redundant especially if I change both converter and interface versions. Thinking about this I do want to get rid of these tests completely but I will open another PR describing my reasoning as I don't want this to get in the way. I will revert this to just eliminate the passing of the nwbfile which does not make sense. |
||
metadata = self.interface.get_metadata() | ||
if "session_start_time" not in metadata["NWBFile"]: | ||
metadata["NWBFile"].update(session_start_time=datetime.now().astimezone()) | ||
|
||
nwbfile_path = str(tmp_path / f"conversion_with_backend_configuration{backend}-{self.test_name}.nwb") | ||
|
||
nwbfile = self.interface.create_nwbfile(metadata=metadata, **self.conversion_options) | ||
backend_configuration = self.interface.get_default_backend_configuration(nwbfile=nwbfile, backend=backend) | ||
self.interface.run_conversion( | ||
nwbfile_path=nwbfile_path, | ||
nwbfile=nwbfile, | ||
overwrite=True, | ||
metadata=metadata, | ||
backend_configuration=backend_configuration, | ||
**self.conversion_options, | ||
) | ||
|
||
@pytest.mark.parametrize("backend", ["hdf5", "zarr"]) | ||
def test_configure_backend_for_equivalent_nwbfiles(self, setup_interface, tmp_path, backend): | ||
|
@@ -169,7 +159,7 @@ def test_all_conversion_checks(self, setup_interface, tmp_path): | |
self.nwbfile_path = nwbfile_path | ||
|
||
self.check_run_conversion_in_nwbconverter_with_backend(nwbfile_path=nwbfile_path, backend="hdf5") | ||
self.check_run_conversion_in_nwbconverter_with_backend_configuration(nwbfile_path=nwbfile_path, backend="hdf5") | ||
self.check_create_backend_configuration_with_converter(nwbfile_path=nwbfile_path, backend="hdf5") | ||
|
||
self.check_read_nwb(nwbfile_path=nwbfile_path) | ||
|
||
|
@@ -208,7 +198,7 @@ class TestNWBConverter(NWBConverter): | |
conversion_options=conversion_options, | ||
) | ||
|
||
def check_run_conversion_in_nwbconverter_with_backend_configuration( | ||
def check_create_backend_configuration_with_converter( | ||
self, nwbfile_path: str, backend: Union["hdf5", "zarr"] = "hdf5" | ||
): | ||
class TestNWBConverter(NWBConverter): | ||
|
@@ -225,14 +215,6 @@ class TestNWBConverter(NWBConverter): | |
|
||
nwbfile = converter.create_nwbfile(metadata=metadata, conversion_options=conversion_options) | ||
backend_configuration = converter.get_default_backend_configuration(nwbfile=nwbfile, backend=backend) | ||
converter.run_conversion( | ||
nwbfile_path=nwbfile_path, | ||
nwbfile=nwbfile, | ||
overwrite=True, | ||
metadata=metadata, | ||
backend_configuration=backend_configuration, | ||
conversion_options=conversion_options, | ||
) | ||
|
||
|
||
class TemporalAlignmentMixin: | ||
|
@@ -825,7 +807,7 @@ def test_metadata_schema_valid(self): | |
def test_run_conversion_with_backend(self): | ||
pass | ||
|
||
def test_run_conversion_with_backend_configuration(self): | ||
def test_create_backend_configuration(self): | ||
pass | ||
|
||
def test_no_metadata_mutation(self): | ||
|
@@ -889,7 +871,6 @@ def check_run_conversion_with_backend_configuration( | |
backend_configuration = self.interface.get_default_backend_configuration(nwbfile=nwbfile, backend=backend) | ||
self.interface.run_conversion( | ||
nwbfile_path=nwbfile_path, | ||
nwbfile=nwbfile, | ||
overwrite=True, | ||
metadata=metadata, | ||
backend_configuration=backend_configuration, | ||
|
@@ -915,7 +896,7 @@ class TestNWBConverter(NWBConverter): | |
conversion_options=conversion_options, | ||
) | ||
|
||
def check_run_conversion_in_nwbconverter_with_backend_configuration( | ||
def check_create_backend_configuration_with_converter( | ||
self, nwbfile_path: str, metadata: dict, backend: Union["hdf5", "zarr"] = "hdf5" | ||
): | ||
class TestNWBConverter(NWBConverter): | ||
|
@@ -929,14 +910,6 @@ class TestNWBConverter(NWBConverter): | |
|
||
nwbfile = converter.create_nwbfile(metadata=metadata, conversion_options=conversion_options) | ||
backend_configuration = converter.get_default_backend_configuration(nwbfile=nwbfile, backend=backend) | ||
converter.run_conversion( | ||
nwbfile_path=nwbfile_path, | ||
nwbfile=nwbfile, | ||
overwrite=True, | ||
metadata=metadata, | ||
backend_configuration=backend_configuration, | ||
conversion_options=conversion_options, | ||
) | ||
|
||
def test_all_conversion_checks(self, metadata: dict): | ||
interface_kwargs = self.interface_kwargs | ||
|
@@ -960,7 +933,7 @@ def test_all_conversion_checks(self, metadata: dict): | |
self.check_run_conversion_in_nwbconverter_with_backend( | ||
nwbfile_path=self.nwbfile_path, metadata=metadata, backend="hdf5" | ||
) | ||
self.check_run_conversion_in_nwbconverter_with_backend_configuration( | ||
self.check_create_backend_configuration_with_converter( | ||
nwbfile_path=self.nwbfile_path, metadata=metadata, backend="hdf5" | ||
) | ||
|
||
|
@@ -1203,7 +1176,7 @@ def test_conversion_options_schema_valid(self): | |
def test_run_conversion_with_backend(self): | ||
pass | ||
|
||
def test_run_conversion_with_backend_configuration(self): | ||
def test_create_backend_configuration(self): | ||
pass | ||
|
||
def test_no_metadata_mutation(self): | ||
|
@@ -1262,7 +1235,7 @@ def check_run_conversion_with_backend_configuration( | |
backend_configuration = self.interface.get_default_backend_configuration(nwbfile=nwbfile, backend=backend) | ||
self.interface.run_conversion( | ||
nwbfile_path=nwbfile_path, | ||
nwbfile=nwbfile, | ||
metadata=metadata, | ||
overwrite=True, | ||
backend_configuration=backend_configuration, | ||
**self.conversion_options, | ||
|
@@ -1287,7 +1260,7 @@ class TestNWBConverter(NWBConverter): | |
conversion_options=conversion_options, | ||
) | ||
|
||
def check_run_conversion_in_nwbconverter_with_backend_configuration( | ||
def check_create_backend_configuration_with_converter( | ||
self, nwbfile_path: str, metadata: dict, backend: Union["hdf5", "zarr"] = "hdf5" | ||
): | ||
class TestNWBConverter(NWBConverter): | ||
|
@@ -1301,14 +1274,6 @@ class TestNWBConverter(NWBConverter): | |
|
||
nwbfile = converter.create_nwbfile(metadata=metadata, conversion_options=conversion_options) | ||
backend_configuration = converter.get_default_backend_configuration(nwbfile=nwbfile, backend=backend) | ||
converter.run_conversion( | ||
nwbfile_path=nwbfile_path, | ||
nwbfile=nwbfile, | ||
overwrite=True, | ||
metadata=metadata, | ||
backend_configuration=backend_configuration, | ||
conversion_options=conversion_options, | ||
) | ||
|
||
def test_all_conversion_checks(self, metadata: dict): | ||
interface_kwargs = self.interface_kwargs | ||
|
@@ -1332,7 +1297,7 @@ def test_all_conversion_checks(self, metadata: dict): | |
self.check_run_conversion_in_nwbconverter_with_backend( | ||
nwbfile_path=self.nwbfile_path, metadata=metadata, backend="hdf5" | ||
) | ||
self.check_run_conversion_in_nwbconverter_with_backend_configuration( | ||
self.check_create_backend_configuration_with_converter( | ||
nwbfile_path=self.nwbfile_path, metadata=metadata, backend="hdf5" | ||
) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to raise this value error right at the beginning for clarity. Also, I think it should be
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition was wrong, I fixed it now. Thanks.
I would like the error to be nested in the apend mode branch. I am also coming at this from the clarity angle but with a different reasoning.
People who read the code should not be bothered with the complexities of the apend case unless they get to that branch. In most cases,
run_conversion
is working as awrite_to_nwbfile
(which I think should be a method!) and I don't want the first reading of the function to introduce unrelated complexities.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really disagree. This is input validation for the function, so it should go right at the top with the inputs. By having in the append branch it's more nested, and to understand the condition you have to combine an else with a not:
else(not appending_to_in_disk_nwbfile) + appending_to_in_memory_file
. In contrast, I don't think it is hard to understand at all thatappending_to_in_memory_nwbfile
andappending_to_in_disk_nwbfile
can't both be true at the same time --> ValueError, even without understanding anything about the details of those branches.With all that being said, this is just style, so if you are still adamant about keeping it where it is, I won't insist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those are good points. I will move it.
I still have the lingering feeling of wanting to push the complexities of appending mode outside of the happy path but maybe I should push for a
write_to_nwbfile
functionality outside of this PR. I think should be easier to read, use, and document for both developers and users.run_conversion
, like the context manager here, has too many responsibilities which leads to too many arguments an anti-pattern and a code smell.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, isolating the complexity of append mode makes sense to me. I look forward to seeing those changes, and lmk if you need any help.