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: 0 additions & 2 deletions src/psd_tools/__main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import unicode_literals

import logging

import docopt
Expand Down
2 changes: 0 additions & 2 deletions src/psd_tools/api/adjustments.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
print(layer.gradient_kind)
"""

from __future__ import annotations

import logging

from psd_tools.api.layers import AdjustmentLayer, FillLayer
Expand Down
2 changes: 0 additions & 2 deletions src/psd_tools/api/effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
Effects module.
"""

from __future__ import annotations

import logging
from typing import Any, Iterator

Expand Down
80 changes: 40 additions & 40 deletions src/psd_tools/api/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
Layer module.
"""

from __future__ import annotations

import logging
from typing import (
Any,
Callable,
Iterable,
Iterator,
Optional,
Protocol,
TypeVar,
Union,
runtime_checkable,
)

Expand Down Expand Up @@ -67,16 +67,16 @@ def __init__(
psd: Any,
record: LayerRecord,
channels: ChannelDataList,
parent: TGroupMixin | None,
parent: Optional[TGroupMixin],
):
from psd_tools.api.psd_image import PSDImage # Circular import

assert isinstance(psd, PSDImage) or psd is None

self._psd: PSDImage | None = psd
self._psd: Optional[PSDImage] = psd
self._record = record
self._channels = channels
self._parent: GroupMixin | None = parent
self._parent: Optional[GroupMixin] = parent

@property
def name(self) -> str:
Expand Down Expand Up @@ -123,7 +123,7 @@ def _invalidate_bbox(self) -> None:
Invalidate this layer's _bbox and any parents recursively to the root.
"""
if isinstance(self, (GroupMixin, ShapeLayer)):
self._bbox: tuple[int, int, int, int] | None = None
self._bbox: Optional[tuple[int, int, int, int]] = None
if isinstance(self.parent, (Group, Artboard)):
self.parent._invalidate_bbox()

Expand Down Expand Up @@ -164,7 +164,7 @@ def opacity(self, value: int) -> None:
self._record.opacity = int(value)

@property
def parent(self) -> TGroupMixin | None:
def parent(self) -> Optional[TGroupMixin]:
"""Parent of this layer."""
return self._parent # type: ignore

Expand Down Expand Up @@ -192,7 +192,7 @@ def blend_mode(self) -> BlendMode:
return self._record.blend_mode

@blend_mode.setter
def blend_mode(self, value: bytes | str | BlendMode) -> None:
def blend_mode(self, value: Union[bytes, str, BlendMode]) -> None:
if isinstance(value, str):
value = value.encode("ascii")
self._record.blend_mode = BlendMode(value)
Expand Down Expand Up @@ -313,7 +313,7 @@ def has_mask(self) -> bool:
return self._record.mask_data is not None

@property
def mask(self) -> Mask | None:
def mask(self) -> Optional[Mask]:
"""
Returns mask associated with this layer.

Expand All @@ -335,7 +335,7 @@ def has_vector_mask(self) -> bool:
)

@property
def vector_mask(self) -> VectorMask | None:
def vector_mask(self) -> Optional[VectorMask]:
"""
Returns vector mask associated with this layer.

Expand Down Expand Up @@ -392,7 +392,7 @@ def has_stroke(self) -> bool:
return Tag.VECTOR_STROKE_DATA in self.tagged_blocks

@property
def stroke(self) -> Stroke | None:
def stroke(self) -> Optional[Stroke]:
"""Property for strokes."""
if not hasattr(self, "_stroke"):
self._stroke = None
Expand Down Expand Up @@ -424,7 +424,7 @@ def unlock(self) -> None:
self.lock(0)

@property
def locks(self) -> ProtectedSetting | None:
def locks(self) -> Optional[ProtectedSetting]:
protected_settings_block = self.tagged_blocks.get(Tag.PROTECTED_SETTING)

if protected_settings_block is not None:
Expand All @@ -433,8 +433,8 @@ def locks(self) -> ProtectedSetting | None:
return None

def topil(
self, channel: int | None = None, apply_icc: bool = True
) -> PILImage | None:
self, channel: Optional[int] = None, apply_icc: bool = True
) -> Optional[PILImage]:
"""
Get PIL Image of the layer.

Expand All @@ -461,8 +461,8 @@ def topil(
return convert_layer_to_pil(self, channel, apply_icc)

def numpy(
self, channel: str | None = None, real_mask: bool = True
) -> np.ndarray | None:
self, channel: Optional[str] = None, real_mask: bool = True
) -> Optional[np.ndarray]:
"""
Get NumPy array of the layer.

