Skip to content

Commit

Permalink
ENH: Give more informative file format errors
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mferrera committed Feb 14, 2024
1 parent 33652a2 commit 335dbff
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 25 deletions.
2 changes: 2 additions & 0 deletions src/xtgeo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def _xprint(msg):
BlockedWellsNotFoundError,
DateNotFoundError,
GridNotFoundError,
InvalidFileFormatError,
KeywordFoundNoDateError,
KeywordNotFoundError,
WellNotFoundError,
Expand Down Expand Up @@ -191,6 +192,7 @@ def _xprint(msg):
"GridProperty",
"GridRelative",
"GridRelative",
"InvalidFileFormatError",
"KeywordFoundNoDateError",
"KeywordNotFoundError",
"MetaDataCPGeometry",
Expand Down
4 changes: 4 additions & 0 deletions src/xtgeo/common/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
10 changes: 9 additions & 1 deletion src/xtgeo/cube/cube1.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"):
Expand Down
24 changes: 21 additions & 3 deletions src/xtgeo/grid3d/_grid_import.py
Original file line number Diff line number Diff line change
@@ -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__)

Expand Down Expand Up @@ -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"
Expand Down
22 changes: 20 additions & 2 deletions src/xtgeo/grid3d/grid_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -79,7 +80,20 @@ 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(
Expand Down Expand Up @@ -151,7 +165,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'."
)


# --------------------------------------------------------------------------------------
Expand Down
19 changes: 18 additions & 1 deletion src/xtgeo/grid3d/grid_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -95,7 +96,23 @@ 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(
Expand Down
15 changes: 11 additions & 4 deletions src/xtgeo/io/_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
"""
Expand Down Expand Up @@ -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

Expand All @@ -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."""
Expand Down Expand Up @@ -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)

Expand Down
18 changes: 17 additions & 1 deletion src/xtgeo/surface/regular_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__
Expand Down Expand Up @@ -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):
Expand Down
13 changes: 9 additions & 4 deletions src/xtgeo/well/_well_aux.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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}."
)


Expand Down
10 changes: 9 additions & 1 deletion src/xtgeo/xyz/points.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__
Expand All @@ -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(
Expand Down
8 changes: 7 additions & 1 deletion src/xtgeo/xyz/polygons.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__
Expand All @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions tests/test_grid3d/test_grid_properties.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Testing: test_grid_operations"""


import io
import sys

Expand All @@ -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
Expand Down Expand Up @@ -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")


Expand Down
5 changes: 3 additions & 2 deletions tests/test_grid3d/test_gridprops_list_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)


Expand Down
7 changes: 4 additions & 3 deletions tests/test_io/test_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import pytest
import xtgeo
from xtgeo.common.exceptions import InvalidFileFormatError
from xtgeo.io._file import FileFormat, FileWrapper

xtg = xtgeo.XTGeoDialog()
Expand Down Expand Up @@ -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()


Expand Down

0 comments on commit 335dbff

Please sign in to comment.