Skip to content

Commit

Permalink
remove defaults in deps and add .as_dict method to the `DefaultDepe…
Browse files Browse the repository at this point in the history
…ndency` class (developmentseed#819)

* remove defaults in deps and add `kwargs` property

* rename kwargs to as_dict()
  • Loading branch information
vincentsarago authored Jul 9, 2024
1 parent a03de92 commit bce7c75
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 143 deletions.
32 changes: 32 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
# Release Notes

## Unreleased

* Remove all default values to the dependencies
* `DatasetParams.unscale`: `False` -> `None` (default to `False` in rio-tiler)
* `DatasetParams.resampling_method`: `nearest` -> `None` (default to `nearest` in rio-tiler)
* `DatasetParams.reproject_method`: `nearest` -> `None` (default to `nearest` in rio-tiler)
* `ImageRenderingParams.add_mask`: `True` -> `None` (default to `True` in rio-tiler)
* `StatisticsParams.categorical`: `False` -> `None` (default to `False` in rio-tiler)

* Add `as_dict(exclude_none=True/False)` method to the `DefaultDependency` class.

```python
from typing import Optional
from titiler.core.dependencies import DefaultDependency
from dataclasses import dataclass

@dataclass
class Deps(DefaultDependency):
value: Optional[int] = None

print({**Deps().__dict__.items()})
>> {'value': None}

Deps().as_dict() # `exclude_none` defaults to True
>> {}

Deps(value=1).as_dict()
>> {'value': 1}
```

* Use `.as_dict()` method when passing option to rio-tiler Reader's methods to avoid parameter conflicts when using custom Readers.

## 0.18.5 (2024-07-03)

* Set version requirement for FastAPI to `>=0.111.0`
Expand Down
8 changes: 4 additions & 4 deletions docs/src/advanced/Extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,11 @@ class thumbnailExtension(FactoryExtension):
env=Depends(factory.environment_dependency),
):
with rasterio.Env(**env):
with factory.reader(src_path, **reader_params) as src:
with factory.reader(src_path, **reader_params.as_dict()) as src:
image = src.preview(
max_size=self.max_size,
**layer_params,
**dataset_params,
**layer_params.as_dict(),
**dataset_params.as_dict(),
)

if post_process:
Expand All @@ -163,7 +163,7 @@ class thumbnailExtension(FactoryExtension):
img_format=format.driver,
colormap=colormap,
**format.profile,
**render_params,
**render_params.as_dict(),
)

return Response(content, media_type=format.mediatype)
Expand Down
1 change: 0 additions & 1 deletion docs/src/advanced/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import re

from fastapi import FastAPI, HTTPException, Query

from titiler.core.dependencies import DefaultDependency
from titiler.mosaic.factory import MosaicTilerFactory


Expand Down
34 changes: 18 additions & 16 deletions docs/src/advanced/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def preview(
params: ImageParams = Depends(),
):
with Reader(url) as cog:
img = cog.preview(**params) # classes built with `DefaultDependency` can be unpacked
img = cog.preview(**params.as_dict()) # we use `DefaultDependency().as_dict()` to pass only non-None parameters
# or
img = cog.preview(max_size=params.max_size)
...
Expand All @@ -36,7 +36,7 @@ def preview(

In the example above, we create a custom `ImageParams` dependency which will then be injected to the `preview` endpoint to add **max_size**, **height** and **width** query string parameters.

Using `titiler.core.dependencies.DefaultDependency`, we can `unpack` the class as if it was a dictionary, which helps with customization.
Using `titiler.core.dependencies.DefaultDependency`, we can use `.as_dict(exclude_none=True/False)` method to `unpack` the object parameters. This can be useful if method or reader do not take the same parameters.

#### AssetsParams

Expand Down Expand Up @@ -547,29 +547,31 @@ class DatasetParams(DefaultDependency):
bool,
Query(
title="Apply internal Scale/Offset",
description="Apply internal Scale/Offset. Defaults to `False`.",
description="Apply internal Scale/Offset. Defaults to `False` in rio-tiler.",
),
] = False
resampling_method: Annotated[
RIOResampling,
Optional[RIOResampling],
Query(
alias="resampling",
description="RasterIO resampling algorithm. Defaults to `nearest`.",
description="RasterIO resampling algorithm. Defaults to `nearest` in rio-tiler.",
),
] = "nearest"
] = None
reproject_method: Annotated[
WarpResampling,
Optional[WarpResampling],
Query(
alias="reproject",
description="WarpKernel resampling algorithm (only used when doing re-projection). Defaults to `nearest`.",
description="WarpKernel resampling algorithm (only used when doing re-projection). Defaults to `nearest` in rio-tiler.",
),
] = "nearest"
] = None

