Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/psd_tools/api/adjustments.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from psd_tools.api.layers import AdjustmentLayer, FillLayer
from psd_tools.constants import Tag
from psd_tools.psd.adjustments import Curves, Levels, LevelRecord
from psd_tools.psd.adjustments import Curves, LevelRecord, Levels
from psd_tools.psd.descriptor import DescriptorBlock
from psd_tools.utils import new_registry

Expand Down
3 changes: 2 additions & 1 deletion src/psd_tools/api/effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
from typing import Any, Iterator, Optional, Protocol

from psd_tools.api.protocols import LayerProtocol
from psd_tools.constants import Resource, Tag
from psd_tools.psd.descriptor import Descriptor, List
from psd_tools.psd.image_resources import ImageResources
Expand Down Expand Up @@ -43,7 +44,7 @@ class Effects:
Only present effects are kept.
"""

def __init__(self, layer: Any): # TODO: Circular import
def __init__(self, layer: LayerProtocol):
self._data: Optional[Descriptor] = None
for tag in (
Tag.OBJECT_BASED_EFFECTS_LAYER_INFO,
Expand Down
30 changes: 15 additions & 15 deletions src/psd_tools/api/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import logging
from typing import (
TYPE_CHECKING,
Any,
Callable,
Iterable,
Expand All @@ -21,6 +22,9 @@
except ImportError:
from typing_extensions import Self

if TYPE_CHECKING:
from psd_tools.api.psd_image import PSDImage

import numpy as np
from PIL.Image import Image as PILImage

Expand Down Expand Up @@ -62,19 +66,15 @@
TGroupMixin = TypeVar("TGroupMixin", bound="GroupMixin")


class Layer(object):
class Layer:
def __init__(
self,
psd: Any,
psd: Optional["PSDImage"],
record: LayerRecord,
channels: ChannelDataList,
parent: Optional[TGroupMixin],
):
from psd_tools.api.psd_image import PSDImage # Circular import

assert isinstance(psd, PSDImage) or psd is None

self._psd: Optional[PSDImage] = psd
self._psd: Optional["PSDImage"] = psd
self._record = record
self._channels = channels
self._parent: Optional[GroupMixin] = parent
Expand Down Expand Up @@ -794,7 +794,7 @@ def move_down(self, offset: int = 1) -> Self:

return self.move_up(-1 * offset)

def _fetch_tagged_blocks(self, target_psd: Any) -> None: # Circular import
def _fetch_tagged_blocks(self, target_psd: "PSDImage") -> None:
# Retrieve the patterns contained in the layer current ._psd and add them to the target psd
_psd = target_psd

Expand Down Expand Up @@ -844,7 +844,7 @@ def _fetch_tagged_blocks(self, target_psd: Any) -> None: # Circular import
class GroupMixin(Protocol):
_bbox: Optional[tuple[int, int, int, int]] = None
_layers: list[Layer]
_psd: Any # TODO: Circular import
_psd: Optional["PSDImage"]

@property
def left(self) -> int:
Expand Down Expand Up @@ -989,9 +989,9 @@ def _check_valid_layers(self, layers: Union[Layer, Iterable[Layer]]) -> None:
)

def _update_layer_metadata(self) -> None:
from psd_tools.api.psd_image import PSDImage # Circular import
from psd_tools.api.psd_image import PSDImage

_psd: Optional[PSDImage] = self if isinstance(self, PSDImage) else self._psd
_psd: Optional["PSDImage"] = self if isinstance(self, PSDImage) else self._psd # type: ignore

for layer in self.descendants():
if layer._psd != _psd and _psd is not None:
Expand All @@ -1005,7 +1005,7 @@ def _update_layer_metadata(self) -> None:
layer._parent = self

def _update_psd_record(self) -> None:
from psd_tools.api.psd_image import PSDImage # Circular import
from psd_tools.api.psd_image import PSDImage

if isinstance(self, PSDImage):
self._mark_updated()
Expand Down Expand Up @@ -1100,7 +1100,7 @@ def _get_bbox(layer, **kwargs):

def __init__(
self,
psd: Any,
psd: Optional["PSDImage"],
record: LayerRecord,
channels: ChannelDataList,
parent: Optional[TGroupMixin],
Expand Down Expand Up @@ -1378,7 +1378,7 @@ class PixelLayer(Layer):
def frompil(
cls,
pil_im: PILImage,
psd_file: Optional[Any] = None, # TODO: Fix circular import
psd_file: Optional["PSDImage"] = None,
layer_name: str = "Layer",
top: int = 0,
left: int = 0,
Expand Down Expand Up @@ -1469,7 +1469,7 @@ def frompil(

return self

def _convert(self, target_psd: Any) -> "PixelLayer":
def _convert(self, target_psd: "PSDImage") -> "PixelLayer":
# assert self._psd is not None, "This layer cannot be converted because it has no psd file linked."

if self._psd is None:
Expand Down
5 changes: 3 additions & 2 deletions src/psd_tools/api/mask.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@

from PIL.Image import Image as PILImage

from psd_tools.api.protocols import LayerProtocol
from psd_tools.constants import ChannelID
from psd_tools.psd.layer_and_mask import MaskData, MaskFlags

logger = logging.getLogger(__name__)


class Mask(object):
class Mask:
"""Mask data attached to a layer.

