getting and setting mac file and folder finder labels from Python

Question:

I have been trying to find out how to get and set the colour of file labels from python.

The closest thing I’ve found to a solution was this, but I can’t seem to find the module macfile anywhere. Am I just not looking hard enough?

Is there a different way to achieve this if not?

Asked By: GP89

||

Answers:

You can do this in python using the xattr module.

Here is an example, taken mostly from this question:

from xattr import xattr

colornames = {
    0: 'none',
    1: 'gray',
    2: 'green',
    3: 'purple',
    4: 'blue',
    5: 'yellow',
    6: 'red',
    7: 'orange',
}

attrs = xattr('./test.cpp')

try:
    finder_attrs = attrs['com.apple.FinderInfo']
    color = finder_attrs[9] >> 1 & 7
except KeyError:
    color = 0

print colornames[color]

Since I have colored this file with the red label, this prints 'red' for me. You can use the xattr module to also write a new label back to disk.

Answered By: jterrace

If you follow favoretti’s link and then scroll down a bit, there’s a link to
https://github.com/danthedeckie/display_colors, which does this via xattr, but without the binary manipulations. I rewrote his code a bit:

from xattr import xattr

def set_label(filename, color_name):
    colors = ['none', 'gray', 'green', 'purple', 'blue', 'yellow', 'red', 'orange']
    key = u'com.apple.FinderInfo'
    attrs = xattr(filename)
    current = attrs.copy().get(key, chr(0)*32)
    changed = current[:9] + chr(colors.index(color_name)*2) + current[10:]
    attrs.set(key, changed)

set_label('/Users/chbrown/Desktop', 'green')
Answered By: chbrown

The macfile module is part of the appscript module, and was renamed to mactypes in “2006-11-20 — 0.2.0”

Using this module, here are two functions to get and set the finder labels with appscript version 1.0:

from appscript import app
from mactypes import File as MacFile


# Note these label names could be changed in the Finder preferences,
# but the colours are fixed
FINDER_LABEL_NAMES = {
    0: 'none',
    1: 'orange',
    2: 'red',
    3: 'yellow',
    4: 'blue',
    5: 'purple',
    6: 'green',
    7: 'gray',
}


def finder_label(path):
    """Get the Finder label colour for the given path

    >>> finder_label("/tmp/example.txt")
    'green'
    """
    idx = app('Finder').items[MacFile(path)].label_index.get()
    return FINDER_LABEL_NAMES[idx]


def set_finder_label(path, label):
    """Set the Finder label by colour

    >>> set_finder_label("/tmp/example.txt", "blue")
    """

    label_rev = {v:k for k, v in FINDER_LABEL_NAMES.items()}

    available = label_rev.keys()
    if label not in available:
        raise ValueError(
            "%r not in available labels of %s" % (
                label,
                ", ".join(available)))

    app('Finder').items[MacFile(path)].label_index.set(label_rev[label])

if __name__ == "__main__":
    # Touch file
    path = "blah"
    open(path, "w").close()

    # Toggle label colour
    if finder_label(path) == "green":
        set_finder_label(path, "red")
    else:
        set_finder_label(path, "green")
Answered By: dbr

I don’t know if this question is still relevant to anybody but there is a new package “mac-tag” that solves this issue.

pip install mac-tag

and then you have functions like:

function    __doc__
mac_tag.add(tags, path) # add tags to path(s)
mac_tag.find(tags, path=None)   # return a list of all paths with tags, limited to path(s) if present
mac_tag.get(path)   # return dict where keys are paths, values are lists of tags. equivalent of tag -l
mac_tag.match(tags, path)   # return a list of paths with with matching tags
mac_tag.parse_list_output(out)  # parse tag -l output and return dict
mac_tag.remove(tags, path)  # remove tags from path(s)
mac_tag.update(tags, path)  # set path(s) tags. equivalent of `tag -s

complete documentation at: https://pypi.org/project/mac-tag/

Answered By: CG_python

Suppose you have this folder:

enter image description here

Notice that some files have multiple tags. One tag is Work which is a custom tag.

There are three ways that I have used to get the Finder tags into Python.


First, use xattr to generate a binary plist of extended attributes from the key 'com.apple.metadata:_kMDItemUserTags' and then convert that to a list with plistlib:

import xattr
import plistlib 

def get_tags_xattr(fn):
    try:
        bpl=xattr.getxattr(fn, 'com.apple.metadata:_kMDItemUserTags')
        rtr=[e.partition('n')[0] 
                  for e in plistlib.loads(bpl, fmt=plistlib.FMT_BINARY)]
        return rtr if rtr else None
    
    except OSError:
        return None 

The second is to use the program tag which can be installed with brew:

from subprocess import run, PIPE

def get_tags_tag(fn):
    if 'com.apple.metadata:_kMDItemUserTags' in xattr.listxattr(fn):
        rtr=run(['tag', '-g', '--no-name', fn], 
            stdout=PIPE).stdout.decode('utf-8').splitlines()
        return rtr if rtr else None
    
    return None 

The third is to use the PyPi module osxmetadata:

import osxmetadata

def get_tags_osxmeta(fn):
    tags=[t.name for t in osxmetadata.OSXMetaData(fn).tags]
    if tags:
        return tags 
    
    return None 

Here is a demo of all three:

from pathlib import Path 

p=Path('/tmp/test')

for fn in (fn for fn in p.glob('*') if fn.is_file()):
    print(fn, get_tags_osxmeta(fn), 
          get_tags_tag(fn), 
          get_tags_xattr(fn))   

Prints on that sample folder:

/tmp/test/DSC_2930-m.jpg ['Red', 'Green'] ['Green', 'Red'] ['Red', 'Green']
/tmp/test/DSC_2929.JPG None None None
/tmp/test/DSC_2939.JPG None None None
/tmp/test/DSC_2938.JPG ['Red'] ['Red'] ['Red']
/tmp/test/DSC_2937.JPG None None None
/tmp/test/DSC_2942-m.jpg ['Red', 'Orange', 'Gray', 'Work'] ['Gray', 'Orange', 'Red', 'Work'] ['Red', 'Orange', 'Gray', 'Work']
/tmp/test/DSC_2934.JPG ['Red'] ['Red'] ['Red']
/tmp/test/DSC_2930.JPG None None None
/tmp/test/DSC_2931.JPG None None None
/tmp/test/DSC_2933.JPG None None None
/tmp/test/DSC_2932.JPG ['Red'] ['Red'] ['Red']
/tmp/test/DSC_2941.JPG None None None
/tmp/test/DSC_2942.JPG None None None

The best of those (that which I use most often) is the osxmetadata module. It is fast and very flexible. If you just need the tags, the other ways work well too. The first is likely the fastest.

Answered By: dawg
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.