Skip to content

Commit 690c825

Browse files
Merge pull request #436 from NeuroML/fix-file-compatibility-check
Fix file compatibility check
2 parents 6131ee4 + b01fcbf commit 690c825

File tree

2 files changed

+88
-20
lines changed

2 files changed

+88
-20
lines changed

pyneuroml/io.py

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import lems.model.model as lems_model
1919
import neuroml.loaders as loaders
2020
import neuroml.writers as writers
21+
from lxml import etree
2122
from neuroml import NeuroMLDocument
2223

2324
from pyneuroml.errors import ARGUMENT_ERR, FILE_NOT_FOUND_ERR, NMLFileTypeError
@@ -27,15 +28,6 @@
2728
logger.setLevel(logging.INFO)
2829

2930

30-
# extension: standard
31-
pynml_file_type_dict = {
32-
"xml": "LEMS",
33-
"nml": "NeuroML",
34-
"sedml": "SED-ML",
35-
"sbml": "SBML",
36-
}
37-
38-
3931
def read_neuroml2_file(
4032
nml2_file_name: str,
4133
include_includes: bool = False,
@@ -256,7 +248,7 @@ def confirm_neuroml_file(filename: str, sys_error: bool = False) -> None:
256248
)
257249

258250
try:
259-
confirm_file_type(filename, ["nml"])
251+
confirm_file_type(filename, ["nml"], "neuroml")
260252
except NMLFileTypeError as e:
261253
if filename.startswith("LEMS_"):
262254
logger.warning(error_string)
@@ -276,9 +268,6 @@ def confirm_lems_file(filename: str, sys_error: bool = False) -> None:
276268
:param sys_error: toggle whether function should exit or raise exception
277269
:type sys_error: bool
278270
"""
279-
# print('Checking file: %s'%filename)
280-
# Some conditions to check if a LEMS file was entered
281-
# TODO: Ideally we'd like to check the root node: checking file extensions is brittle
282271
error_string = textwrap.dedent(
283272
"""
284273
*************************************************************************************
@@ -288,7 +277,7 @@ def confirm_lems_file(filename: str, sys_error: bool = False) -> None:
288277
"""
289278
)
290279
try:
291-
confirm_file_type(filename, ["xml"])
280+
confirm_file_type(filename, ["xml"], "lems")
292281
except NMLFileTypeError as e:
293282
if filename.endswith("nml"):
294283
logger.warning(error_string)
@@ -302,15 +291,22 @@ def confirm_lems_file(filename: str, sys_error: bool = False) -> None:
302291
def confirm_file_type(
303292
filename: str,
304293
file_exts: typing.List[str],
294+
root_tag: typing.Optional[str] = None,
305295
error_str: typing.Optional[str] = None,
306296
sys_error: bool = False,
307297
) -> None:
308-
"""Confirm that a file exists and has the necessary extension
298+
"""Confirm that a file exists and is of the provided type.
299+
300+
First we rely on file extensions to test for type, since this is the
301+
simplest way. If this test fails, we read the full file and test the root
302+
tag if one has been provided.
309303
310304
:param filename: filename to confirm
311305
:type filename: str
312306
:param file_exts: list of valid file extensions, without the leading dot
313307
:type file_exts: list of strings
308+
:param root_tag: root tag for file, used if extensions do not match
309+
:type root_tag: str
314310
:param error_str: an optional error string to print along with the thrown
315311
exception
316312
:type error_str: string (optional)
@@ -320,11 +316,32 @@ def confirm_file_type(
320316
"""
321317
confirm_file_exists(filename)
322318
filename_ext = filename.split(".")[-1]
323-
file_types = [f"{x} ({pynml_file_type_dict[x]})" for x in file_exts]
324-
if filename_ext not in file_exts:
325-
error_string = (
326-
f"Expected file extension(s): {', '.join(file_types)}; got {filename_ext}"
327-
)
319+
320+
matched = False
321+
322+
if filename_ext in file_exts:
323+
matched = True
324+
325+
got_root_tag = None
326+
327+
if matched is False:
328+
if root_tag is not None:
329+
with open(filename) as i_file:
330+
xml_tree = etree.parse(i_file)
331+
tree_root = xml_tree.getroot()
332+
got_root_tag = tree_root.tag
333+
334+
if got_root_tag.lower() == root_tag.lower():
335+
matched = True
336+
337+
if matched is False:
338+
error_string = f"Expected file extension does not match: {', '.join(file_exts)}; got {filename_ext}."
339+
340+
if root_tag is not None:
341+
error_string += (
342+
f" Expected root tag does not match: {root_tag}; got {got_root_tag}"
343+
)
344+
328345
if error_str is not None:
329346
error_string += "\n" + error_str
330347
if sys_error is True:

tests/test_io.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Tests for io methods
4+
5+
File: tests/test_io.py
6+
7+
Copyright 2024 NeuroML contributors
8+
"""
9+
10+
import logging
11+
import os
12+
13+
from pyneuroml.errors import NMLFileTypeError
14+
from pyneuroml.io import confirm_file_type
15+
16+
from . import BaseTestCase
17+
18+
logger = logging.getLogger(__name__)
19+
logger.setLevel(logging.DEBUG)
20+
21+
22+
class TestIo(BaseTestCase):
23+
"""Test io module"""
24+
25+
def test_confirm_file_type(self):
26+
"""Test confirm_file_type method."""
27+
28+
# LEMS file with xml extension
29+
test_lems_file = "a_test_lems_file.xml"
30+
with open(test_lems_file, "w") as f:
31+
print("<LEMS> </LEMS>", file=f)
32+
confirm_file_type(test_lems_file, ["xml"])
33+
34+
# lems file with non xml extension but provided tag
35+
test_lems_file2 = "a_test_lems_file.lems"
36+
with open(test_lems_file2, "w") as f:
37+
print("<LEMS> </LEMS>", file=f)
38+
39+
confirm_file_type(test_lems_file2, ["xml"], "lems")
40+
41+
# lems file with non xml and bad tag: should fail
42+
with self.assertRaises(NMLFileTypeError):
43+
test_lems_file3 = "a_bad_test_lems_file.lems"
44+
with open(test_lems_file3, "w") as f:
45+
print("<LAMS> </LAMS>", file=f)
46+
47+
confirm_file_type(test_lems_file3, ["xml"], "lems")
48+
49+
os.unlink(test_lems_file)
50+
os.unlink(test_lems_file2)
51+
os.unlink(test_lems_file3)

0 commit comments

Comments
 (0)