There are two distinct internal mask data: user mask and vector mask.
Expand All @@ -22,7 +23,7 @@ class Mask(object):
real mask.
"""

def __init__(self, layer: Any): # TODO: Circular import
def __init__(self, layer: LayerProtocol):
self._layer = layer
self._data: MaskData = layer._record.mask_data

Expand Down
19 changes: 10 additions & 9 deletions src/psd_tools/api/numpy_io.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import logging
from typing import Any, Literal, Optional, Union
from typing import TYPE_CHECKING, Any, Literal, Optional, Union

import numpy as np

from psd_tools.constants import ChannelID, ColorMode, Resource, Tag

if TYPE_CHECKING:
from psd_tools.api.protocols import LayerProtocol, PSDProtocol

logger = logging.getLogger(__name__)

EXPECTED_CHANNELS = {
Expand All @@ -20,16 +23,14 @@


def get_array(
layer: Any, channel: Optional[str], **kwargs: Any
) -> np.ndarray: # TODO: Circular import
layer: Union["LayerProtocol", "PSDProtocol"], channel: Optional[str], **kwargs: Any
) -> np.ndarray:
if layer.kind == "psdimage":
return get_image_data(layer, channel)
return get_layer_data(layer, channel, **kwargs)
return get_image_data(layer, channel) # type: ignore
return get_layer_data(layer, channel, **kwargs) # type: ignore


def get_image_data(
psd: Any, channel: Optional[str]
) -> np.ndarray: # TODO: Circular import
def get_image_data(psd: "PSDProtocol", channel: Optional[str]) -> np.ndarray:
if (channel == "mask") or (channel == "shape" and not has_transparency(psd)):
return np.ones((psd.height, psd.width, 1), dtype=np.float32)

Expand Down Expand Up @@ -57,7 +58,7 @@ def get_image_data(


def get_layer_data(
layer: Any, channel: Optional[str], real_mask: bool = True
layer: "LayerProtocol", channel: Optional[str], real_mask: bool = True
) -> np.ndarray:
def _find_channel(layer, width, height, condition):
depth, version = layer._psd.depth, layer._psd.version
Expand Down
14 changes: 8 additions & 6 deletions src/psd_tools/api/pil_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import io
import logging
from typing import Any, Optional, Union
from typing import TYPE_CHECKING, Optional, Union

from PIL import Image
from PIL.Image import Image as PILImage
Expand All @@ -14,6 +14,9 @@
from psd_tools.psd.image_resources import ThumbnailResource, ThumbnailResourceV4
from psd_tools.psd.patterns import Pattern

if TYPE_CHECKING:
from psd_tools.api.protocols import LayerProtocol, PSDProtocol

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -73,7 +76,7 @@ def get_pil_depth(pil_mode: str) -> int:


def convert_image_data_to_pil(
psd: Any, channel: Optional[int], apply_icc: bool
psd: "PSDProtocol", channel: Optional[int], apply_icc: bool
) -> Optional[PILImage]:
"""Convert ImageData to PIL Image."""

Expand Down Expand Up @@ -118,9 +121,8 @@ def convert_image_data_to_pil(
return _remove_white_background(image)


# TODO: Type hint for layer.
def convert_layer_to_pil(
layer: Any, channel: Optional[int], apply_icc: bool
layer: "LayerProtocol", channel: Optional[int], apply_icc: bool
) -> Optional[PILImage]:
"""Convert Layer to PIL Image."""
alpha = None
Expand Down Expand Up @@ -203,7 +205,7 @@ def convert_thumbnail_to_pil(
return image


def _merge_channels(layer: Any) -> Optional[PILImage]:
def _merge_channels(layer: "LayerProtocol") -> Optional[PILImage]:
mode = get_pil_mode(layer._psd.color_mode)
channels = [
_get_channel(layer, info.id)
Expand All @@ -216,7 +218,7 @@ def _merge_channels(layer: Any) -> Optional[PILImage]:
return Image.merge(mode, channels) # type: ignore


def _get_channel(layer: Any, channel: int) -> Optional[PILImage]:
def _get_channel(layer: "LayerProtocol", channel: int) -> Optional[PILImage]:
if channel == ChannelID.USER_LAYER_MASK:
width = layer.mask._data.right - layer.mask._data.left
height = layer.mask._data.bottom - layer.mask._data.top
Expand Down
Loading
Loading