Skip to content

Commit

Permalink
Merge pull request #86 from RhetTbull/bug_kMDItemKeywords_string_valu…
Browse files Browse the repository at this point in the history
…e_83

Bug k md item keywords string value 83
  • Loading branch information
RhetTbull committed Nov 16, 2022
2 parents 243081f + 20ef4ad commit 4ec2fd7
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.2.0
current_version = 1.2.1
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
serialize = {major}.{minor}.{patch}

Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,23 @@ Options:
-j, --json Print output in JSON format, for use with
--list and --get.
-X, --wipe Wipe all metadata attributes from FILE.
-s, --set ATTRIBUTE VALUE Set ATTRIBUTE to VALUE.
-s, --set ATTRIBUTE VALUE Set ATTRIBUTE to VALUE. If ATTRIBUTE is a
multi-value attribute, such as keywords
(kMDItemKeywords), you may specify --set
multiple times to add to the array of values:
'--set keywords foo --set keywords bar' will
set keywords to ['foo', 'bar']. Not that this
will overwrite any existing values for the
attribute; see also --append.
-l, --list List all metadata attributes for FILE.
-c, --clear ATTRIBUTE Remove attribute from FILE.
-a, --append ATTRIBUTE VALUE Append VALUE to ATTRIBUTE; for multi-valued
attributes, appends only if VALUE is not
already present.
already present. May be used in combination
with --set to add to an existing value: '--set
keywords foo --append keywords bar' will set
keywords to ['foo', 'bar'], overwriting any
existing values for the attribute.
-g, --get ATTRIBUTE Get value of ATTRIBUTE.
-r, --remove ATTRIBUTE VALUE Remove VALUE from ATTRIBUTE; only applies to
multi-valued attributes.
Expand Down
11 changes: 9 additions & 2 deletions osxmetadata/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,11 @@ def get_help(self, ctx):
"-s",
"set_",
metavar="ATTRIBUTE VALUE",
help="Set ATTRIBUTE to VALUE.",
help="Set ATTRIBUTE to VALUE. "
"If ATTRIBUTE is a multi-value attribute, such as keywords (kMDItemKeywords), "
"you may specify --set multiple times to add to the array of values: "
"'--set keywords foo --set keywords bar' will set keywords to ['foo', 'bar']. "
"Not that this will overwrite any existing values for the attribute; see also --append.",
nargs=2,
multiple=True,
required=False,
Expand Down Expand Up @@ -788,7 +792,10 @@ def get_help(self, ctx):
"--append",
"-a",
metavar="ATTRIBUTE VALUE",
help="Append VALUE to ATTRIBUTE; for multi-valued attributes, appends only if VALUE is not already present.",
help="Append VALUE to ATTRIBUTE; for multi-valued attributes, appends only if VALUE is not already present. "
"May be used in combination with --set to add to an existing value: "
"'--set keywords foo --append keywords bar' will set keywords to ['foo', 'bar'], "
"overwriting any existing values for the attribute.",
nargs=2,
multiple=True,
required=False,
Expand Down
2 changes: 1 addition & 1 deletion osxmetadata/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
""" osxmetadata version """

__version__ = "1.2.0"
__version__ = "1.2.1"
14 changes: 11 additions & 3 deletions osxmetadata/mditem.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
# appropriate Objective C object pointers.


def MDItemSetAttribute(mditem, name, attr):
def MDItemSetAttribute(mditem, name, attr) -> bool:
"""dummy function definition"""
...

