11from __future__ import annotations
22
3- import pathlib
3+ import sys
4+ from pathlib import Path
45
56import numpy as np
67from numpy .typing import ArrayLike
78
8- from ._common import num_nodes_per_cell
9+ from ._common import error , num_nodes_per_cell
910from ._exceptions import ReadError , WriteError
1011from ._files import is_buffer
1112from ._mesh import CellBlock , Mesh
1213
13- extension_to_filetype = {}
14+ extension_to_filetypes = {}
1415reader_map = {}
1516_writer_map = {}
1617
1718
18- def register_format (name : str , extensions : list [str ], reader , writer_map ):
19+ def register_format (
20+ format_name : str , extensions : list [str ], reader , writer_map
21+ ) -> None :
1922 for ext in extensions :
20- extension_to_filetype [ext ] = name
23+ if ext not in extension_to_filetypes :
24+ extension_to_filetypes [ext ] = []
25+ extension_to_filetypes [ext ].append (format_name )
2126
2227 if reader is not None :
23- reader_map [name ] = reader
28+ reader_map [format_name ] = reader
29+
2430 _writer_map .update (writer_map )
2531
2632
27- def _filetype_from_path (path : pathlib .Path ):
33+ def deregister_format (format_name : str ):
34+ for value in extension_to_filetypes .values ():
35+ if format_name in value :
36+ value .remove (format_name )
37+
38+ if format_name in reader_map :
39+ reader_map .pop (format_name )
40+
41+ if format_name in _writer_map :
42+ _writer_map .pop (format_name )
43+
44+
45+ def _filetypes_from_path (path : Path ) -> list [str ]:
2846 ext = ""
29- out = None
47+ out = []
3048 for suffix in reversed (path .suffixes ):
3149 ext = (suffix + ext ).lower ()
32- if ext in extension_to_filetype :
33- out = extension_to_filetype [ext ]
50+ try :
51+ out += extension_to_filetypes [ext ]
52+ except KeyError :
53+ pass
3454
35- if out is None :
36- raise ReadError (f"Could not deduce file format from extension '{ ext } '." )
55+ if not out :
56+ raise ReadError (f"Could not deduce file format from path '{ path } '." )
3757 return out
3858
3959
@@ -46,31 +66,48 @@ def read(filename, file_format: str | None = None):
4666 :returns mesh{2,3}d: The mesh data.
4767 """
4868 if is_buffer (filename , "r" ):
49- if file_format is None :
50- raise ReadError ("File format must be given if buffer is used" )
51- if file_format == "tetgen" :
52- raise ReadError (
53- "tetgen format is spread across multiple files "
54- "and so cannot be read from a buffer"
55- )
56- msg = f"Unknown file format '{ file_format } '"
57- else :
58- path = pathlib .Path (filename )
59- if not path .exists ():
60- raise ReadError (f"File { filename } not found." )
69+ return _read_buffer (filename , file_format )
6170
62- if not file_format :
63- # deduce file format from extension
64- file_format = _filetype_from_path (path )
71+ return _read_file (Path (filename ), file_format )
6572
66- msg = f"Unknown file format '{ file_format } ' of '{ filename } '."
6773
74+ def _read_buffer (filename , file_format : str | None ):
75+ if file_format is None :
76+ raise ReadError ("File format must be given if buffer is used" )
77+ if file_format == "tetgen" :
78+ raise ReadError (
79+ "tetgen format is spread across multiple files "
80+ "and so cannot be read from a buffer"
81+ )
6882 if file_format not in reader_map :
69- raise ReadError (msg )
83+ raise ReadError (f"Unknown file format ' { file_format } '" )
7084
7185 return reader_map [file_format ](filename )
7286
7387
88+ def _read_file (path : Path , file_format : str | None ):
89+ if not path .exists ():
90+ raise ReadError (f"File { path } not found." )
91+
92+ if file_format :
93+ possible_file_formats = [file_format ]
94+ else :
95+ # deduce possible file formats from extension
96+ possible_file_formats = _filetypes_from_path (path )
97+
98+ for file_format in possible_file_formats :
99+ if file_format not in reader_map :
100+ raise ReadError (f"Unknown file format '{ file_format } ' of '{ path } '." )
101+
102+ try :
103+ return reader_map [file_format ](str (path ))
104+ except ReadError :
105+ pass
106+
107+ error (f"Couldn't read file { path } as either of { ', ' .join (possible_file_formats )} " )
108+ sys .exit (1 )
109+
110+
74111def write_points_cells (
75112 filename ,
76113 points : ArrayLike ,
@@ -113,10 +150,12 @@ def write(filename, mesh: Mesh, file_format: str | None = None, **kwargs):
113150 "tetgen format is spread across multiple files, and so cannot be written to a buffer"
114151 )
115152 else :
116- path = pathlib . Path (filename )
153+ path = Path (filename )
117154 if not file_format :
118- # deduce file format from extension
119- file_format = _filetype_from_path (path )
155+ # deduce possible file formats from extension
156+ file_formats = _filetypes_from_path (path )
157+ # just take the first one
158+ file_format = file_formats [0 ]
120159
121160 try :
122161 writer = _writer_map [file_format ]
0 commit comments