Expand All @@ -476,13 +476,13 @@ def numpy(

def composite(
self,
viewport: tuple[int, int, int, int] | None = None,
viewport: Optional[tuple[int, int, int, int]] = None,
force: bool = False,
color: float | tuple[float, ...] | np.ndarray = 1.0,
alpha: float | np.ndarray = 0.0,
layer_filter: Callable | None = None,
color: Union[float, tuple[float, ...], np.ndarray] = 1.0,
alpha: Union[float, np.ndarray] = 0.0,
layer_filter: Optional[Callable] = None,
apply_icc: bool = True,
) -> PILImage | None:
) -> Optional[PILImage]:
"""
Composite layer and masks (mask, vector mask, and clipping layers).

Expand Down Expand Up @@ -521,7 +521,7 @@ def clip_layers(self) -> list[Self]:
"""
if self.clipping_layer:
return []

# Look for clipping layers in the parent scope.
parent: GroupMixin = self.parent or self._psd # type: ignore
index = parent.index(self)
Expand Down Expand Up @@ -736,7 +736,7 @@ def _fetch_tagged_blocks(self, target_psd: Any) -> None: # Circular import

@runtime_checkable
class GroupMixin(Protocol):
_bbox: tuple[int, int, int, int] | None = None
_bbox: Optional[tuple[int, int, int, int]] = None
_layers: list[Layer]
_psd: Any # TODO: Circular import

Expand Down Expand Up @@ -867,7 +867,7 @@ def count(self, layer: Layer) -> int:

return self._layers.count(layer)

def _check_valid_layers(self, layers: Layer | Iterable[Layer]) -> None:
def _check_valid_layers(self, layers: Union[Layer, Iterable[Layer]]) -> None:
assert layers is not self, "Cannot add the group {} to itself.".format(self)

if isinstance(layers, Layer):
Expand All @@ -885,7 +885,7 @@ def _check_valid_layers(self, layers: Layer | Iterable[Layer]) -> None:
def _update_layer_metadata(self) -> None:
from psd_tools.api.psd_image import PSDImage # Circular import

_psd: PSDImage | None = self if isinstance(self, PSDImage) else self._psd
_psd: Optional[PSDImage] = self if isinstance(self, PSDImage) else self._psd

for layer in self.descendants():
if layer._psd != _psd and _psd is not None:
Expand Down Expand Up @@ -925,7 +925,7 @@ def descendants(self) -> Iterator[Layer]:
if isinstance(layer, GroupMixin):
yield from layer.descendants()

