Skip to content

Commit

Permalink
fix(action_manager/utils): 1. Requires initialization of app when nee…
Browse files Browse the repository at this point in the history
…ded instead of do it on import to pass import test. 2. Warns user when typeID collision happens. (And id2str will try to avoid giving user string that will cause collision.)

typeID collision happens when a stringID and a charID share the same name. For example, charID 'From' -> stringID 'from' and charID 'from' -> stringID 'originalAddressAttr', so 'from' is a string that will cause collision. After scanning the cpp header "PITerminology.h" and "PIStringTerminology.h", I found 3 such strings and hardcoded them into utils.py. str2id() will not automatically correct user when user gives a collision string (because no one knows what he want it to be a stringID or an charID!), but will regard that string as stringID and throw a warning. Users can follow that warning's guide to fix error and suppress that warning.
  • Loading branch information
TsXor committed Sep 10, 2022
1 parent 04e9362 commit 53eeb27
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 6 deletions.
4 changes: 2 additions & 2 deletions photoshop/api/action_manager/js_converter/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ def parseref(tdict):
plist = ["!ref"]
# py37 compat
try:
exec(
"""ext = [(str2refgetpacker[val["type"]](e) """
ext = eval(
"""[(str2refgetpacker[val["type"]](e) """
+ """if type(val := e["Value"]) == dict """
+ """else str2refgetpacker["default"](e)) for e in d2l]"""
)
Expand Down
70 changes: 66 additions & 4 deletions photoshop/api/action_manager/utils.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
"""TypeID conversion utilities of this submodule."""

# Import built-in modules
import warnings
from functools import wraps

# Import local modules
from photoshop.api._core import Photoshop


__all__ = ["str2id", "id2str"]


class app(Photoshop):
"""Partially reimplement the Application class in this file to avoid circular import."""
class AppAgent(Photoshop):
"""Partially reimplement the Application class
in this file to avoid circular import."""

typename = "Application"

Expand All @@ -19,7 +24,21 @@ def id2str(self, number: int) -> str:
return self.app.typeIDToStringID(number)


converter = app()
converter = None


def requireapp(func):
"""A simple decorator that initializes the global
app instance when the decorated function is called."""

@wraps(func)
def wrapped(*args, **kwargs):
global converter
if converter is None:
converter = AppAgent()
return func(*args, **kwargs)

return wrapped


def str2hash(x: str) -> int:
Expand All @@ -35,10 +54,47 @@ def hash2str(x: int) -> str:
return x.to_bytes(length=4, byteorder="big").decode()


# Current approach of str2id is not perfect, it will face some "collisions".
# This means, if there exists a charID which is the same as another stringID,
# it will cause a "collision".
# For example charID 'From' -> stringID 'from'
# and charID 'from' -> stringID 'originalAddressAttr'
# We can know when this will happen by prescanning the cpp header
# "PITerminology.h" and "PIStringTerminology.h".
# Prescanned collisions are stored here. If you suffer from collisions that
# str2id cannot handle, please open an issue.

collisions = {
"chr": ("Cpy ", "From", "Type"),
"str": ("copyEvent", "originalAddressAttr", "class"),
"collision": ("copy", "from", "type"),
}

COLLISION_WARNING = """
You are using a string that is a collision of stringID and charID.
On this situation, str2id will consider this string as stringID. If you encounter COMerror
on app.executeAction() later, you can try to replace this string(%s) with its corresponding
charID(%s). If you are sure you want to use it as stringID and suppress this warning,
replace this string(%s) with its corresponding stringID(%s).
If this warning doesn't solve you problem, please open an issue."""


@requireapp
def str2id(psstr: str) -> str:
"""Convert charID or stringID to typeID"""
assert type(psstr) == str
if len(psstr) == 4:
try:
search = collisions["collision"].index(psstr)
warnstr = COLLISION_WARNING % (
repr(collisions["collision"][search]),
repr(collisions["chr"][search]),
repr(collisions["collision"][search]),
repr(collisions["str"][search]),
)
warnings.warn(warnstr, category=RuntimeWarning)
except ValueError:
pass # I know what I am doing.
typeid = str2hash(psstr)
try:
restr = converter.id2str(typeid)
Expand All @@ -51,6 +107,12 @@ def str2id(psstr: str) -> str:
return typeid


@requireapp
def id2str(typeid: int) -> str:
"""Convert typeID to stringID"""
return converter.id2str(typeid)
result = converter.id2str(typeid)
try:
search = collisions["collision"].index(result)
return collisions["chr"][search]
except ValueError:
return result

0 comments on commit 53eeb27

Please sign in to comment.