Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5386433
feat: Add extensive type annotations, add classes for path handling, …
pappnu Aug 12, 2025
6a599d8
fix(Layer): return parent with correct type
pappnu Aug 13, 2025
b5f203f
fix(LayerSet): fix iterating LayerSet object directly
pappnu Aug 13, 2025
74e1a92
test(TestNewDocument): test getting layer parent and expand layer ite…
pappnu Aug 13, 2025
a4e437c
feat: add py.typed file to indicate that the package contains type an…
pappnu Aug 13, 2025
319217f
fix(executeActionGet): correct input argument type
pappnu Aug 13, 2025
b59449f
fix(Application): executeAction can be called without an ActionDescri…
pappnu Aug 14, 2025
92bfcde
fix(Select): select takes an array of four coordinates
pappnu Aug 14, 2025
4735d6d
style: Format and sort imports
pappnu Aug 15, 2025
e3389d6
feat(Layer): Add default value to rotate anchor
pappnu Aug 15, 2025
514eea7
fix(Documents): Document width and height can be given as floats, tho…
pappnu Aug 16, 2025
460dc4d
fix(path_item.py): Import TYPE_CHECKING from typing instead of errone…
pappnu Aug 26, 2025
2d149ec
refactor: Avoid unnecessary getattribute calls to the wrapper objects
pappnu Feb 15, 2026
a6e8518
test(manual_test_new_document.py): Add tests for layer querying
pappnu Feb 15, 2026
236ca7c
feat: Bump Python requirement to >=3.10, modernize pyproject.toml, up…
pappnu Feb 15, 2026
4b373ce
docs(README.md): Instruct how to browse the COM API with Visual Studio
pappnu Feb 15, 2026
30fd637
fix(Selection): Selection's select can take a sequence of more than f…
pappnu Feb 22, 2026
2cf8d78
fix(FontSize): Don't use StrEnum as Python 3.10 doesn't have it
pappnu Feb 25, 2026
0dfd911
fix(Notifiers): Access the Photoshop Application object correctly
pappnu Feb 25, 2026
75323ad
feat(Photoshop): Allow accessing the Photoshop Application object fro…
pappnu Feb 25, 2026
d7d0bd5
fix(import-test.yml): Fix and update test workflow
pappnu May 18, 2026
e7ea931
build(pyproject.toml): Align and simplify Ruff and isort rules
pappnu May 19, 2026
bb5659e
style: Autoformat code
pappnu May 19, 2026
041f65a
test(manual_test_layer_comps.py): Generalize and expand layer comp tests
pappnu May 25, 2026
1857950
fix: adding and removing layer comps
pappnu May 25, 2026
3fcd7a2
fix(Notifier): Use delete instead of remove to delete a notifier
pappnu May 25, 2026
349b543
fix: Remove function calls that aren't present in the PS VBS API
pappnu May 25, 2026
5e2858c
fix: function and property naming mistakes
pappnu May 26, 2026
851cf20
ci: Replace Black, Isort and Flake8 with Ruff
pappnu Jun 13, 2026
d3d8d4d
style: Format and lint code
pappnu Jun 13, 2026
9c36494
chore: retrigger CI
loonghao Sep 22, 2026
c780ed5
fix(core): keep annotations lazy so the CLSID fallback helpers import
loonghao Oct 1, 2026
6c99b0a
build: keep the Python 3.8 floor while landing the type annotations
loonghao Oct 1, 2026
66258df
test: give the collection rework real automated coverage
loonghao Oct 1, 2026
21eaa77
ci: require Poetry 2.x now that metadata uses PEP 621
loonghao Oct 1, 2026
48c24cb
build: move project metadata back to [tool.poetry]
loonghao Oct 1, 2026
8227c46
test: scope the manual-test skip hook to its own directory
loonghao Oct 1, 2026
f09c26c
test: pin the getByName first-match and exact-match contract
loonghao Oct 1, 2026
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
16 changes: 9 additions & 7 deletions .github/workflows/import-test.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
name: Import Test
on: [pull_request]
on:
pull_request:
workflow_dispatch:

jobs:
python-check:
runs-on: windows-2022
runs-on: windows-latest
strategy:
max-parallel: 3
matrix:
Expand All @@ -14,12 +16,12 @@ jobs:
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install -U pip poetry
poetry --version
poetry install -vvv || poetry install -vvv || poetry install -vvv
poetry run pytest
python -m pip install poetry
python -m poetry --version
python -m poetry install -vvv
python -m poetry run pytest
13 changes: 2 additions & 11 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,5 @@ jobs:

- name: Run tests and linters
run: |
#!/bin/sh -e
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
export PREFIX="poetry run python -m "
if [ -d 'venv' ] ; then
export PREFIX="venv/bin/"
fi

${PREFIX}black photoshop --check
${PREFIX}isort --check-only photoshop
ruff check photoshop
poetry run ruff check photoshop
poetry run ruff format --check photoshop
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ result/
.coverage
/venv/
venv_python
.venv/

