# Copyright (c) 2025 Centre National d'Etudes Spatiales (CNES).
#
# This file is part of PANDORA
#
# https://github.com/CNES/Pandora
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""
This module contains descriptors which returns Margins.
Descriptors are a kind of properties object that can be reused in several classes.
They meant to be used as class attributes.
"""
from __future__ import annotations
from typing import overload
from pandora.margins import Margins
# In order to make a descriptor instance aware of the name it is affected to,
# we can use the dunder method `__set_name__` which is called at affectation.
# When the attribute the descriptor is affected to is called directly from the
# class and not from an instance of this class, the instance argument passed to
# the `__get__` method is `None`. In this case, we want to return the
# descriptor itself and not a value. In order to tell mypy that depending on
# the type of the argument a different type is returned, we declare an
# overload. Thus, the use of overload is only for typing purpose.
[docs]
class ReadOnlyDescriptor:
"""Descriptor that can not be reassigned."""
# pylint:disable=too-few-public-methods
[docs]
def __set_name__(self, owner: type[object], name: str) -> None:
self.attribute_name = name # pylint:disable=attribute-defined-outside-init
[docs]
def __set__(self, instance: object, value: object) -> None:
raise AttributeError("Read-Only attribute", self.attribute_name)
[docs]
class FixedMargins(ReadOnlyDescriptor):
"""Getter returns Margins with fixed values."""
# pylint:disable=too-few-public-methods
def __init__(self, left: int, up: int, right: int, down: int) -> None:
[docs]
self.value = Margins(left, up, right, down)
@overload
[docs]
def __get__(self, instance: None, owner: None) -> FixedMargins: ...
@overload
def __get__(self, instance: object, owner: type[object]) -> Margins: ...
def __get__(self, instance: object | None, owner: type[object] | None = None) -> FixedMargins | Margins:
if instance is None:
return self
return self.value
[docs]
class NullMargins(UniformMargins):
"""Margins with null values in all directions."""
# pylint:disable=too-few-public-methods
def __init__(self) -> None:
super().__init__(0)
[docs]
class HalfWindowMargins(ReadOnlyDescriptor):
"""Getter returns Margins corresponding to half window.
Expects instance object has a `_window_size` member.
"""
# pylint:disable=too-few-public-methods
@overload
[docs]
def __get__(self, instance: None, owner: None) -> HalfWindowMargins: ...
@overload
def __get__(self, instance: object, owner: type[object]) -> Margins: ...
def __get__(self, instance: object | None, owner: type[object] | None = None) -> HalfWindowMargins | Margins:
if instance is None:
return self
# We call __dict__ because mypy says "object" has no attribute "_window_size"
value = int((instance.__dict__["_window_size"] - 1) / 2)
return Margins(value, value, value, value)