def find(self, name: str) -> Layer | None:
def find(self, name: str) -> Optional[Layer]:
"""
Returns the first layer found for the given layer name

Expand Down Expand Up @@ -997,15 +997,15 @@ def __init__(
psd: Any,
record: LayerRecord,
channels: ChannelDataList,
parent: TGroupMixin | None,
parent: Optional[TGroupMixin],
):
self._layers = []
self._bounding_record = None
self._bounding_channels = None
Layer.__init__(self, psd, record, channels, parent)

@property
def _setting(self) -> SectionDividerSetting | None:
def _setting(self) -> Optional[SectionDividerSetting]:
# Can be None.
return self.tagged_blocks.get_data(Tag.SECTION_DIVIDER_SETTING)

Expand All @@ -1017,7 +1017,7 @@ def blend_mode(self) -> BlendMode:
return super(Group, self).blend_mode

@blend_mode.setter
def blend_mode(self, value: str | bytes | BlendMode) -> None:
def blend_mode(self, value: Union[str, bytes, BlendMode]) -> None:
_value = BlendMode(value.encode("ascii") if isinstance(value, str) else value)
if _value == BlendMode.PASS_THROUGH:
self._record.blend_mode = BlendMode.NORMAL
Expand Down Expand Up @@ -1050,11 +1050,11 @@ def clipping_layer(self, value: bool) -> None:

def composite(
self,
viewport: tuple[int, int, int, int] | None = None,
viewport: Optional[tuple[int, int, int, int]] = None,
force: bool = False,
color: float | tuple[float, ...] | np.ndarray = 1.0,
alpha: float | np.ndarray = 0.0,
layer_filter: Callable | None = None,
color: Union[float, tuple[float, ...], np.ndarray] = 1.0,
alpha: Union[float, np.ndarray] = 0.0,
layer_filter: Optional[Callable] = None,
apply_icc: bool = True,
):
"""
Expand Down Expand Up @@ -1099,7 +1099,7 @@ def new(
cls,
name: str = "Group",
open_folder: bool = True,
parent: GroupMixin | None = None,
parent: Optional[GroupMixin] = None,
) -> Self:
"""
Create a new Group object with minimal records and data channels and metadata to properly include the group in the PSD file.
Expand Down Expand Up @@ -1157,7 +1157,7 @@ def group_layers(
cls,
layers: list[Layer],
name: str = "Group",
parent: GroupMixin | None = None,
parent: Optional[GroupMixin] = None,
open_folder: bool = True,
):
"""
Expand Down Expand Up @@ -1266,7 +1266,7 @@ class PixelLayer(Layer):
def frompil(
cls,
pil_im: PILImage,
psd_file: Any | None = None, # TODO: Fix circular import
psd_file: Optional[Any] = None, # TODO: Fix circular import
layer_name: str = "Layer",
top: int = 0,
left: int = 0,
Expand Down Expand Up @@ -1461,7 +1461,7 @@ def text(self) -> str:
return self._data.text_data.get(b"Txt ").value.rstrip("\x00")

@property
def text_type(self) -> TextType | None:
def text_type(self) -> Optional[TextType]:
"""
Text type. Read-only.

Expand Down Expand Up @@ -1504,7 +1504,7 @@ def transform(self) -> tuple[float, float, float, float, float, float]:
return self._data.transform

@property
def _engine_data(self) -> engine_data.EngineData | engine_data.EngineData2:
def _engine_data(self) -> Union[engine_data.EngineData, engine_data.EngineData2]:
"""Styling and resource information."""
return self._data.text_data.get(b"EngineData").value

Expand All @@ -1524,7 +1524,7 @@ def document_resources(self) -> engine_data.Dict:
return self._engine_data.get("DocumentResources")

@property
def warp(self) -> DescriptorBlock | None:
def warp(self) -> Optional[DescriptorBlock]:
"""Warp configuration."""
return self._data.warp

Expand All @@ -1536,7 +1536,7 @@ class ShapeLayer(Layer):

def __init__(self, *args: Any):
super(ShapeLayer, self).__init__(*args)
self._bbox: tuple[int, int, int, int] | None = None
self._bbox: Optional[tuple[int, int, int, int]] = None

@property
def left(self) -> int:
Expand Down
8 changes: 3 additions & 5 deletions src/psd_tools/api/mask.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
Mask module.
"""

from __future__ import annotations

import logging
from typing import Any
from typing import Any, Optional

from PIL.Image import Image as PILImage

Expand Down Expand Up @@ -99,15 +97,15 @@ def parameters(self):
return self._data.parameters

@property
def real_flags(self) -> MaskFlags | None:
def real_flags(self) -> Optional[MaskFlags]:
"""Real flag."""
return self._data.real_flags

def _has_real(self) -> bool:
"""Return True if the mask has real flags."""
return self.real_flags is not None and self.real_flags.parameters_applied

def topil(self, real: bool=True, **kwargs: Any) -> PILImage | None:
def topil(self, real: bool = True, **kwargs: Any) -> Optional[PILImage]:
"""
Get PIL Image of the mask.

Expand Down
18 changes: 11 additions & 7 deletions src/psd_tools/api/numpy_io.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from __future__ import annotations

import logging
from typing import Any, Literal
from typing import Any, Literal, Optional, Union

import numpy as np

Expand All @@ -22,14 +20,16 @@


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


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

Expand All @@ -56,7 +56,9 @@ def get_image_data(psd: Any, channel: str | None) -> np.ndarray: # TODO: Circul
return data


def get_layer_data(layer: Any, channel: str | None, real_mask: bool = True) -> np.ndarray:
def get_layer_data(
layer: Any, channel: Optional[str], real_mask: bool = True
) -> np.ndarray:
def _find_channel(layer, width, height, condition):
depth, version = layer._psd.depth, layer._psd.version
iterator = zip(layer._record.channel_info, layer._channels)
Expand Down Expand Up @@ -147,7 +149,9 @@ def get_transparency_index(psd: Any) -> int:


def _parse_array(
data: bytes | bytearray, depth: Literal[1, 8, 16, 32], lut: np.ndarray | None = None
data: Union[bytes, bytearray],
depth: Literal[1, 8, 16, 32],
lut: Optional[np.ndarray] = None,
) -> np.ndarray:
if depth == 8:
parsed = np.frombuffer(data, ">u1")
Expand Down
Loading