Skip to content

Commit

Permalink
setup proper logging
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzocerrone committed Nov 4, 2024
1 parent 5daa021 commit 18f2e1f
Show file tree
Hide file tree
Showing 16 changed files with 119 additions and 30 deletions.
6 changes: 5 additions & 1 deletion src/ngio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
"""Next Generation file format IO."""

import os
from importlib.metadata import PackageNotFoundError, version

from ngio.core import Image, Label, NgffImage
from ngio.utils import ngio_logger, set_logger_level

__all__ = ["Image", "Label", "NgffImage"]
__all__ = ["Image", "Label", "NgffImage", "set_logger_level", "ngio_logger"]

set_logger_level(os.getenv("NGIO_LOGGER_LEVEL", "WARNING"))

try:
__version__ = version("ngio")
Expand Down
2 changes: 1 addition & 1 deletion src/ngio/core/image_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

from typing import Any, Literal

from ngio._common_types import ArrayLike
from ngio.core.image_like_handler import ImageLike
from ngio.core.roi import WorldCooROI
from ngio.io import StoreOrGroup
from ngio.ngff_meta import PixelSize
from ngio.ngff_meta.fractal_image_meta import ImageMeta
from ngio.utils._common_types import ArrayLike


class Image(ImageLike):
Expand Down
2 changes: 1 addition & 1 deletion src/ngio/core/image_like_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import zarr
from dask.delayed import Delayed

from ngio._common_types import ArrayLike
from ngio.core.dimensions import Dimensions
from ngio.core.roi import WorldCooROI
from ngio.core.utils import Lock
Expand All @@ -22,6 +21,7 @@
get_ngff_image_meta_handler,
)
from ngio.pipes import DataTransformPipe, NaiveSlicer, RoiSlicer, on_disk_zoom
from ngio.utils._common_types import ArrayLike


class ImageLike:
Expand Down
11 changes: 7 additions & 4 deletions src/ngio/core/label_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

import zarr

from ngio._common_types import ArrayLike
from ngio.core.image_handler import Image
from ngio.core.image_like_handler import ImageLike
from ngio.core.roi import WorldCooROI
from ngio.core.utils import create_empty_ome_zarr_label
from ngio.io import StoreLike, StoreOrGroup
from ngio.io import AccessModeLiteral, StoreLike, StoreOrGroup
from ngio.ngff_meta.fractal_image_meta import LabelMeta, PixelSize
from ngio.utils._common_types import ArrayLike


class Label(ImageLike):
Expand Down Expand Up @@ -209,16 +209,19 @@ def __init__(
group: StoreLike | zarr.Group,
image_ref: Image | None = None,
cache: bool = True,
mode: AccessModeLiteral = "r+",
) -> None:
"""Initialize the LabelGroupHandler."""
self._mode = mode
if not isinstance(group, zarr.Group):
group = zarr.open_group(group, mode="a")
group = zarr.open_group(group, mode=self._mode)

if "labels" not in group:
self._group = group.create_group("labels")
self._group.attrs["labels"] = [] # initialize the labels attribute
else:
self._group: zarr.Group = group["labels"]
self._group = group["labels"]
assert isinstance(self._group, zarr.Group)

self._image_ref = image_ref
self._metadata_cache = cache
Expand Down
13 changes: 8 additions & 5 deletions src/ngio/core/ngff_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ngio.core.image_handler import Image
from ngio.core.label_handler import LabelGroup
from ngio.core.utils import create_empty_ome_zarr_image
from ngio.io import StoreLike, open_group_wrapper
from ngio.io import AccessModeLiteral, StoreLike, open_group_wrapper
from ngio.ngff_meta import get_ngff_image_meta_handler
from ngio.ngff_meta.fractal_image_meta import ImageMeta, PixelSize
from ngio.tables.tables_group import TableGroup
Expand All @@ -14,16 +14,19 @@
class NgffImage:
"""A class to handle OME-NGFF images."""

