From 07737691d5309c6029a1c6f8a1eaeeac9a6b1eb1 Mon Sep 17 00:00:00 2001 From: mferrera Date: Wed, 14 Feb 2024 09:54:07 +0100 Subject: [PATCH] ENH: Give more informative file format errors This will list some potential file formats, which are not complete in their file extensions permutations but should be sufficient enough to give a hint as to what kind of required. --- src/xtgeo/common/exceptions.py | 4 ++++ src/xtgeo/cube/cube1.py | 6 +++++- src/xtgeo/grid3d/_grid_import.py | 14 +++++++++++--- src/xtgeo/grid3d/grid_properties.py | 12 ++++++++++-- src/xtgeo/grid3d/grid_property.py | 7 ++++++- src/xtgeo/io/_file.py | 11 +++++++---- src/xtgeo/surface/regular_surface.py | 7 ++++++- src/xtgeo/well/_well_aux.py | 9 +++++---- src/xtgeo/xyz/points.py | 6 +++++- src/xtgeo/xyz/polygons.py | 6 +++++- tests/test_grid3d/test_grid_properties.py | 4 ++-- .../test_grid3d/test_gridprops_list_properties.py | 5 +++-- tests/test_io/test_file.py | 7 ++++--- 13 files changed, 73 insertions(+), 25 deletions(-) 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..04a7d95ee 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,10 @@ 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") + raise InvalidFileFormatError( + f"File format {fmt} is invalid for type Cube. Supported formats are " + "'segy', 'storm', 'xtg'." + ) 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..dab72e535 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,13 @@ 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: + raise InvalidFileFormatError( + f"File format {fformat} is invalid for type Grid. Supported formats are " + "'roff', 'roff_binary', 'roff_ascii', " + "'egrid', 'fegrid', 'grdecl', 'bgrdecl'" + "'hdf', 'xtg'." + ) if gfile.memstream: result["name"] = "unknown" diff --git a/src/xtgeo/grid3d/grid_properties.py b/src/xtgeo/grid3d/grid_properties.py index c1666787a..ee3ac6ff4 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,10 @@ def list_gridproperties( FileFormat.UNRST, ): return list(read_eclrun_properties(pfile)) - raise ValueError(f"Cannot read properties from file format {fformat}") + raise InvalidFileFormatError( + f"File format {fformat} is invalid for type GridProperties. Supported formats " + " are 'roff', 'roff_ascii', 'roff_binary', 'init', 'finit', 'unrst', 'funrst'." + ) def gridproperties_from_file( @@ -151,7 +155,11 @@ def gridproperties_from_file( maxkeys=MAXKEYWORDS, ) ) - raise ValueError("Invalid file format {fformat}") + raise InvalidFileFormatError( + f"File format {fformat} is invalid for type GridProperties. " + "Supported formats are 'roff', 'roff_ascii', 'roff_binary', " + "'init', 'finit', 'unrst', 'funrst'." + ) # -------------------------------------------------------------------------------------- diff --git a/src/xtgeo/grid3d/grid_property.py b/src/xtgeo/grid3d/grid_property.py index c082eda47..6101382c4 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,11 @@ 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}") + raise InvalidFileFormatError( + f"File format {fformat} is invalid for type GridProperty. Supported formats are" + " 'roff', 'roff_binary', 'roff_ascii', 'init', 'finit', 'unrst', 'funrst', " + "'grdecl', 'bgrdecl', 'xtg'." + ) def gridproperty_from_file( diff --git a/src/xtgeo/io/_file.py b/src/xtgeo/io/_file.py index 665afcaee..d17c78696 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: @@ -434,8 +435,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 +454,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 +494,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..77862305c 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,11 @@ 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}") + raise InvalidFileFormatError( + f"File format {file_format} is invalid for type RegularSurface. Supported " + "formats are 'irap', 'irap_binary', 'irap_ascii', 'ijxyz', 'zmap', 'xtg', " + "'hdf'." + ) def _allow_deprecated_init(func): diff --git a/src/xtgeo/well/_well_aux.py b/src/xtgeo/well/_well_aux.py index f6e976175..304b8e806 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,13 @@ 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'" + raise InvalidFileFormatError( + f"File format {file_format} is invalid for type Well. Supported formats are " + "'rmswell', 'irap_ascii', 'hdf'." ) diff --git a/src/xtgeo/xyz/points.py b/src/xtgeo/xyz/points.py index 8d519438b..bff7dfd30 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,10 @@ 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}") + raise InvalidFileFormatError( + f"File format {file_format} is invalid for type Points. Supported formats are " + "'xyz', 'zmap', 'rms_attr'." + ) def _file_importer( diff --git a/src/xtgeo/xyz/polygons.py b/src/xtgeo/xyz/polygons.py index 25ebf8b53..fc6bd40bf 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,10 @@ 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}") + raise InvalidFileFormatError( + f"File format {file_format} is invalid for type Points. Supported formats are " + "'xyz', 'zmap'" + ) 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()