diff --git a/setup.cfg b/setup.cfg index 9aa79dff6..f3a851f68 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = meshio -version = 5.2.6 +version = 5.3.0 author = Nico Schlömer et al. author_email = nico.schloemer@gmail.com description = I/O for many mesh formats diff --git a/src/meshio/__init__.py b/src/meshio/__init__.py index 16675ffbb..444e232ab 100644 --- a/src/meshio/__init__.py +++ b/src/meshio/__init__.py @@ -34,7 +34,8 @@ from .__about__ import __version__ from ._exceptions import ReadError, WriteError from ._helpers import ( - extension_to_filetype, + deregister_format, + extension_to_filetypes, read, register_format, write, @@ -77,8 +78,9 @@ "read", "write", "register_format", + "deregister_format", "write_points_cells", - "extension_to_filetype", + "extension_to_filetypes", "Mesh", "CellBlock", "ReadError", diff --git a/src/meshio/_cli/_ascii.py b/src/meshio/_cli/_ascii.py index 91f7f0f44..45aff5a9a 100644 --- a/src/meshio/_cli/_ascii.py +++ b/src/meshio/_cli/_ascii.py @@ -3,7 +3,7 @@ from .. import ansys, flac3d, gmsh, mdpa, ply, stl, vtk, vtu, xdmf from .._common import error -from .._helpers import _filetype_from_path, read, reader_map +from .._helpers import _filetypes_from_path, read, reader_map def add_args(parser): @@ -20,8 +20,12 @@ def add_args(parser): def ascii(args): - # read mesh data - fmt = args.input_format or _filetype_from_path(pathlib.Path(args.infile)) + if args.input_format: + fmts = [args.input_format] + else: + fmts = _filetypes_from_path(pathlib.Path(args.infile)) + # pick the first + fmt = fmts[0] size = os.stat(args.infile).st_size print(f"File size before: {size / 1024 ** 2:.2f} MB") diff --git a/src/meshio/_cli/_binary.py b/src/meshio/_cli/_binary.py index 3378e082f..ccf063580 100644 --- a/src/meshio/_cli/_binary.py +++ b/src/meshio/_cli/_binary.py @@ -2,7 +2,7 @@ import pathlib from .. import ansys, flac3d, gmsh, mdpa, ply, stl, vtk, vtu, xdmf -from .._helpers import _filetype_from_path, read, reader_map +from .._helpers import _filetypes_from_path, read, reader_map def add_args(parser): @@ -19,8 +19,12 @@ def add_args(parser): def binary(args): - # read mesh data - fmt = args.input_format or _filetype_from_path(pathlib.Path(args.infile)) + if args.input_format: + fmts = [args.input_format] + else: + fmts = _filetypes_from_path(pathlib.Path(args.infile)) + # pick the first + fmt = fmts[0] size = os.stat(args.infile).st_size print(f"File size before: {size / 1024 ** 2:.2f} MB") diff --git a/src/meshio/_cli/_compress.py b/src/meshio/_cli/_compress.py index 71dcb3bcb..9103a0bb4 100644 --- a/src/meshio/_cli/_compress.py +++ b/src/meshio/_cli/_compress.py @@ -3,7 +3,7 @@ from .. import ansys, cgns, gmsh, h5m, mdpa, ply, stl, vtk, vtu, xdmf from .._common import error -from .._helpers import _filetype_from_path, read, reader_map +from .._helpers import _filetypes_from_path, read, reader_map def add_args(parser): @@ -26,8 +26,12 @@ def add_args(parser): def compress(args): - # read mesh data - fmt = args.input_format or _filetype_from_path(pathlib.Path(args.infile)) + if args.input_format: + fmts = [args.input_format] + else: + fmts = _filetypes_from_path(pathlib.Path(args.infile)) + # pick the first + fmt = fmts[0] size = os.stat(args.infile).st_size print(f"File size before: {size / 1024 ** 2:.2f} MB") diff --git a/src/meshio/_cli/_decompress.py b/src/meshio/_cli/_decompress.py index c84005dbc..3c67dda34 100644 --- a/src/meshio/_cli/_decompress.py +++ b/src/meshio/_cli/_decompress.py @@ -3,7 +3,7 @@ from .. import cgns, h5m, vtu, xdmf from .._common import error -from .._helpers import _filetype_from_path, read, reader_map +from .._helpers import _filetypes_from_path, read, reader_map def add_args(parser): @@ -19,8 +19,12 @@ def add_args(parser): def decompress(args): - # read mesh data - fmt = args.input_format or _filetype_from_path(pathlib.Path(args.infile)) + if args.input_format: + fmts = [args.input_format] + else: + fmts = _filetypes_from_path(pathlib.Path(args.infile)) + # pick the first + fmt = fmts[0] size = os.stat(args.infile).st_size print(f"File size before: {size / 1024 ** 2:.2f} MB") diff --git a/src/meshio/_helpers.py b/src/meshio/_helpers.py index 8f23d4158..b67d807de 100644 --- a/src/meshio/_helpers.py +++ b/src/meshio/_helpers.py @@ -1,39 +1,59 @@ from __future__ import annotations -import pathlib +import sys +from pathlib import Path import numpy as np from numpy.typing import ArrayLike -from ._common import num_nodes_per_cell +from ._common import error, num_nodes_per_cell from ._exceptions import ReadError, WriteError from ._files import is_buffer from ._mesh import CellBlock, Mesh -extension_to_filetype = {} +extension_to_filetypes = {} reader_map = {} _writer_map = {} -def register_format(name: str, extensions: list[str], reader, writer_map): +def register_format( + format_name: str, extensions: list[str], reader, writer_map +) -> None: for ext in extensions: - extension_to_filetype[ext] = name + if ext not in extension_to_filetypes: + extension_to_filetypes[ext] = [] + extension_to_filetypes[ext].append(format_name) if reader is not None: - reader_map[name] = reader + reader_map[format_name] = reader + _writer_map.update(writer_map) -def _filetype_from_path(path: pathlib.Path): +def deregister_format(format_name: str): + for value in extension_to_filetypes.values(): + if format_name in value: + value.remove(format_name) + + if format_name in reader_map: + reader_map.pop(format_name) + + if format_name in _writer_map: + _writer_map.pop(format_name) + + +def _filetypes_from_path(path: Path) -> list[str]: ext = "" - out = None + out = [] for suffix in reversed(path.suffixes): ext = (suffix + ext).lower() - if ext in extension_to_filetype: - out = extension_to_filetype[ext] + try: + out += extension_to_filetypes[ext] + except KeyError: + pass - if out is None: - raise ReadError(f"Could not deduce file format from extension '{ext}'.") + if not out: + raise ReadError(f"Could not deduce file format from path '{path}'.") return out @@ -46,31 +66,48 @@ def read(filename, file_format: str | None = None): :returns mesh{2,3}d: The mesh data. """ if is_buffer(filename, "r"): - if file_format is None: - raise ReadError("File format must be given if buffer is used") - if file_format == "tetgen": - raise ReadError( - "tetgen format is spread across multiple files " - "and so cannot be read from a buffer" - ) - msg = f"Unknown file format '{file_format}'" - else: - path = pathlib.Path(filename) - if not path.exists(): - raise ReadError(f"File {filename} not found.") + return _read_buffer(filename, file_format) - if not file_format: - # deduce file format from extension - file_format = _filetype_from_path(path) + return _read_file(Path(filename), file_format) - msg = f"Unknown file format '{file_format}' of '{filename}'." +def _read_buffer(filename, file_format: str | None): + if file_format is None: + raise ReadError("File format must be given if buffer is used") + if file_format == "tetgen": + raise ReadError( + "tetgen format is spread across multiple files " + "and so cannot be read from a buffer" + ) if file_format not in reader_map: - raise ReadError(msg) + raise ReadError(f"Unknown file format '{file_format}'") return reader_map[file_format](filename) +def _read_file(path: Path, file_format: str | None): + if not path.exists(): + raise ReadError(f"File {path} not found.") + + if file_format: + possible_file_formats = [file_format] + else: + # deduce possible file formats from extension + possible_file_formats = _filetypes_from_path(path) + + for file_format in possible_file_formats: + if file_format not in reader_map: + raise ReadError(f"Unknown file format '{file_format}' of '{path}'.") + + try: + return reader_map[file_format](str(path)) + except ReadError: + pass + + error(f"Couldn't read file {path} as either of {', '.join(possible_file_formats)}") + sys.exit(1) + + def write_points_cells( filename, points: ArrayLike, @@ -113,10 +150,12 @@ def write(filename, mesh: Mesh, file_format: str | None = None, **kwargs): "tetgen format is spread across multiple files, and so cannot be written to a buffer" ) else: - path = pathlib.Path(filename) + path = Path(filename) if not file_format: - # deduce file format from extension - file_format = _filetype_from_path(path) + # deduce possible file formats from extension + file_formats = _filetypes_from_path(path) + # just take the first one + file_format = file_formats[0] try: writer = _writer_map[file_format] diff --git a/src/meshio/ansys/_ansys.py b/src/meshio/ansys/_ansys.py index c52716f50..3f8abe016 100644 --- a/src/meshio/ansys/_ansys.py +++ b/src/meshio/ansys/_ansys.py @@ -461,4 +461,4 @@ def write(filename, mesh, binary=True): first_index = last_index + 1 -register_format("ansys", [], read, {"ansys": write}) +register_format("ansys", [".msh"], read, {"ansys": write}) diff --git a/tests/test_public.py b/tests/test_public.py index d60fafd8e..ec6f14091 100644 --- a/tests/test_public.py +++ b/tests/test_public.py @@ -3,4 +3,4 @@ def test_public_attributes(): # Just make sure this is here - meshio.extension_to_filetype + meshio.extension_to_filetypes