def __init__(self, store: StoreLike, cache: bool = False) -> None:
def __init__(
self, store: StoreLike, cache: bool = False, mode: AccessModeLiteral = "r+"
) -> None:
"""Initialize the NGFFImage in read mode."""
self.store = store
self.group = open_group_wrapper(store=store, mode="r+")
self._mode = mode
self.group = open_group_wrapper(store=store, mode=self._mode)
self._image_meta = get_ngff_image_meta_handler(
self.group, meta_mode="image", cache=cache
)
self._metadata_cache = cache
self.table = TableGroup(self.group)
self.label = LabelGroup(self.group, image_ref=self.get_image())
self.table = TableGroup(self.group, mode=self._mode)
self.label = LabelGroup(self.group, image_ref=self.get_image(), mode=self._mode)

@property
def image_meta(self) -> ImageMeta:
Expand Down
2 changes: 1 addition & 1 deletion src/ngio/ngff_meta/fractal_image_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from pydantic import BaseModel, Field
from typing_extensions import Self

from ngio.pydantic_utils import BaseWithExtraFields
from ngio.utils._pydantic_utils import BaseWithExtraFields


class Channel(BaseWithExtraFields):
Expand Down
2 changes: 1 addition & 1 deletion src/ngio/ngff_meta/v04/specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from pydantic import BaseModel, Field, field_validator

