finder_colors
finder_colors copied to clipboard
Catalina issue
Hi - when run against a file with a colour tag I get the error:
unsupported operand type(s) for &: 'str' and 'int'
I wonder if you you are still maintaining the code and if I can assist with troubleshooting?
BR
Sam
I've had some limited success rewriting bits of this. However, I'm not expert, and am not 100% sure about what's going on.
It seems that either OSX has changed the way tags are stored or a newer version of xattr
outputs them in a different format. At the moment, xattr
reads (and needs to write) a bytes object. A first issue (with the "get colour" function) seems to arise from calling ord()
on the bytes object. A second issue (when trying to change the colour) seems to arise from manipulating the bytes object such that a new value can be added.
You can solve the first problem (and retrieve the colour info) simply by removing the ord
stuff. I'm not sure what the masking does or if this will cause a problem but this works fine for me.
As for the second problem: the line that throws the error is trying to insert the new colour code into the appropriate place in the bytes object by concatenating [first half of the old object] + [new code, at index 9] + [second half of old the object]
. It seems Python is only happy for you to concatenate strings in this context, hence the error. chr(COLORS[color]
is a string, whereas previous[:9]
is still bytes. Assuming the existing code worked in the first place, it looks like either:
(i) Everything used to be bytes, and you used to be able to concatenate them. (Unlikely.)
(ii) Everything used to be strings, and you used to be able to concatenate them + read/write strings with xattr
into OSX easily. (Likely.)
Anyway, to get it working, you first have to ensure that the new object to be written, new
, is a bytes object, otherwise xattr
won't work. I just used bytes()
on it with utf8 encoding. I experienced issues simply calling str()
on previous[:9]
and then converting back to bytes. Something odd happens and you end up with doubled \\
between the bytes. In the end, since these are usually 00
anyway if you don't use other tags (I don't know what the other indices do, tbh), I figured I may as well just pad the bytes object with sufficient 00
s either side of the new colour code, then convert that to bytes. And it worked!
I figured this out by inspecting the BLANK
object in the original code. That object is a string! Hence I could get colours to assign when the folder didn't already have a colour on it, but the program crashed when they did. Essentially, I removed all differentiation between cases where the folder has a colour and doesn't, and just made it overwrite the whole tag with a new tag containing the colour.
This is working well enough for my purposes, but obviously isn't an ideal solution. I hope someone who understands what's going on better than I do can come and take a look.
Here's my modified code:
_FINDER_INFO_TAG = u'com.apple.FinderInfo'
COLORS = {'none': 0, 'gray': 2, 'green': 4, 'purple': 6,
'blue': 8, 'yellow': 10, 'red': 12, 'orange': 14}
NAMES = {0: 'none', 2: 'gray', 4: 'green', 6: 'purple',
8: 'blue', 10 : 'yellow', 12 : 'red', 14 : 'orange' }
#retrieve colour tag
def get(filename):
try:
attrs = xattr(filename)
color_num = attrs.get(_FINDER_INFO_TAG)[9] #remove mask + ord()
return NAMES[color_num]
except IOError as err:
if err.errno == 93: # attribute not found...
return NAMES[0]
# else
raise err
except KeyError as err:
return NAMES[0]
#set colour tag
def set(filename, color):
attrs = xattr(filename)
new = bytes(9*chr(0) + chr(COLORS[color]) + 22*chr(0),'utf8')
"""
Just overwrite all the tags with a new object. Pad either side of the colour code at index 9 with 00 and convert to bytes so xattr can write.
"""
attrs.set(_FINDER_INFO_TAG, new)
@danthedeckie Are you still around? :)
Just bumped into the the same issues, the following worked for me. Unfortunately the underlying macos code only returns one color tag even if multiple set:
diff --git a/finder_colors.py b/finder_colors.py
index 95f3a4d..c4dd653 100755
--- a/finder_colors.py
+++ b/finder_colors.py
@@ -69,7 +69,7 @@ def get(filename):
try:
attrs = xattr(filename)
- color_num = ord(attrs.get(_FINDER_INFO_TAG)[9]) & 14
+ color_num = attrs.get(_FINDER_INFO_TAG)[9] & 14
# & 14 to mask with "1110" (ie ignore all other bits).
return NAMES[color_num]
@@ -98,7 +98,7 @@ def set(filename, color): # pylint: disable=W0622
+ prev_color_extra_bits) \
+ previous[10:]
- attrs.set(_FINDER_INFO_TAG, new)
+ attrs.set(_FINDER_INFO_TAG, new.encode())
return new
The new location for tags (colors as well as "Home", "Work", etc. arbitrary labels), is the xattr com.apple.metadata:_kMDItemUserTags:
, while the com.apple.FinderInfo seems to only contain the last-set color tag, and even then it seems to only contain it if that metadata attribute existed previously (it is not set for files that didn't have that xattr prior to color assignment in Finder, it seems). The attribute appears to be a flat bplist, at least for Big Sur, here's an example of a file I set to Red, then Orange, then Yellow. You can now have multiple colors (tags) assigned.
com.apple.FinderInfo:
00000000 00 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020
com.apple.metadata:_kMDItemUserTags:
00000000 62 70 6C 69 73 74 30 30 A3 01 02 03 55 52 65 64 |bplist00....URed|
00000010 0A 36 58 4F 72 61 6E 67 65 0A 37 58 59 65 6C 6C |.6XOrange.7XYell|
00000020 6F 77 0A 35 08 0C 12 1B 00 00 00 00 00 00 01 01 |ow.5............|
00000030 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 24 |.......$|
00000048
Here is the mdls
output for that file:
kMDItemUserTags = (
Red,
Orange,
Yellow
)