diff --git a/README.md b/README.md index 3ab23fc..b59f231 100644 --- a/README.md +++ b/README.md @@ -166,13 +166,25 @@ keywords kMDItemKeywords, com.apple.metadata:kMDItemKeywords; (_kMDItemUserTags) which are keywords/tags shown in the Finder and searchable in Spotlight using "tag:tag_name". A list of strings. +participants kMDItemParticipants, com.apple.metadata:kMDItemParticipants; + The list of people who are visible in an image or movie or + written about in a document. A list of strings. +projects kMDItemProjects, com.apple.metadata:kMDItemProjects; The + list of projects that this file is part of. For example, if + you were working on a movie all of the files could be marked + as belonging to the project “My Movie”. A list of strings. rating kMDItemStarRating, com.apple.metadata:kMDItemStarRating; User rating of this item. For example, the stars rating of an iTunes track. An integer. +stationary kMDItemFSIsStationery, + com.apple.metadata:kMDItemFSIsStationery; Boolean indicating + if this file is stationery. tags _kMDItemUserTags, com.apple.metadata:_kMDItemUserTags; Finder tags; searchable in Spotlight using "tag:tag_name". If you want tags/keywords visible in the Finder, use this instead of kMDItemKeywords. A list of Tag objects. +version kMDItemVersion, com.apple.metadata:kMDItemVersion; The + version number of this file. A string. wherefroms kMDItemWhereFroms, com.apple.metadata:kMDItemWhereFroms; Describes where the file was obtained from (e.g. URL downloaded from). A list of strings. @@ -198,8 +210,12 @@ Information about commonly used MacOS metadata attributes is available from [App |FinderInfo|finderinfo|com.apple.FinderInfo|Color tag set by the Finder. Colors can also be set by _kMDItemUserTags. This is controlled by the Finder and it's recommended you don't directly access this attribute. If you set or remove a color tag via _kMDItemUserTag, osxmetadata will automatically handle processing of FinderInfo color tag.| |kMDItemHeadline|headline|com.apple.metadata:kMDItemHeadline|A publishable entry providing a synopsis of the contents of the file. A string.| |kMDItemKeywords|keywords|com.apple.metadata:kMDItemKeywords|Keywords associated with this file. For example, “Birthday”, “Important”, etc. This differs from Finder tags (_kMDItemUserTags) which are keywords/tags shown in the Finder and searchable in Spotlight using "tag:tag_name". A list of strings.| -|kMDItemStarRating|rating|com.apple.metadata:kMDItemStarRating|User rating of this item. For example, the stars rating of an iTunes track. An int.| +|kMDItemParticipants|participants|com.apple.metadata:kMDItemParticipants|The list of people who are visible in an image or movie or written about in a document. A list of strings.| +|kMDItemProjects|projects|com.apple.metadata:kMDItemProjects|The list of projects that this file is part of. For example, if you were working on a movie all of the files could be marked as belonging to the project “My Movie”. A list of strings.| +|kMDItemStarRating|rating|com.apple.metadata:kMDItemStarRating|User rating of this item. For example, the stars rating of an iTunes track. An integer.| +|kMDItemFSIsStationery|stationary|com.apple.metadata:kMDItemFSIsStationery|Boolean indicating if this file is stationery.| |_kMDItemUserTags|tags|com.apple.metadata:_kMDItemUserTags|Finder tags; searchable in Spotlight using "tag:tag_name". If you want tags/keywords visible in the Finder, use this instead of kMDItemKeywords. A list of Tag objects.| +|kMDItemVersion|version|com.apple.metadata:kMDItemVersion|The version number of this file. A string.| |kMDItemWhereFroms|wherefroms|com.apple.metadata:kMDItemWhereFroms|Describes where the file was obtained from (e.g. URL downloaded from). A list of strings.| ## Example uses of the package diff --git a/osxmetadata/_version.py b/osxmetadata/_version.py index 95bab6b..07b5a5e 100644 --- a/osxmetadata/_version.py +++ b/osxmetadata/_version.py @@ -1,3 +1,3 @@ """ osxmetadata version """ -__version__ = "0.99.18" +__version__ = "0.99.19" diff --git a/osxmetadata/attributes.py b/osxmetadata/attributes.py index 2b04b81..d9dbe5f 100644 --- a/osxmetadata/attributes.py +++ b/osxmetadata/attributes.py @@ -311,6 +311,19 @@ "The version number of this file. A string.", None, ), + "stationary": Attribute( + "stationary", + "kMDItemFSIsStationery", + kMDItemFSIsStationery, + bool, + False, + False, + bool, + False, + False, + "Boolean indicating if this file is stationery.", + None, + ), } # used for formatting output of --list @@ -379,6 +392,13 @@ def validate_attribute_value(attribute, value): raise TypeError( f"{val} cannot be converted to expected type {attribute.type_}" ) + elif attribute.type_ == bool: + try: + new_val = bool(val) + except ValueError: + raise TypeError( + f"{val} cannot be converted to expected type {attribute.type_}" + ) elif attribute.type_ == datetime.datetime: if isinstance(val, datetime.datetime): new_val = val diff --git a/osxmetadata/constants.py b/osxmetadata/constants.py index b391506..7ba1cc4 100644 --- a/osxmetadata/constants.py +++ b/osxmetadata/constants.py @@ -76,6 +76,7 @@ kMDItemParticipants = "com.apple.metadata:kMDItemParticipants" kMDItemProjects = "com.apple.metadata:kMDItemProjects" kMDItemVersion = "com.apple.metadata:kMDItemVersion" +kMDItemFSIsStationery = "com.apple.metadata:kMDItemFSIsStationery" # Special handling for Finder comments @@ -108,4 +109,5 @@ "kMDItemParticipants", "kMDItemProjects", "kMDItemVersion", + "kMDItemFSIsStationery", ] diff --git a/osxmetadata/osxmetadata.py b/osxmetadata/osxmetadata.py index a79f2a0..928b4eb 100644 --- a/osxmetadata/osxmetadata.py +++ b/osxmetadata/osxmetadata.py @@ -51,6 +51,7 @@ kMDItemUserTags, kMDItemVersion, kMDItemWhereFroms, + kMDItemFSIsStationery, ) from .datetime_utils import ( datetime_naive_to_utc, @@ -81,7 +82,7 @@ "kMDItemParticipants", "kMDItemProjects", "kMDItemVersion", - "_FINDER_COMMENT_NAMES", + "kMDItemFSIsStationery", ] @@ -137,6 +138,7 @@ class OSXMetaData: "participants", "projects", "version", + "stationary", ] def __init__(self, fname, tz_aware=False): @@ -159,7 +161,7 @@ def __init__(self, fname, tz_aware=False): # ATTRIBUTES contains both long and short names, want only the short names (attribute.name) for name in {attribute.name for attribute in ATTRIBUTES.values()}: attribute = ATTRIBUTES[name] - if attribute.class_ not in [str, float, int, datetime.datetime]: + if attribute.class_ not in [str, float, int, bool, datetime.datetime]: super().__setattr__( name, attribute.class_(attribute, self._attrs, self) ) diff --git a/tests/test_bool_attributes.py b/tests/test_bool_attributes.py new file mode 100644 index 0000000..c3a810f --- /dev/null +++ b/tests/test_bool_attributes.py @@ -0,0 +1,122 @@ +""" Test bool attributes """ + +import platform +from tempfile import NamedTemporaryFile, TemporaryDirectory + +import pytest +from osxmetadata.attributes import ATTRIBUTES + + +@pytest.fixture(params=["file", "dir"]) +def temp_file(request): + + # TESTDIR for temporary files usually defaults to "/tmp", + # which may not have XATTR support (e.g. tmpfs); + # manual override here. + TESTDIR = None + if request.param == "file": + tempfile = NamedTemporaryFile(dir=TESTDIR) + tempfilename = tempfile.name + yield tempfilename + tempfile.close() + else: + tempdir = TemporaryDirectory(dir=TESTDIR) + tempdirname = tempdir.name + yield tempdirname + tempdir.cleanup() + + +test_data = [ + (attr) + for attr in sorted(list(ATTRIBUTES.keys())) + if ATTRIBUTES[attr].class_ == bool +] +ids = [ + attr for attr in sorted(list(ATTRIBUTES.keys())) if ATTRIBUTES[attr].class_ == bool +] + +test_data2 = [ + (attribute_name) + for attribute_name in { + ATTRIBUTES[attr].name + for attr in sorted(ATTRIBUTES) + if ATTRIBUTES[attr].class_ == bool + } +] +ids2 = [ + attribute_name + for attribute_name in { + ATTRIBUTES[attr].name + for attr in sorted(ATTRIBUTES) + if ATTRIBUTES[attr].class_ == bool + } +] + + +@pytest.mark.parametrize("attribute", test_data, ids=ids) +def test_str_attribute(temp_file, attribute): + """test set_attribute, get_attribute, etc""" + from osxmetadata import OSXMetaData + from osxmetadata.constants import _FINDER_COMMENT_NAMES + + meta = OSXMetaData(temp_file) + expected = True + meta.set_attribute(attribute, expected) + got = meta.get_attribute(attribute) + assert expected == got + + meta.set_attribute(attribute, not expected) + assert not meta.get_attribute(attribute) + + meta.clear_attribute(attribute) + assert meta.get_attribute(attribute) is None + + # test setting empty string + # setting finder comment to empty string clears it + # but other fields get set to empty string + # this mirrors the way finder comments work in mdls + meta.set_attribute(attribute, "") + assert not meta.get_attribute(attribute) + + with pytest.raises(AttributeError): + meta.update_attribute(attribute, "foo") + + with pytest.raises(TypeError): + meta.discard_attribute(attribute, "foo") + + with pytest.raises(TypeError): + meta.remove_attribute(attribute, "foo") + + +@pytest.mark.parametrize("attribute", test_data2, ids=ids2) +def test_str_attribute_2(temp_file, attribute): + """test getting and setting attribute by name""" + from osxmetadata import OSXMetaData + + meta = OSXMetaData(temp_file) + setattr(meta, attribute, True) + test_attr = getattr(meta, attribute) + assert test_attr + assert meta.get_attribute(attribute) + + +def test_stationary(temp_file): + """test string functions on one of the bool attributes""" + from osxmetadata import OSXMetaData + + meta = OSXMetaData(temp_file) + meta.stationary = True + assert meta.stationary + assert meta.get_attribute("stationary") + + meta.stationary = not meta.stationary + assert not meta.stationary + assert not meta.get_attribute("stationary") + + meta.stationary = None + assert meta.stationary is None + assert meta.get_attribute("stationary") is None + + meta.stationary = True + assert meta.stationary + assert meta.get_attribute("stationary")