SkillAgentSearch skills...

Osxmetadata

Python package to read and write various MacOS extended attribute metadata such as tags/keywords and Finder comments from files. Includes CLI tool for reading/writing metadata.

Install / Use

/learn @RhetTbull/Osxmetadata
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

osxmetadata

Code style: black License: MIT All Contributors

What is osxmetadata?

osxmetadata provides a simple interface to access various metadata about MacOS / OS X files. Currently supported metadata attributes include tags/keywords, Finder comments, authors, etc.

Why osxmetadata?

Apple provides rich support for file metadata through the MDItem class and the NSURL getResourceValue:forKey:error: method. However, Apple does not provide a way to easily set much of the metadata. For example, while there is a documented MDItem MDItemCopyAttribute to copy metadata attributes such as kMDItemAuthors, Apple does not provide a public interface to set this data. Other data, such as Finder comments, can only be set through sending AppleScript commands to the Finder and others, like Finder tags can be retrieved but cannot be set through the public API.

osxmetadata provides a unified interface to get and set most of the metadata available on your Mac from python. It uses a combination of documented and undocumented APIs to access the metadata. It also provides a simple interface to set Finder tags and Finder comments.

MacOS provides some tools to view these various metadata attributes. For example, mdls lists the MDItem Spotlight metadata associated with a file but doesn't let you edit the data. osxmetadata makes it easy to to both view and manipulate the macOS metadata attributes, either programmatically or through an included osxmetadata command line tool.

Supported operating systems

Only works on MacOS. Tested on macOS 13.5.1 (Ventura); should work on all versions of macOS 10.15 and later. Supported on Python 3.10+.

Installation instructions

Installation using pipx

If you aren't familiar with installing python applications, I recommend you install osxmetadata with pipx. If you use pipx, you will not need to create a virtual environment as pipx takes care of this. The easiest way to do this on a Mac is to use homebrew:

  • Open Terminal (search for Terminal in Spotlight or look in Applications/Utilities)
  • Install homebrew according to instructions at https://brew.sh/
  • Type the following into Terminal: brew install pipx
  • Then type this: pipx install osxmetadata
  • Now you should be able to run osxmetadata by typing: osxmetadata

Once you've installed osxmetadata with pipx, to upgrade to the latest version:

pipx upgrade osxmetadata

Installation using pip

You can also install directly from pypi:

pip install osxmetadata

Once you've installed osxmetadata with pip, to upgrade to the latest version:

pip install --upgrade osxmetadata

Installation from git repository

OSXMetaData uses setuptools, thus simply run:

git clone https://github.com/RhetTbull/osxmetadata.git
cd osxmetadata
pip install poetry
poetry install

I recommend you create a virtual environment before installing osxmetadata.

Using the API

>>> import datetime
>>> import pathlib
>>> from osxmetadata import *
>>> pathlib.Path("test_file.txt").touch()
>>> md = OSXMetaData("test_file.txt")
>>> md.set(kMDItemAuthors, ["Jane Smith", "John Doe"])
>>> md.get(kMDItemAuthors)
['Jane Smith', 'John Doe']
>>> md.kMDItemFinderComment = "This is my comment"
>>> md.kMDItemFinderComment
'This is my comment'
>>> md.tags = [Tag("Test", FINDER_COLOR_NONE), Tag("ToDo", FINDER_COLOR_RED)]
>>> md.tags
[Tag(name='Test', color=0), Tag(name='ToDo', color=6)]
>>> md[kMDItemDueDate] = datetime.datetime(2022,10,1)
>>> md[kMDItemDueDate]
datetime.datetime(2022, 10, 1, 0, 0)
>>>

Somewhat contrary to the Zen of Python, osxmetadata provides more than one way to access metadata attributes. You can get and set metadata attributes using the get()/set() getter/setter methods, using the attribute name as a dictionary key on the OSXMetaData object, or using the attribute name as an attribute on the OSXMetaData() object. For example, the following are all equivalent:

  • OSXMetaData.get(attribute) - get the value of the metadata attribute attribute
  • OSXMetaData[attribute] - get the value of the metadata attribute attribute
  • OSXMetaData.attribute - get the value of the metadata attribute attribute

As are the following:

  • OSXMetaData.set(attribute, value) - set the value of the metadata attribute attribute to value
  • OSXMetaData[attribute] = value - set the value of the metadata attribute attribute to value
  • OSXMetaData.attribute = value - set the value of the metadata attribute attribute to value

This allows you to use osxmetadata in accordance with your own code style preferences.