# Docs
docs_src/_build/
Expand Down
55 changes: 14 additions & 41 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
exclude: conf.py
repos:
- repo: https://github.com/ambv/black
rev: 22.12.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.9
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: black
language_version: python3.10
- hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-docstring-first
Expand All @@ -14,43 +17,13 @@ repos:
- id: debug-statements
- id: name-tests-test
- id: requirements-txt-fixer
repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.4.0
- hooks:
- additional_dependencies:
- flake8-typing-imports==1.5.0
id: flake8
repo: https://github.com/pycqa/flake8
rev: 3.7.9
- hooks:
- id: autopep8
repo: https://github.com/pre-commit/mirrors-autopep8
rev: v1.4.4
- hooks:
- repo: https://github.com/pre-commit/pre-commit
rev: v4.6.0
hooks:
- id: validate_manifest
repo: https://github.com/pre-commit/pre-commit
rev: v1.21.0
- hooks:
- args:
- --py36-plus
id: pyupgrade
repo: https://github.com/asottile/pyupgrade
rev: v1.25.3
- hooks:
- args:
- --py3-plus
id: reorder-python-imports
repo: https://github.com/asottile/reorder_python_imports
rev: v1.9.0
- hooks:
- args:
- --py36-plus
id: add-trailing-comma
repo: https://github.com/asottile/add-trailing-comma
rev: v1.5.0
- hooks:
- repo: https://github.com/commitizen-tools/commitizen
rev: v4.16.3
hooks:
- id: commitizen
stages:
- commit-msg
repo: https://github.com/commitizen-tools/commitizen
rev: v2.17.8
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ Useful links
- http://shining-lucy.com/wiki/page.php?id=appwiki:photoshop:ps_script
- http://web.archive.org/web/20140121053819/http://www.pcpix.com/Photoshop/char.html
- http://www.tonton-pixel.com/scripts/utility-scripts/get-equivalent-id-code/index.html
- https://github.com/Adobe-CEP/CEP-Resources/tree/master/Documentation/Product%20specific%20Documentation/Photoshop%20Scripting
- https://github.com/Adobe-CEP/Samples/tree/master/PhotoshopEvents
- https://evanmccall.wordpress.com/2015/03/09/how-to-develop-photoshop-tools-in-python

Expand Down Expand Up @@ -242,9 +243,7 @@ We maintain high code quality standards through automated tools and consistent s
- Quotes: Double quotes preferred

