Skip to content

Commit 2712d1c

Browse files
committed
fix(action_manager/utils): 1. Requires initialization of app when needed 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. Signed-off-by: TsXor <[email protected]>
1 parent 04e9362 commit 2712d1c

File tree

2 files changed

+68
-6
lines changed

2 files changed

+68
-6
lines changed

photoshop/api/action_manager/js_converter/convert.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ def parseref(tdict):
8585
plist = ["!ref"]
8686
# py37 compat
8787
try:
88-
exec(
89-
"""ext = [(str2refgetpacker[val["type"]](e) """
88+
ext = eval(
89+
"""[(str2refgetpacker[val["type"]](e) """
9090
+ """if type(val := e["Value"]) == dict """
9191
+ """else str2refgetpacker["default"](e)) for e in d2l]"""
9292
)

photoshop/api/action_manager/utils.py

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
"""TypeID conversion utilities of this submodule."""
22

3+
# Import built-in modules
4+
import warnings
5+
from functools import wraps
6+
37
# Import local modules
48
from photoshop.api._core import Photoshop
59

610

711
__all__ = ["str2id", "id2str"]
812

913

10-
class app(Photoshop):
11-
"""Partially reimplement the Application class in this file to avoid circular import."""
14+
class AppAgent(Photoshop):
15+
"""Partially reimplement the Application class
16+
in this file to avoid circular import."""
1217

1318
typename = "Application"
1419

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

2126

22-
converter = app()
27+
converter = None
28+
29+
30+
def requireapp(func):
31+
"""A simple decorator that initializes the global
32+
app instance when the decorated function is called."""
33+
34+
@wraps(func)
35+
def wrapped(*args, **kwargs):
36+
global converter
37+
if converter is None:
38+
converter = AppAgent()
39+
return func(*args, **kwargs)
40+
41+
return wrapped
2342

2443

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

3756

57+
# Current approach of str2id is not perfect, it will face some "collisions".
58+
# This means, if there exists a charID which is the same as another stringID,
59+
# it will cause a "collision".
60+
# For example charID 'From' -> stringID 'from'
61+
# and charID 'from' -> stringID 'originalAddressAttr'
62+
# We can know when this will happen by prescanning the cpp header
63+
# "PITerminology.h" and "PIStringTerminology.h".
64+
# Prescanned collisions are stored here. If you suffer from collisions that
65+
# str2id cannot handle, please open an issue.
66+
67+
collisions = {
68+
"chr": ("Cpy ", "From", "Type"),
69+
"str": ("copyEvent", "originalAddressAttr", "class"),
70+
"collision": ("copy", "from", "type"),
71+
}
72+
73+
COLLISION_WARNING = """
74+
You are using a string that is a collision of stringID and charID.
75+
On this situation, str2id will consider this string as stringID. If you encounter COMerror
76+
on app.executeAction() later, you can try to replace this string(%s) with its corresponding
77+
charID(%s). If you are sure you want to use it as stringID and suppress this warning,
78+
replace this string(%s) with its corresponding stringID(%s).
79+
If this warning doesn't solve you problem, please open an issue."""
80+
81+
82+
@requireapp
3883
def str2id(psstr: str) -> str:
3984
"""Convert charID or stringID to typeID"""
4085
assert type(psstr) == str
4186
if len(psstr) == 4:
87+
try:
88+
search = collisions["collision"].index(psstr)
89+
warnstr = COLLISION_WARNING % (
90+
repr(collisions["collision"][search]),
91+
repr(collisions["chr"][search]),
92+
repr(collisions["collision"][search]),
93+
repr(collisions["str"][search]),
94+
)
95+
warnings.warn(warnstr, category=RuntimeWarning)
96+
except ValueError:
97+
pass # I know what I am doing.
4298
typeid = str2hash(psstr)
4399
try:
44100
restr = converter.id2str(typeid)
@@ -51,6 +107,12 @@ def str2id(psstr: str) -> str:
51107
return typeid
52108

53109

110+
@requireapp
54111
def id2str(typeid: int) -> str:
55112
"""Convert typeID to stringID"""
56-
return converter.id2str(typeid)
113+
result = converter.id2str(typeid)
114+
try:
115+
search = collisions["collision"].index(result)
116+
return collisions["chr"][search]
117+
except ValueError:
118+
return result

0 commit comments

Comments
 (0)