Supported attribute names include all attributes defined for MDItem and all resource keys defined for NSURL. Additionally, the metadata constants defined in the MDImporter are supported as well as the following additional attributes:

  • _kMDItemUserTags - list of Finder tags
  • kMDItemDownloadedDate - list of datetime objects for when the file was downloaded

Additionally, osxmetadata defines a "shortcut name" attribute for each MDItem attribute that can be used as a shortcut OSXMetaData class attribute. The shortcut name is the lowercase value of text following kMDItem for each attribute. For example, kMDItemAuthors has a short name of authors so you can set the authors like this:

>>> from osxmetadata import *
>>> md = OSXMetaData("test_file.txt")
>>> md.authors = ["Jane Smith", "John Doe"]
>>> md.authors
['Jane Smith', 'John Doe']
>>>

and kMDItemDueDate would have a short name of duedate:

>>> from osxmetadata import *
>>> import datetime
>>> md = OSXMetaData("test_file.txt")
>>> md.duedate = datetime.datetime(2022, 10, 1)
>>> md.duedate
datetime.datetime(2022, 10, 1, 0, 0)
>>>

The names of all supported attributes are available in the osxmetadata.ALL_ATTRIBUTES set:

>>> from osxmetadata import ALL_ATTRIBUTES
>>> "kMDItemDueDate" in ALL_ATTRIBUTES
True
>>> "NSURLTagNamesKey" in ALL_ATTRIBUTES
True
>>> "findercomment" in ALL_ATTRIBUTES
True
>>>

The class attributes are handled dynamically which, unfortunately, means that IDEs like PyCharm and Visual Studio Code cannot provide tab-completion for them.

[!NOTE] When writing or updating metadata with OSXMetaData, the OS will take some time to update the metadata on disk; in my testing, this can be as short as 100ms or as long as 3s. This means that if you read the metadata immediately after writing it, you may not see the updated metadata. If your use case requires the use of immediate read after write, you may need to implement a delay in your code to allow the OS time to update the metadata on disk. This appears to be an OS limitation and not something that can be controlled by osxmetadata.


## Finder Tags

Unlike other attributes, which are mapped to native Python types appropriate for the source Objective C type, Finder tags (`_kMDItemUserTags` or `tags`) have two components: a name (str) and a color ID (unsigned int in range 0 to 7) representing a color tag in the Finder.  Reading tags returns a list of `Tag` namedtuples and setting tags requires a list of `Tag` namedtuples.

```pycon
>>> from osxmetadata import *
>>> md = OSXMetaData("test_file.txt")
>>> md.tags = [Tag("Test", FINDER_COLOR_NONE), Tag("ToDo", FINDER_COLOR_RED)]
>>> md.tags
[Tag(name='Test', color=0), Tag(name='ToDo', color=6)]
>>> md.get("_kMDItemUserTags")
[Tag(name='Test', color=0), Tag(name='ToDo', color=6)]
>>>

Tag names (but not colors) can also be accessed through the NSURLTagNamesKey resource key and the label color ID is accessible through NSURLLabelNumberKey; the localized label color name is accessible through NSURLLocalizedLabelKey though these latter two resource keys only return a single color whereas a file may have more than one color tag. For most purposes, I recommend using the tags attribute as it is more convenient and provides access to both the name and color ID of the tag.

>>> from osxmetadata import *
>>> md = OSXMetaData("test_file.txt")
>>> md.tags = [Tag("Test", FINDER_COLOR_NONE), Tag("ToDo", FINDER_COLOR_RED)]
>>> md.tags
[Tag(name='Test', color=0), Tag(name='ToDo', color=6)]
>>> md.NSURLTagNamesKey
(
    Test,
    ToDo
)
>>> md.NSURLLabelNumberKey
6
>>> md.NSURLLocalizedLabelKey
'Red'
>>> md.NSURLTagNamesKey = ["NewTag"]
>>> md.NSURLTagNamesKey
(
    NewTag
)
>>> md.tags
[Tag(name='NewTag', color=0)]
>>>

Create a Tag namedtuple

Tag(name, color)

  • name: tag name (str)
  • color: color ID for Finder color label associated with tag (int)

Valid color constants (exported by osxmetadata):

  • FINDER_COLOR_NONE = 0
  • FINDER_COLOR_GRAY = 1
  • FINDER_COLOR_GREEN = 2
  • FINDER_COLOR_PURPLE = 3
  • FINDER_COLOR_BLUE = 4
  • FINDER_COLOR_YELLOW = 5
View on GitHub
GitHub Stars153
CategoryContent
Updated25d ago
Forks4

Languages

Python

Security Score

100/100

Audited on Mar 11, 2026

No findings