diff --git a/src/xtgeo/__init__.py b/src/xtgeo/__init__.py index 5ad898852..b7a8ddc14 100644 --- a/src/xtgeo/__init__.py +++ b/src/xtgeo/__init__.py @@ -71,6 +71,7 @@ def _xprint(msg): BlockedWellsNotFoundError, DateNotFoundError, GridNotFoundError, + InvalidFileFormatError, KeywordFoundNoDateError, KeywordNotFoundError, WellNotFoundError, @@ -191,6 +192,7 @@ def _xprint(msg): "GridProperty", "GridRelative", "GridRelative", + "InvalidFileFormatError", "KeywordFoundNoDateError", "KeywordNotFoundError", "MetaDataCPGeometry", diff --git a/src/xtgeo/common/exceptions.py b/src/xtgeo/common/exceptions.py index 21f96918c..87ebe2c9b 100644 --- a/src/xtgeo/common/exceptions.py +++ b/src/xtgeo/common/exceptions.py @@ -36,3 +36,7 @@ class GridNotFoundError(ValueError): class BlockedWellsNotFoundError(ValueError): """Blocked Wells icon is not found in the request (ValueError)""" + + +class InvalidFileFormatError(ValueError): + """File format given for importing or exporting files is invalid or unknown.""" diff --git a/src/xtgeo/cube/cube1.py b/src/xtgeo/cube/cube1.py index 2c4e5f49e..1e1b96341 100644 --- a/src/xtgeo/cube/cube1.py +++ b/src/xtgeo/cube/cube1.py @@ -14,6 +14,7 @@ import numpy as np from xtgeo.common.constants import UNDEF +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.common.log import null_logger from xtgeo.common.sys import generic_hash from xtgeo.common.types import Dimensions @@ -39,7 +40,14 @@ def _data_reader_factory(fmt: FileFormat): return _cube_import.import_stormcube if fmt == FileFormat.XTG: return _cube_import.import_xtgregcube - raise ValueError("File format is not supported") + + extensions = FileFormat.extensions_string( + [FileFormat.SEGY, FileFormat.STORM, FileFormat.XTG] + ) + raise InvalidFileFormatError( + f"File format {fmt} is invalid for type Cube. " + f"Supported formats are {extensions}." + ) def cube_from_file(mfile, fformat="guess"): diff --git a/src/xtgeo/grid3d/_grid_import.py b/src/xtgeo/grid3d/_grid_import.py index 75a224f1b..fbff403bd 100644 --- a/src/xtgeo/grid3d/_grid_import.py +++ b/src/xtgeo/grid3d/_grid_import.py @@ -1,14 +1,15 @@ """Grid import functions for various formats.""" + from __future__ import annotations from pathlib import Path from typing import Any -from xtgeo.common import null_logger -from xtgeo.grid3d import _grid_import_ecl, _grid_import_roff +from xtgeo.common.exceptions import InvalidFileFormatError +from xtgeo.common.log import null_logger from xtgeo.io._file import FileFormat, FileWrapper -from . import _grid_import_xtgcpgeom +from . import _grid_import_ecl, _grid_import_roff, _grid_import_xtgcpgeom logger = null_logger(__name__) @@ -49,6 +50,23 @@ def from_file( result.update(_grid_import_xtgcpgeom.import_xtgcpgeom(gfile, **kwargs)) elif fformat == FileFormat.HDF: result.update(_grid_import_xtgcpgeom.import_hdf5_cpgeom(gfile, **kwargs)) + else: + extensions = FileFormat.extensions_string( + [ + FileFormat.ROFF_BINARY, + FileFormat.ROFF_ASCII, + FileFormat.EGRID, + FileFormat.FEGRID, + FileFormat.GRDECL, + FileFormat.BGRDECL, + FileFormat.XTG, + FileFormat.HDF, + ] + ) + raise InvalidFileFormatError( + f"File format {fformat} is invalid for type Grid. " + f"Supported formats are {extensions}." + ) if gfile.memstream: result["name"] = "unknown" diff --git a/src/xtgeo/grid3d/grid_properties.py b/src/xtgeo/grid3d/grid_properties.py index c1666787a..84511a13c 100644 --- a/src/xtgeo/grid3d/grid_properties.py +++ b/src/xtgeo/grid3d/grid_properties.py @@ -12,6 +12,7 @@ from xtgeo.common import XTGDescription, XTGeoDialog, null_logger from xtgeo.common.constants import MAXDATES, MAXKEYWORDS +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.common.version import __version__ from xtgeo.io._file import FileFormat, FileWrapper @@ -79,7 +80,21 @@ def list_gridproperties( FileFormat.UNRST, ): return list(read_eclrun_properties(pfile)) - raise ValueError(f"Cannot read properties from file format {fformat}") + + extensions = FileFormat.extensions_string( + [ + FileFormat.ROFF_BINARY, + FileFormat.ROFF_ASCII, + FileFormat.INIT, + FileFormat.FINIT, + FileFormat.UNRST, + FileFormat.FUNRST, + ] + ) + raise InvalidFileFormatError( + f"File format {fformat} is invalid for type GridProperties. " + f"Supported formats are {extensions}." + ) def gridproperties_from_file( @@ -151,7 +166,21 @@ def gridproperties_from_file( maxkeys=MAXKEYWORDS, ) ) - raise ValueError("Invalid file format {fformat}") + + extensions = FileFormat.extensions_string( + [ + FileFormat.ROFF_BINARY, + FileFormat.ROFF_ASCII, + FileFormat.INIT, + FileFormat.FINIT, + FileFormat.UNRST, + FileFormat.FUNRST, + ] + ) + raise InvalidFileFormatError( + f"File format {fformat} is invalid for type GridProperties. " + f"Supported formats are {extensions}." + ) # -------------------------------------------------------------------------------------- diff --git a/src/xtgeo/grid3d/grid_property.py b/src/xtgeo/grid3d/grid_property.py index c082eda47..3d1ae69b2 100644 --- a/src/xtgeo/grid3d/grid_property.py +++ b/src/xtgeo/grid3d/grid_property.py @@ -14,6 +14,7 @@ import xtgeo from xtgeo.common import XTGeoDialog, null_logger from xtgeo.common.constants import UNDEF, UNDEF_INT, UNDEF_INT_LIMIT, UNDEF_LIMIT +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.common.types import Dimensions from xtgeo.common.version import __version__ from xtgeo.io._file import FileFormat, FileWrapper @@ -95,7 +96,24 @@ def _data_reader_factory(fformat: FileFormat) -> Callable: return import_bgrdecl_prop if fformat == FileFormat.XTG: return import_xtgcpprop - raise ValueError(f"Invalid grid property file format {fformat}") + + extensions = FileFormat.extensions_string( + [ + FileFormat.ROFF_BINARY, + FileFormat.ROFF_ASCII, + FileFormat.INIT, + FileFormat.FINIT, + FileFormat.UNRST, + FileFormat.FUNRST, + FileFormat.GRDECL, + FileFormat.BGRDECL, + FileFormat.XTG, + ] + ) + raise InvalidFileFormatError( + f"File format {fformat} is invalid for type GridProperty. " + f"Supported formats are {extensions}." + ) def gridproperty_from_file( diff --git a/src/xtgeo/io/_file.py b/src/xtgeo/io/_file.py index 665afcaee..fd5b7586b 100644 --- a/src/xtgeo/io/_file.py +++ b/src/xtgeo/io/_file.py @@ -15,6 +15,7 @@ from typing import TYPE_CHECKING, Literal, Union import xtgeo._cxtgeo +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.common.log import null_logger if TYPE_CHECKING: @@ -70,6 +71,10 @@ class FileFormat(Enum): RMS_ATTR = ["rms_attr", "rms_attrs", "rmsattr.*"] UNKNOWN = ["unknown"] + @staticmethod + def extensions_string(formats: list[FileFormat]) -> str: + return ", ".join([f"'{item}'" for fmt in formats for item in fmt.value]) + class FileWrapper: """ @@ -434,8 +439,8 @@ def fileformat(self, fileformat: str | None = None) -> FileFormat: if fmt == FileFormat.UNKNOWN: fmt = self._format_from_contents() if fmt == FileFormat.UNKNOWN: - raise ValueError( - f"Unknown or unsupported file format for file {self._file}" + raise InvalidFileFormatError( + f"File format {fileformat} is unknown or unsupported" ) return fmt @@ -453,7 +458,9 @@ def _validate_fileformat(self, fileformat: str | None) -> None: for regex in fmt.value: if "*" in regex and re.compile(regex).match(fileformat): return - raise ValueError("Unknown or unsupported file format: {fileformat}") + raise InvalidFileFormatError( + f"File format {fileformat} is unknown or unsupported" + ) def _format_from_suffix(self, fileformat: str | None = None) -> FileFormat: """Detect format by the file suffix.""" @@ -491,7 +498,7 @@ def _format_from_contents(self) -> FileFormat: self.file.seek(mark) else: if not self.exists(): - raise ValueError(f"File {self.name} does not exist") + raise FileNotFoundError(f"File {self.name} does not exist") with open(self.file, "rb") as fhandle: fhandle.readinto(buffer) diff --git a/src/xtgeo/surface/regular_surface.py b/src/xtgeo/surface/regular_surface.py index 300a1f833..ed06ae15e 100644 --- a/src/xtgeo/surface/regular_surface.py +++ b/src/xtgeo/surface/regular_surface.py @@ -55,6 +55,7 @@ VERYLARGENEGATIVE, VERYLARGEPOSITIVE, ) +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.common.log import null_logger from xtgeo.common.sys import generic_hash from xtgeo.common.version import __version__ @@ -250,7 +251,22 @@ def _data_reader_factory(file_format: FileFormat): return _regsurf_import.import_xtg if file_format == FileFormat.HDF: return _regsurf_import.import_hdf5_regsurf - raise ValueError(f"Unknown file format {file_format}") + + extensions = FileFormat.extensions_string( + [ + FileFormat.IRAP_BINARY, + FileFormat.IRAP_ASCII, + FileFormat.IJXYZ, + FileFormat.PETROMOD, + FileFormat.ZMAP_ASCII, + FileFormat.XTG, + FileFormat.HDF, + ] + ) + raise InvalidFileFormatError( + f"File format {file_format} is invalid for type RegularSurface. " + f"Supported formats are {extensions}." + ) def _allow_deprecated_init(func): diff --git a/src/xtgeo/well/_well_aux.py b/src/xtgeo/well/_well_aux.py index f6e976175..8c1f8a3b3 100644 --- a/src/xtgeo/well/_well_aux.py +++ b/src/xtgeo/well/_well_aux.py @@ -14,6 +14,7 @@ import pandas as pd from xtgeo.common._xyz_enum import _AttrName +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.common.log import null_logger from xtgeo.io._file import FileFormat, FileWrapper @@ -26,13 +27,17 @@ def _data_reader_factory(file_format: FileFormat): - if file_format in (FileFormat.RMSWELL, FileFormat.IRAP_ASCII, FileFormat.UNKNOWN): + if file_format in (FileFormat.RMSWELL, FileFormat.IRAP_ASCII): return _well_io.import_rms_ascii if file_format == FileFormat.HDF: return _well_io.import_hdf5_well - raise ValueError( - f"Unknown file format {file_format}, supported formats are " - "'rmswell', 'irap_ascii' and 'hdf'" + + extensions = FileFormat.extensions_string( + [FileFormat.RMSWELL, FileFormat.IRAP_ASCII, FileFormat.HDF] + ) + raise InvalidFileFormatError( + f"File format {file_format} is invalid for Well types. " + f"Supported formats are {extensions}." ) diff --git a/src/xtgeo/xyz/points.py b/src/xtgeo/xyz/points.py index 8d519438b..a73d6ba65 100644 --- a/src/xtgeo/xyz/points.py +++ b/src/xtgeo/xyz/points.py @@ -13,6 +13,7 @@ import pandas as pd import xtgeo +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.common.log import null_logger from xtgeo.common.sys import inherit_docstring from xtgeo.common.version import __version__ @@ -32,7 +33,14 @@ def _data_reader_factory(file_format: FileFormat): return _xyz_io.import_zmap if file_format == FileFormat.RMS_ATTR: return _xyz_io.import_rms_attr - raise ValueError(f"Unknown file format {file_format}") + + extensions = FileFormat.extensions_string( + [FileFormat.XYZ, FileFormat.ZMAP_ASCII, FileFormat.RMS_ATTR] + ) + raise InvalidFileFormatError( + f"File format {file_format} is invalid for type Points. " + f"Supported formats are {extensions}." + ) def _file_importer( diff --git a/src/xtgeo/xyz/polygons.py b/src/xtgeo/xyz/polygons.py index 25ebf8b53..548d87ffa 100644 --- a/src/xtgeo/xyz/polygons.py +++ b/src/xtgeo/xyz/polygons.py @@ -16,6 +16,7 @@ import pandas as pd import shapely.geometry as sg +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.common.log import null_logger from xtgeo.common.sys import inherit_docstring from xtgeo.common.version import __version__ @@ -39,7 +40,12 @@ def _data_reader_factory(file_format: FileFormat): return _xyz_io.import_xyz if file_format == FileFormat.ZMAP_ASCII: return _xyz_io.import_zmap - raise ValueError(f"Unknown file format {file_format}") + + extensions = FileFormat.extensions_string([FileFormat.XYZ, FileFormat.ZMAP_ASCII]) + raise InvalidFileFormatError( + f"File format {file_format} is invalid for type Polygons. " + f"Supported formats are {extensions}." + ) def _file_importer( diff --git a/tests/test_grid3d/test_grid_properties.py b/tests/test_grid3d/test_grid_properties.py index eba81bbf0..f30d22d69 100644 --- a/tests/test_grid3d/test_grid_properties.py +++ b/tests/test_grid3d/test_grid_properties.py @@ -1,6 +1,5 @@ """Testing: test_grid_operations""" - import io import sys @@ -9,6 +8,7 @@ import xtgeo from hypothesis import assume, given from xtgeo.common import XTGeoDialog +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.grid3d import GridProperties, GridProperty from .grid_generator import xtgeo_grids @@ -121,7 +121,7 @@ def test_gridproperties_from_roff(grid_property): def test_gridproperties_invalid_format(grid_property): buff = io.BytesIO() grid_property.to_file(buff, fformat="roff") - with pytest.raises(ValueError, match="Invalid file format"): + with pytest.raises(InvalidFileFormatError, match="invalid for type GridProperties"): xtgeo.gridproperties_from_file(buff, fformat="segy") diff --git a/tests/test_grid3d/test_gridprops_list_properties.py b/tests/test_grid3d/test_gridprops_list_properties.py index 3fdcb6c32..d7a490d55 100644 --- a/tests/test_grid3d/test_gridprops_list_properties.py +++ b/tests/test_grid3d/test_gridprops_list_properties.py @@ -3,6 +3,7 @@ import pytest from xtgeo.common import XTGeoDialog +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.grid3d import list_gridproperties from xtgeo.grid3d._gridprops_import_roff import read_roff_properties from xtgeo.io._file import FileWrapper @@ -29,11 +30,11 @@ ROFF_THREE_PROPS = TPATH / "3dgrids/reek/reek_geo2_grid_3props.roff" -@pytest.mark.parametrize("test_file", ["A.EGRID", "b.grdecl", "t.segy", "c.RSSPEC"]) +@pytest.mark.parametrize("test_file", ["A.EGRID", "b.grdecl", "t.segy", "c.rms_attr"]) def test_raise_on_invalid_filetype(tmp_path, test_file): filepath = tmp_path / test_file Path(filepath).touch() - with pytest.raises(ValueError, match="file format"): + with pytest.raises(InvalidFileFormatError, match="invalid for type GridProperties"): list_gridproperties(filepath) diff --git a/tests/test_io/test_file.py b/tests/test_io/test_file.py index 37a809019..f149bce6b 100644 --- a/tests/test_io/test_file.py +++ b/tests/test_io/test_file.py @@ -5,6 +5,7 @@ import pytest import xtgeo +from xtgeo.common.exceptions import InvalidFileFormatError from xtgeo.io._file import FileFormat, FileWrapper xtg = xtgeo.XTGeoDialog() @@ -35,15 +36,15 @@ def fixture_reek_grid_path(testpath): def test_fileformat_unknown_empty_memstream(): - with pytest.raises(ValueError, match="Unknown or unsupported"): + with pytest.raises(InvalidFileFormatError, match="unknown or unsupported"): FileWrapper(io.StringIO()).fileformat() - with pytest.raises(ValueError, match="Unknown or unsupported"): + with pytest.raises(InvalidFileFormatError, match="unknown or unsupported"): FileWrapper(io.BytesIO()).fileformat() @pytest.mark.parametrize("length", [0, 4, 8, 24, 9]) def test_fileformat_unknown_zeroed_memstream_with_varied_length(length): - with pytest.raises(ValueError, match="Unknown or unsupported"): + with pytest.raises(InvalidFileFormatError, match="unknown or unsupported"): FileWrapper(io.BytesIO(b"\00" * length)).fileformat()