Expand Down Expand Up @@ -100,7 +100,7 @@ def set_mditem_metadata(
) -> bool:
"""Set file metadata using undocumented function MDItemSetAttribute
file: path to file
mditem: MDItem object
attribute: metadata attribute to set
value: value to set attribute to; must match the type expected by the attribute (e.g. bool, str, List[str], float, datetime.datetime)
Expand Down Expand Up @@ -149,7 +149,13 @@ def get_mditem_metadata(
elif attribute_type == "float":
return float(value)
elif attribute_type == "list":
return [str(x) for x in value]
# some attributes like kMDItemKeywords do not always follow the documented type
# and can return a single comma-delimited string instead of a list (See #83)
return (
str(value).split(",")
if isinstance(value, (objc.pyobjc_unicode, str))
else [str(x) for x in value]
)
elif attribute_type == "datetime.datetime":
return CFDate_to_datetime(value)
elif attribute_type == "list[datetime.datetime]":
Expand All @@ -160,6 +166,8 @@ def get_mditem_metadata(
elif "__NSTaggedDate" in repr(type(value)):
# this is a hack but works for MDImporter attributes that don't have a documented type
return NSDate_to_datetime(value)
elif isinstance(value, objc.pyobjc_unicode):
return str(value)
else:
return value
except ValueError:
Expand Down
35 changes: 34 additions & 1 deletion osxmetadata/osxmetadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@
set_finderinfo_stationerypad,
)
from .finder_tags import _kMDItemUserTags, get_finder_tags, set_finder_tags
from .mditem import MDItemValueType, get_mditem_metadata, set_or_remove_mditem_metadata
from .mditem import (
MDItemValueType,
get_mditem_metadata,
set_or_remove_mditem_metadata,
MDItemSetAttribute,
)
from .nsurl_metadata import get_nsurl_metadata, set_nsurl_metadata

ALL_ATTRIBUTES = {
Expand Down Expand Up @@ -197,6 +202,34 @@ def to_json(

return json.dumps(dict_data, indent=indent)

def get_mditem_attribute_value(self, attribute: str) -> t.Any:
"""Get the raw MDItem attribute value without any type conversion.
Args:
attribute: metadata attribute name
Returns:
raw MDItem attribute value as returned by CoreServices.MDItemCopyAttribute()
Note: This is a low level function that you probably don't need to use,
but may be useful in some cases. You should probably use the get() method instead.
"""
return CoreServices.MDItemCopyAttribute(self._mditem, attribute)

def set_mditem_attribute_value(self, attribute: str, value: t.Any) -> bool:
"""Set the raw MDItem attribute value without any type conversion.
Args:
attribute: metadata attribute name
value: value to set attribute to
Returns: True if successful otherwise False
Note: This is a low level function that you probably don't need to use,
but may be useful in some cases. You should probably use the set() method instead.
"""
return MDItemSetAttribute(self._mditem, attribute, value)

@property
def path(self) -> str:
"""Return path to file"""
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "osxmetadata"
version = "1.2.0"
version = "1.2.1"
description = "Read and write meta data, such as tags/keywords, Finder comments, etc. on MacOS files"
authors = ["Rhet Turnbull <rturnbull+git@gmail.com>"]
license = "MIT License"
Expand Down
90 changes: 90 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,50 @@ def test_cli_set(test_file):
assert md.description == "Goodbye World"


def test_cli_set_multi_keywords_1(test_file):
"""Test --set with multiple keywords (#83)"""

runner = CliRunner()
result = runner.invoke(
cli,
[
"--set",
"keywords",
"Foo",
"--set",
"keywords",
"Bar",
test_file.name,
],
)
snooze()
assert result.exit_code == 0
md = OSXMetaData(test_file.name)
assert sorted(md.keywords) == ["Bar", "Foo"]


def test_cli_set_multi_keywords_2(test_file):
"""Test --set, --append with multiple keywords (#83)"""

runner = CliRunner()
result = runner.invoke(
cli,
[
"--set",
"keywords",
"Foo",
"--append",
"keywords",
"Bar",
test_file.name,
],
)
snooze()
assert result.exit_code == 0
md = OSXMetaData(test_file.name)
assert sorted(md.keywords) == ["Bar", "Foo"]


def test_cli_clear(test_file):
"""Test --clear"""

Expand Down Expand Up @@ -151,6 +195,52 @@ def test_cli_append(test_file):
assert md.tags == [Tag("test", 0)]


def test_cli_set_then_append(test_file):
"""Test --set then --append"""

md = OSXMetaData(test_file.name)
md.authors = ["John Doe"]

# set initial value
runner = CliRunner()
result = runner.invoke(
cli,
[
"--set",
"keywords",
"foo",
test_file.name,
],
)
assert result.exit_code == 0

# set again and verify that it overwrites
result = runner.invoke(
cli,
[
"--set",
"keywords",
"bar",
test_file.name,
],
)
assert result.exit_code == 0
assert md.keywords == ["bar"]

# append and verify that it appends
result = runner.invoke(
cli,
[
"--append",
"keywords",
"baz",
test_file.name,
],
)
assert result.exit_code == 0
assert sorted(md.keywords) == ["bar", "baz"]


def test_cli_get(test_file):
"""Test --get"""

Expand Down
10 changes: 10 additions & 0 deletions tests/test_mditem_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,13 @@ def test_mditem_attributes_audio(test_audio):

md = OSXMetaData(test_audio)
assert md.get("kMDItemAudioSampleRate") == 44100.0


def test_get_set_mditem_attribute_value(test_file):
"""test get and set of mditem attribute value using the direct methods without value conversion, #83"""

md = OSXMetaData(test_file.name)
md.set_mditem_attribute_value("kMDItemComment", "foo,bar")
snooze()
assert md.get_mditem_attribute_value("kMDItemComment") == "foo,bar"
assert md.comment == "foo,bar"

0 comments on commit 4ec2fd7

Please sign in to comment.