def __post_init__(self):
"""Post Init."""
if self.nodata is not None:
self.nodata = numpy.nan if self.nodata == "nan" else float(self.nodata)
self.unscale = bool(self.unscale)

if self.unscale is not None:
self.unscale = bool(self.unscale)
```

</details>
Expand Down Expand Up @@ -719,12 +721,12 @@ class ImageRenderingParams(DefaultDependency):
"""Image Rendering options."""

add_mask: Annotated[
bool,
Optional[bool],
Query(
alias="return_mask",
description="Add mask to the output data. Defaults to `True`",
description="Add mask to the output data. Defaults to `True` in rio-tiler",
),
] = True
] = None
```

</details>
Expand Down Expand Up @@ -861,9 +863,9 @@ class StatisticsParams(DefaultDependency):
"""Statistics options."""

categorical: Annotated[
bool,
Query(description="Return statistics for categorical dataset."),
] = False
Optional[bool],
Query(description="Return statistics for categorical dataset. Defaults to `False` in rio-tiler"),
] = None
categories: Annotated[
Optional[List[Union[float, int]]],
Query(
Expand Down
4 changes: 2 additions & 2 deletions src/titiler/core/tests/test_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ def is_nan(params=Depends(dependencies.DatasetParams)):
response = client.get("/")
assert not response.json()["nodata"]
assert not response.json()["unscale"]
assert response.json()["resampling_method"] == "nearest"
assert not response.json()["resampling_method"]

response = client.get("/?resampling=cubic")
assert not response.json()["nodata"]
Expand All @@ -447,7 +447,7 @@ def _endpoint(params=Depends(dependencies.ImageRenderingParams)):

client = TestClient(app)
response = client.get("/")
assert response.json()["add_mask"] is True
assert not response.json()["add_mask"]

response = client.get("/?return_mask=False")
assert response.json()["add_mask"] is False
Expand Down
39 changes: 27 additions & 12 deletions src/titiler/core/titiler/core/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,23 @@ class DefaultDependency:

def keys(self):
"""Return Keys."""
warnings.warn(
"Dict unpacking will be removed for `DefaultDependency` in titiler 0.19.0",
DeprecationWarning,
)
return self.__dict__.keys()

def __getitem__(self, key):
"""Return value."""
return self.__dict__[key]

def as_dict(self, exclude_none: bool = True) -> Dict:
"""Transform dataclass to dict."""
if exclude_none:
return {k: v for k, v in self.__dict__.items() if v is not None}

return dict(self.__dict__.items())


# Dependencies for simple BaseReader (e.g COGReader)
@dataclass
Expand Down Expand Up @@ -380,45 +391,47 @@ class DatasetParams(DefaultDependency):
),
] = None
unscale: Annotated[
bool,
Optional[bool],
Query(
title="Apply internal Scale/Offset",
description="Apply internal Scale/Offset. Defaults to `False`.",
),
] = False
] = None
resampling_method: Annotated[
RIOResampling,
Optional[RIOResampling],
Query(
alias="resampling",
description="RasterIO resampling algorithm. Defaults to `nearest`.",
),
] = "nearest"
] = None
reproject_method: Annotated[
WarpResampling,
Optional[WarpResampling],
Query(
alias="reproject",
description="WarpKernel resampling algorithm (only used when doing re-projection). Defaults to `nearest`.",
),
] = "nearest"
] = None

def __post_init__(self):
"""Post Init."""
if self.nodata is not None:
self.nodata = numpy.nan if self.nodata == "nan" else float(self.nodata)
self.unscale = bool(self.unscale)

if self.unscale is not None:
self.unscale = bool(self.unscale)


@dataclass
class ImageRenderingParams(DefaultDependency):
"""Image Rendering options."""

add_mask: Annotated[
bool,
Optional[bool],
Query(
alias="return_mask",
description="Add mask to the output data. Defaults to `True`",
),
] = True
] = None


RescaleType = List[Tuple[float, ...]]
Expand Down Expand Up @@ -459,9 +472,11 @@ class StatisticsParams(DefaultDependency):
"""Statistics options."""

categorical: Annotated[
bool,
Query(description="Return statistics for categorical dataset."),
] = False
Optional[bool],
Query(
description="Return statistics for categorical dataset. Defaults to `False`"
),
] = None
categories: Annotated[
Optional[List[Union[float, int]]],
Query(
Expand Down
Loading

0 comments on commit bce7c75

Please sign in to comment.