Skip to content

Commit

Permalink
Added --json to CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
RhetTbull committed Feb 29, 2020
1 parent 8d9985c commit d4f9906
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 55 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Options:
-v, --version Show the version and exit.
-w, --walk Walk directory tree, processing each file in the
tree
-j, --json Print output in JSON format, for use with --list
and --get.
--set ATTRIBUTE VALUE Set ATTRIBUTE to VALUE
--list List all metadata attributes for FILE
--clear ATTRIBUTE Remove attribute from FILE
Expand All @@ -65,7 +67,7 @@ set keywords to ['foo', 'bar']
Short Name Description
authors kMDItemAuthors, com.apple.metadata:kMDItemAuthors; The
author, or authors, of the contents of the file. An array of
author, or authors, of the contents of the file. A list of
strings.
comment kMDItemComment, com.apple.metadata:kMDItemComment; A comment
related to the file. This differs from the Finder comment,
Expand Down Expand Up @@ -94,15 +96,15 @@ keywords kMDItemKeywords, 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"An
array of strings.
Finder and searchable in Spotlight using "tag:tag_name"A
list of strings.
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.
instead of kMDItemKeywords. A list of strings.
wherefroms kMDItemWhereFroms, com.apple.metadata:kMDItemWhereFroms;
Describes where the file was obtained from (e.g. URL
downloaded from). An array of strings.
downloaded from). A list of strings.
```


Expand Down
14 changes: 8 additions & 6 deletions osxmetadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import xattr

from .attributes import ATTRIBUTES, Attribute
from .classes import _AttributeList, _AttributeTagsList
from .classes import _AttributeList, _AttributeTagsList
from .constants import ( # _DOWNLOAD_DATE,; _FINDER_COMMENT,; _TAGS,; _WHERE_FROM,
_COLORIDS,
_COLORNAMES,
Expand Down Expand Up @@ -46,9 +46,11 @@
clear_finder_comment,
validate_attribute_value,
)
from ._version import __version__

__all__ = [
"OSXMetaData",
"__version__",
"ATTRIBUTES",
"kMDItemAuthors",
"kMDItemComment",
Expand Down Expand Up @@ -127,7 +129,8 @@ def get_attribute(self, attribute_name):

# user tags need special processing to normalize names
if attribute.name == "tags":
return self.tags
self.tags._load_data()
return self.tags.data

try:
plist = plistlib.loads(self._attrs[attribute.constant])
Expand Down Expand Up @@ -167,7 +170,7 @@ def set_attribute(self, attribute_name, value):

# verify type is correct
value = validate_attribute_value(attribute, value)

# if attribute.list and (type(value) == list or type(value) == set):
# for val in value:
# if attribute.type_ != type(val):
Expand Down Expand Up @@ -219,7 +222,7 @@ def append_attribute(self, attribute_name, value, update=False):
# start with existing values
new_value = self.get_attribute(attribute.name)

value = validate_attribute_value(attribute,value)
value = validate_attribute_value(attribute, value)

if attribute.list:
if new_value is not None:
Expand All @@ -240,8 +243,7 @@ def append_attribute(self, attribute_name, value, update=False):
if new_value is not None:
new_value += value
else:
new_value = value

new_value = value

# # verify type is correct
# if attribute.list and (type(value) == list or type(value) == set):
Expand Down
103 changes: 60 additions & 43 deletions osxmetadata/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from ._version import __version__
from .attributes import _LONG_NAME_WIDTH, _SHORT_NAME_WIDTH, ATTRIBUTES
from .classes import _AttributeList, _AttributeTagsList
from .constants import _TAGS_NAMES
from .utils import validate_attribute_value

Expand Down Expand Up @@ -106,9 +107,8 @@ def get_help(self, ctx):
"-j",
"json_",
is_flag=True,
help="Print output in JSON format, for use with --list.",
help="Print output in JSON format, for use with --list and --get.",
default=False,
hidden=True, # not yet implemented
)
DEBUG_OPTION = click.option(
"--debug", required=False, is_flag=True, default=False, hidden=True
Expand Down Expand Up @@ -218,7 +218,14 @@ def cli(
if invalid_attr:
click.echo("") # add a new line before rest of help text
click.echo(ctx.get_help())
ctx.exit()
ctx.exit(2)

# check that json_ only used with get or list_
if json_ and not any([get, list_]):
click.echo("--json can only be used with --get or --list", err=True)
click.echo("") # add a new line before rest of help text
click.echo(ctx.get_help())
ctx.exit(2)

for f in files:
if walk and os.path.isdir(f):
Expand Down Expand Up @@ -319,59 +326,69 @@ def process_file(fpath, json_, set_, append, update, remove, clear, get, list_):

if get:
logging.debug(f"get: {get}")
if json_:
data = {}
data["_version"] = __version__
data["_filepath"] = str(fpath)
data["_filename"] = fpath.name
for attr in get:
attribute = ATTRIBUTES[attr]
logging.debug(f"getting {attr}")
value = md.get_attribute(attribute.name)
click.echo(
f"{attribute.name:{_SHORT_NAME_WIDTH}}{attribute.constant:{_LONG_NAME_WIDTH}} = {value}"
)
if json_:
if attribute.type_ == datetime.datetime:
# need to convert datetime.datetime to string to serialize
value = md.get_attribute(attribute.name)
if type(value) == list:
value = [v.isoformat() for v in value]
else:
value = value.isoformat()
data[attribute.constant] = value
else:
# get raw value
data[attribute.constant] = md.get_attribute(attribute.name)
else:
value = md.get_attribute_str(attribute.name)
click.echo(
f"{attribute.name:{_SHORT_NAME_WIDTH}}{attribute.constant:{_LONG_NAME_WIDTH}} = {value}"
)
if json_:
json_str = json.dumps(data)
click.echo(json_str)

if list_:
attribute_list = md.list_metadata()
if json_:
data = {}
data["_version"] = __version__
data["_filepath"] = str(fpath)
data["_filename"] = fpath.name
for attr in attribute_list:
try:
attribute = ATTRIBUTES[attr]
value = md.get_attribute_str(attribute.name)
click.echo(
f"{attribute.name:{_SHORT_NAME_WIDTH}}{attribute.constant:{_LONG_NAME_WIDTH}} = {value}"
)
if json_:
if attribute.type_ == datetime.datetime:
# need to convert datetime.datetime to string to serialize
value = md.get_attribute(attribute.name)
if type(value) == list:
value = [v.isoformat() for v in value]
else:
value = value.isoformat()
data[attribute.constant] = value
else:
# get raw value
data[attribute.constant] = md.get_attribute(attribute.name)
else:
value = md.get_attribute_str(attribute.name)
click.echo(
f"{attribute.name:{_SHORT_NAME_WIDTH}}{attribute.constant:{_LONG_NAME_WIDTH}} = {value}"
)
except KeyError:
click.echo(
f"{'UNKNOWN':{_SHORT_NAME_WIDTH}}{attr:{_LONG_NAME_WIDTH}} = THIS ATTRIBUTE NOT HANDLED"
)


# def write_json_data(fp, data):
# json.dump(data, fp)
# fp.write("\n")


# def write_text_data(fp, data):
# file = data["file"]

# fc = data["fc"]
# fc = fc if fc is not None else ""

# dldate = data["dldate"]
# dldate = dldate if dldate is not None else ""

# desc = data["description"]
# desc = desc if desc is not None else ""

# where_from = data["where_from"]
# where_from = where_from if where_from is not None else ""

# tags = data["tags"]
# tags = tags if len(tags) != 0 else ""

# print(f"file: {file}", file=fp)
# print(f"description: {desc}", file=fp)
# print(f"tags: {tags}", file=fp)
# print(f"Finder comment: {fc}", file=fp)
# print(f"Download date: {dldate}", file=fp)
# print(f"Where from: {where_from}", file=fp)
# print("\n", file=fp)
if json_:
json_str = json.dumps(data)
click.echo(json_str)


# def restore_from_json(json_file, quiet=False):
Expand Down
2 changes: 1 addition & 1 deletion osxmetadata/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.98.4"
__version__ = "0.98.5"
56 changes: 56 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,62 @@ def test_datetime_list_attributes(temp_file, attribute):
assert meta.get_attribute(attribute) == [dt]


def test_get_json(temp_file):
import json
import pathlib
from osxmetadata import OSXMetaData, ATTRIBUTES, __version__
from osxmetadata.__main__ import cli

runner = CliRunner()
result = runner.invoke(
cli, ["--set", "tags", "foo", "--set", "tags", "bar", temp_file]
)
result = runner.invoke(cli, ["--get", "tags", "--json", temp_file])
assert result.exit_code == 0
json_ = json.loads(result.stdout)
assert json_["com.apple.metadata:_kMDItemUserTags"] == ["foo", "bar"]
assert json_["_version"] == __version__
assert json_["_filename"] == pathlib.Path(temp_file).name


def test_list_json(temp_file):
import json
import pathlib
from osxmetadata import OSXMetaData, ATTRIBUTES, __version__
from osxmetadata.__main__ import cli

runner = CliRunner()
result = runner.invoke(
cli, ["--set", "tags", "foo", "--set", "tags", "bar", temp_file]
)
result = runner.invoke(cli, ["--list", "--json", temp_file])
assert result.exit_code == 0
json_ = json.loads(result.stdout)
assert json_["com.apple.metadata:_kMDItemUserTags"] == ["foo", "bar"]
assert json_["_version"] == __version__
assert json_["_filename"] == pathlib.Path(temp_file).name


def test_cli_error_json(temp_file):
from osxmetadata import OSXMetaData, ATTRIBUTES
from osxmetadata.__main__ import cli

runner = CliRunner()
result = runner.invoke(cli, ["--set", "tags", "foo", "--json", temp_file])
assert result.exit_code == 2
assert "--json can only be used with --get or --list" in result.stdout


def test_cli_error_bad_attribute(temp_file):
from osxmetadata import OSXMetaData, ATTRIBUTES
from osxmetadata.__main__ import cli

runner = CliRunner()
result = runner.invoke(cli, ["--set", "foo", "bar", temp_file])
assert result.exit_code == 2
assert "Invalid attribute foo" in result.stdout


def test_cli_error(temp_file):
from osxmetadata.__main__ import cli

Expand Down

0 comments on commit d4f9906

Please sign in to comment.