Skip to content

Snappy

s3lst_ds.utilities.snappy_utils

Functions:

Name Description
import_esa_snappy

Import esa_snappy package and suppress respective future warnings.

import_esa_snappy

import_esa_snappy(logger: RichLogger | None = None) -> ModuleType

Import esa_snappy package and suppress respective future warnings.

NOTE: the import of esa_snappy retrieves Java logging WARNING and INFO messages. These are irrelevant and seem to be insuppressible. For more details read this thread.

Parameters:

Name Type Description Default
logger RichLogger or None

A rich logger for showing the progress of the processing.

None

Returns:

Type Description
ModuleType

The imported esa_snappy module.

Raises:

Type Description
ImportError

If esa_snappy is not installed.

Source code in src/s3lst_ds/utilities/snappy_utils.py
def import_esa_snappy(logger: RichLogger | None = None) -> ModuleType:
    """
    Import `esa_snappy` package and suppress respective future warnings.

    NOTE: the import of `esa_snappy` retrieves Java logging `WARNING` and `INFO`
    messages. These are irrelevant and seem to be insuppressible. For more details read
    [this thread](https://forum.step.esa.int/t/snap-gpt-warning/43343/4).

    Parameters
    ----------
    logger : RichLogger or None, default=None
        A rich logger for showing the progress of the processing.

    Returns
    -------
    ModuleType
        The imported `esa_snappy` module.

    Raises
    ------
    ImportError
        If `esa_snappy` is not installed.
    """

    try:
        # ---> Import snappy package while suppressing Java logging messages
        # NOTE: the import of `esa_snappy` retrieves Java logging `WARNING` and `INFO`
        # messages. These are actually irrelevant. For more details read [this
        # thread](https://forum.step.esa.int/t/snap-gpt-warning/43343/4).

        # Open the operating system's null device
        # NOTE: Any output written to this file is discarded immediately.
        # NOTE: https://docs.python.org/3/library/os.html#os.open
        devnull = os.open(
            path=os.devnull,
            # Use write-only mode
            flags=os.O_WRONLY,
        )

        # Get duplicates of the current stdout (which has file descriptor fd=1) and
        # stderr (which has file descriptor fd=2) so they can be restored later.
        # NOTE: https://docs.python.org/3/library/os.html#os.dup
        old_stdout = os.dup(1)
        old_stderr = os.dup(2)

        try:
            # Regard the null dev as stdout (1) and stderr (2) so that any output
            # written to them is discarded.
            # NOTE: https://docs.python.org/3/library/os.html#os.dup2
            os.dup2(fd=devnull, fd2=1)
            os.dup2(fd=devnull, fd2=2)

            # Import snappy package
            esa_snappy = import_module("esa_snappy")

        finally:
            # Restore stdout and stderr
            # NOTE: https://docs.python.org/3/library/os.html#os.dup2
            os.dup2(fd=old_stdout, fd2=1)
            os.dup2(fd=old_stderr, fd2=2)

            # Close the the duplicates and the null device
            os.close(old_stdout)
            os.close(old_stderr)
            os.close(devnull)

        # ---> Supress snappy warnings by setting minimum logging level to SEVERE
        # instead of INFO
        LogManager = esa_snappy.jpy.get_type("java.util.logging.LogManager")
        Level = esa_snappy.jpy.get_type("java.util.logging.Level")
        snap_logger = LogManager.getLogManager().getLogger("org.esa.snap")
        snap_logger.setLevel(Level.SEVERE)

        # ---> Suppress snappy printed messages
        System = esa_snappy.jpy.get_type("java.lang.System")
        PrintStream = esa_snappy.jpy.get_type("java.io.PrintStream")
        NullStream = esa_snappy.jpy.get_type(
            "org.apache.commons.io.output.NullOutputStream"
        )
        System.setOut(PrintStream(NullStream()))
        System.setErr(PrintStream(NullStream()))

        return esa_snappy

    except (ImportError, OSError) as e:
        if logger is not None:
            logger.error(
                "[bold red]This error is most likely caused by a failure in importing"
                + " `esa_snappy` package."
                + f"\nError message: {e}"
                + "\nRequested utilities from the `esa_snappy` Python package may be"
                + " unavailable because the package or the Java backend SNAP are not"
                + " installed or, if installed, not configured."
                + "\n"
                + "\nRun `uv pip install esa-snappy` to install `esa-snappy`"
                + " package, if missing."
                + "\nRun `uv run s3lst-ds-install-snap` to install and configure `SNAP`"
                + " if it is missing, or to just configure it if it exists but it is"
                + " not configured."
                + "[/bold red]",
            )
        raise ImportError(
            "This error is most likely caused by a failure in importing  `esa_snappy`"
            + " package."
            + f"\nError message: {e}"
            + "\nRequested utilities from the `esa_snappy` Python package may be"
            + " unavailable because the package or the Java backend `SNAP` are not"
            + " installed or, if installed, not configured."
            + "\n"
            + "\nRun `uv pip install esa-snappy` to install `esa-snappy`"
            + " package, if missing."
            + "\nRun `uv run s3lst-ds-install-snap` to install and configure `SNAP`"
            + " if it is missing, or to just configure it if it exists but it is"
            + " not configured."
        )