from ngio.pydantic_utils import (
from ngio.utils._pydantic_utils import (
SKIP_NGFF_VALIDATION,
BaseWithExtraFields,
unique_items_validator,
Expand Down
2 changes: 1 addition & 1 deletion src/ngio/pipes/_slicer_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import dask
from dask.delayed import Delayed

from ngio._common_types import ArrayLike
from ngio.utils._common_types import ArrayLike
from ngio.core.roi import RasterCooROI
import zarr

Expand Down
2 changes: 1 addition & 1 deletion src/ngio/pipes/_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from scipy.ndimage import zoom

from ngio._common_types import ArrayLike
from ngio.utils._common_types import ArrayLike


class Transform(Protocol):
Expand Down
2 changes: 1 addition & 1 deletion src/ngio/pipes/data_pipe.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""A module to handle data transforms for image data."""

from ngio._common_types import ArrayLike
from ngio.pipes._slicer_transforms import SlicerTransform
from ngio.pipes._transforms import Transform
from ngio.utils._common_types import ArrayLike


class DataTransformPipe:
Expand Down
22 changes: 14 additions & 8 deletions src/ngio/tables/tables_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import zarr
from pydantic import ValidationError

from ngio.io import StoreLike
from ngio.pydantic_utils import BaseWithExtraFields
from ngio.io import AccessModeLiteral, StoreLike
from ngio.tables.v1 import FeatureTableV1, MaskingROITableV1, ROITableV1
from ngio.utils._pydantic_utils import BaseWithExtraFields

ROITable = ROITableV1
IMPLEMENTED_ROI_TABLES = {"1": ROITableV1}
Expand All @@ -30,12 +30,12 @@
class CommonMeta(BaseWithExtraFields):
"""Common metadata for all tables."""

type: Literal["roi_table", "feature_table", "masking_roi_table"]
type: TableType
fractal_table_version: str = "1"


def _find_table_impl(
table_type: Literal["roi_table", "feature_table", "masking_roi_table"],
table_type: TableType,
version: str,
) -> Table:
"""Find the type of table in the group."""
Expand Down Expand Up @@ -87,10 +87,13 @@ def _get_table_impl(
class TableGroup:
"""A class to handle the /labels group in an OME-NGFF file."""

def __init__(self, group: StoreLike | zarr.Group) -> None:
def __init__(
self, group: StoreLike | zarr.Group, mode: AccessModeLiteral = "r+"
) -> None:
"""Initialize the LabelGroupHandler."""
self._mode = mode
if not isinstance(group, zarr.Group):
group = zarr.open_group(group, mode="a")
group = zarr.open_group(group, mode=self._mode)

if "tables" not in group:
self._group = group.create_group("tables")
Expand All @@ -109,6 +112,8 @@ def _get_list_of_tables(self) -> list[str]:
"""Return the list of tables."""
list_of_tables = self._group.attrs.get("tables", [])
self._validate_list_of_tables(list_of_tables)
assert isinstance(list_of_tables, list)
assert all(isinstance(table_name, str) for table_name in list_of_tables)
return list_of_tables

def list(
Expand Down Expand Up @@ -183,10 +188,10 @@ def get_table(
def new(
self,
name: str,
table_type: str = "roi_table",
table_type: TableType = "roi_table",
overwrite: bool = False,
version: str = "1",
**type_specific_kwargs,
**type_specific_kwargs: dict,
) -> Table:
"""Add a new table to the group."""
list_of_tables = self._get_list_of_tables()
Expand All @@ -206,4 +211,5 @@ def new(

self._group.attrs["tables"] = [*list_of_tables, name]

assert isinstance(new_table, ROITable | FeatureTable | MaskingROITable)
return new_table
13 changes: 13 additions & 0 deletions src/ngio/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Various utilities for the ngio package."""

from ngio.utils._common_types import ArrayLike
from ngio.utils._logger import ngio_logger, set_logger_level
from ngio.utils._pydantic_utils import BaseWithExtraFields, unique_items_validator

__all__ = [
"ArrayLike",
"BaseWithExtraFields",
"unique_items_validator",
"ngio_logger",
"set_logger_level",
]
File renamed without changes.
31 changes: 31 additions & 0 deletions src/ngio/utils/_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Create a generic error class for the NGFF project


class NgioError(Exception):
"""Base class for all errors in the NGFF project."""

pass


class NgioFileNotFoundError(NgioError):
"""Error raised when a file is not found."""

pass


class NgioFileExistsError(NgioError):
"""Error raised when a file already exists."""

pass


class NgioNGFFValidationError(NgioError):
"""Error raised when a file does not pass validation."""

pass


class NgioTableValidationError(NgioError):
"""Error raised when a table does not pass validation."""

pass
29 changes: 29 additions & 0 deletions src/ngio/utils/_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import logging

# Configure the logger
ngio_logger = logging.getLogger("NgioLogger")
ngio_logger.setLevel(logging.ERROR)

# Set up a console handler with a custom format
console_handler = logging.StreamHandler()
formatter = logging.Formatter(
"%(asctime)s - %(levelname)s - %(name)s - "
"[%(module)s.%(funcName)s:%(lineno)d]: %(message)s"
)
console_handler.setFormatter(formatter)

# Add the handler to the logger
ngio_logger.addHandler(console_handler)


def set_logger_level(level: str) -> None:
"""Set the logger level.
Args:
level: The level to set the logger to.
Must be one of "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL".
"""
if level not in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
raise ValueError(f"Invalid log level: {level}")

ngio_logger.setLevel(level)
10 changes: 5 additions & 5 deletions src/ngio/pydantic_utils.py → src/ngio/utils/_pydantic_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import os
from collections import namedtuple
from typing import TypeVar
from typing import Any, NamedTuple, TypeVar

from pydantic import BaseModel, Field, model_serializer, model_validator

Expand All @@ -21,13 +21,13 @@ class BaseWithExtraFields(BaseModel):
extra_fields: dict = Field(default_factory=dict)

@model_validator(mode="before")
def _collect_extra_fields(cls, values):
def _collect_extra_fields(cls, values: dict[str, Any]) -> dict[str, Any]:
extra = {k: v for k, v in values.items() if k not in cls.model_fields}
values["extra_fields"] = extra
return values

@model_serializer(mode="wrap")
def _custom_serializer(self, handler):
def _custom_serializer(self, handler: Any) -> dict[str, Any]:
basic_dict = handler(self)
extra = basic_dict.pop("extra_fields")
return {**basic_dict, **extra}
Expand All @@ -47,6 +47,6 @@ def unique_items_validator(values: list[T]) -> list[T]:
return values


def named_tuple_from_pydantic_model(model: BaseModel) -> namedtuple:
def named_tuple_from_pydantic_model(model: BaseModel) -> NamedTuple:
"""Create a namedtuple from a Pydantic model."""
return namedtuple(name=model.__name__, field_names=model.model_fields.keys())
return namedtuple(name=model.__name__, field_names=model.model_fields.keys()) # type: ignore

0 comments on commit 18f2e1f

Please sign in to comment.