- **Quality Tools**:
- [Black](https://black.readthedocs.io/) - Code formatting
- [isort](https://pycqa.github.io/isort/) - Import organization
- [flake8](https://flake8.pycqa.org/) - Style enforcement
- [Ruff](https://docs.astral.sh/ruff/) - Linting, formatting, and import organization
- [pre-commit](https://pre-commit.com/) - Automated checks before commits

### 🔄 Git Workflow
Expand Down Expand Up @@ -279,7 +278,7 @@ We value thorough testing to ensure reliability:

### 📦 Development Environment

- **Python Versions**: We support Python 3.8+ (see `pyproject.toml` for specifics)
- **Python Versions**: We support Python 3.10+ (see `pyproject.toml` for specifics)
- **Dependency Management**: [Poetry](https://python-poetry.org/) for consistent environments
- **Virtual Environment**: Poetry automatically creates and manages virtual environments

Expand All @@ -295,3 +294,18 @@ We value thorough testing to ensure reliability:
8. **Merge**: Once approved, your PR will be merged

Thank you for contributing to the Photoshop Python API project! 🎉

### Development Notes

One way to view the COM API structure is to use Visual Studio.

1. Install Visual Studio with the .NET desktop development workload
2. Open Visual Studio
3. Choose View -> Object Browser
4. In the Object Browser from the Browse dropdown choose *Edit Custom Component Set...*
5. In the Edit Custom Component Set window's COM tab add Photoshop's Object and Type libraries
6. After pressing OK the libraries should show up in the Object Browser

If the libraries aren't visible in the COM tab make sure you have the .NET desktop development workload installed. Other things you may try are running Photoshop once as administrator and reinstalling Photoshop and/or your PC. The object library is defined usually in */your/path/to/Adobe Photoshop \<version\>/Required/Plug-ins/Extensions/ScriptingSupport.8li*

The [VBS reference 2020](https://github.com/Adobe-CEP/CEP-Resources/blob/master/Documentation/Product%20specific%20Documentation/Photoshop%20Scripting/photoshop-vbs-ref-2020.pdf) is also quite trustworthy, since Adobe isn't actively developing the COM API anymore.
4 changes: 3 additions & 1 deletion docs/gen_api_nav.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Plugin for generate API docs."""

from __future__ import annotations

# Import built-in modules
from pathlib import Path

Expand Down Expand Up @@ -29,7 +31,7 @@ def main():
full_doc_path = full_doc_path.as_posix().replace("\\", "/")
with mkdocs_gen_files.open(full_doc_path, "w") as fd:
ident = ".".join(parts)
print(f"::: " + ident, file=fd)
print("::: " + ident, file=fd)

mkdocs_gen_files.set_edit_path(full_doc_path, path.as_posix().replace("\\", "/"))

Expand Down
6 changes: 5 additions & 1 deletion docs/gen_examples.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
"""Plugin for generate API docs."""

from __future__ import annotations

# Import built-in modules
import os

from pathlib import Path

# Import third-party modules
from jinja2 import Template
import mkdocs_gen_files
import stringcase

from jinja2 import Template


template = Template(
r"""
Expand Down
6 changes: 4 additions & 2 deletions examples/active_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
- Create a new layer and rename it
"""


from __future__ import annotations
# Import local modules
from photoshop import Session

Expand All @@ -28,9 +30,9 @@
docRef.artLayers.add()

# Display current active layer name
ps.echo(docRef.activeLayer.name)
print(docRef.activeLayer.name)

# Create and rename a new layer
new_layer = docRef.artLayers.add()
ps.echo(new_layer.name)
print(new_layer.name)
new_layer.name = "test"
4 changes: 3 additions & 1 deletion examples/add_metadata.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Add metadata to current active document."""


from __future__ import annotations
# Import built-in modules
import os

Expand All @@ -13,4 +15,4 @@
doc.info.provinceState = "Beijing"
doc.info.title = "My Demo"
print("Print all metadata of current active document.")
ps.echo(doc.info)
print(doc.info)
8 changes: 5 additions & 3 deletions examples/add_slate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@

"""


from __future__ import annotations
# Import built-in modules
from datetime import datetime
import os

from datetime import datetime
from tempfile import mkdtemp

# Import third-party modules
# Import local modules
import examples._psd_files as psd # Import from examples.

# Import local modules
from photoshop import Session


Expand Down
3 changes: 3 additions & 0 deletions examples/add_start_application_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@

"""


from __future__ import annotations
# Import built-in modules
import os

from tempfile import mkdtemp

# Import local modules
Expand Down
5 changes: 3 additions & 2 deletions examples/apply_crystallize_filter_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@

"""

# Import third-party modules
import examples._psd_files as psd # Import from examples.

from __future__ import annotations
# Import local modules
import examples._psd_files as psd # Import from examples.

from photoshop import Session


Expand Down
6 changes: 4 additions & 2 deletions examples/change_color_of_background_and_foreground.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
- Default colors
"""


from __future__ import annotations
# Import local modules
from photoshop import Session

Expand All @@ -34,10 +36,10 @@
ps.app.backgroundColor = bg_color

# Print current colors
ps.echo(f"Foreground RGB: {ps.app.foregroundColor.rgb.red}, "
print(f"Foreground RGB: {ps.app.foregroundColor.rgb.red}, "
f"{ps.app.foregroundColor.rgb.green}, "
f"{ps.app.foregroundColor.rgb.blue}")

ps.echo(f"Background RGB: {ps.app.backgroundColor.rgb.red}, "
print(f"Background RGB: {ps.app.backgroundColor.rgb.red}, "
f"{ps.app.backgroundColor.rgb.green}, "
f"{ps.app.backgroundColor.rgb.blue}")
4 changes: 3 additions & 1 deletion examples/compare_colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
- Color model differences
"""


from __future__ import annotations
# Import local modules
from photoshop import Session

Expand All @@ -34,4 +36,4 @@
color1.rgb.green == color2.rgb.green and
color1.rgb.blue == color2.rgb.blue)

ps.echo(f"Colors are {'same' if is_same else 'different'}")
print(f"Colors are {'same' if is_same else 'different'}")
8 changes: 5 additions & 3 deletions examples/convert_smartobject_to_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
- Layer types
"""


from __future__ import annotations
# Import local modules
from photoshop import Session

Expand All @@ -26,12 +28,12 @@

# Convert to smart object
layer.convertToSmartObject()
ps.echo("Layer converted to Smart Object")
print("Layer converted to Smart Object")

# Check if it's a smart object
if layer.kind == ps.LayerKind.SmartObjectLayer:
ps.echo("Layer is now a Smart Object")
print("Layer is now a Smart Object")

# Convert back to regular layer
layer.rasterize(ps.RasterizeType.EntireLayer)
ps.echo("Smart Object converted back to regular layer")
print("Smart Object converted back to regular layer")
3 changes: 3 additions & 0 deletions examples/create_thumbnail.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

"""


from __future__ import annotations
# Import built-in modules
import os

from tempfile import mkdtemp

# Import local modules
Expand Down
4 changes: 3 additions & 1 deletion examples/current_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
- Active tool state
"""


from __future__ import annotations
# Import local modules
from photoshop import Session

Expand All @@ -22,4 +24,4 @@
current = ps.app.currentTool

# Print current tool name
ps.echo(f"Current tool: {current}")
print(f"Current tool: {current}")
Loading
Loading