Skip to content

Commit

Permalink
All folder tests now pass
Browse files Browse the repository at this point in the history
  • Loading branch information
RhetTbull committed Nov 29, 2022
1 parent ac6bd42 commit b14e8b8
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 35 deletions.
54 changes: 26 additions & 28 deletions photoscript/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,29 +151,28 @@ def photos(self, search=None, uuid=None, range_=None):
Args:
search: optional text string to search for (returns matching items)
uuid: optional list of UUIDs to get
range\_: optional list of [start, stop] sequence of photos to get
range: optional list of [start, stop] sequence of photos to get
Returns:
Generator that yields Photo objects
Raises:
ValueError if more than one of search, uuid, range\_ passed or invalid range\_
TypeError if list not passed for range\_
ValueError if more than one of search, uuid, range passed or invalid range
TypeError if list not passed for range
Note: photos() returns a generator instead of a list because retrieving all the photos
from a large Photos library can take a very long time--on my system, the rate is about 1
per second; this is limited by the Photos AppleScript interface and I've not found
anyway to speed it up. Using a generator allows you process photos individually rather
than waiting, possibly hours, for Photos to return the results.
range\_ works like python's range function. Thus range\_=[0,4] will return
Photos 0, 1, 2, 3; range\_=[10] returns the first 10 photos in the library;
range\_ start must be in range 0 to len(PhotosLibrary())-1,
range works like python's range function. Thus range=[0,4] will return
Photos 0, 1, 2, 3; range=[10] returns the first 10 photos in the library;
range start must be in range 0 to len(PhotosLibrary())-1,
stop in range 1 to len(PhotosLibrary()). You may be able to optimize the speed by which
photos are return by chunking up requests in batches of photos using range\_,
photos are return by chunking up requests in batches of photos using range,
e.g. request 10 photos at a time.
"""

if len([x for x in [search, uuid, range_] if x]) > 1:
raise ValueError("Cannot pass more than one of search, uuid, range_")

Expand Down Expand Up @@ -211,10 +210,7 @@ def photos(self, search=None, uuid=None, range_=None):

photo_ids = run_script("photosLibraryGetPhotoByRange", start + 1, stop)

if photo_ids:
return self._iterphotos(uuids=photo_ids)
else:
return []
return self._iterphotos(uuids=photo_ids) if photo_ids else []

def _iterphotos(self, uuids=None):
if uuids:
Expand Down Expand Up @@ -413,7 +409,7 @@ def folders(self, top_level=True):
folder_ids = run_script("photosLibraryFolderIDs", top_level)
return [Folder(uuid) for uuid in folder_ids]

def create_folder(self, name, folder: "Folder" = None) -> "Folder":
def create_folder(self, name: str, folder: "Folder" = None) -> "Folder":
"""creates a folder
Args:
Expand All @@ -435,7 +431,7 @@ def create_folder(self, name, folder: "Folder" = None) -> "Folder":
)

if folder_id != kMissingValue:
return Folder(folder_id)
return Folder(idstring=folder_id)
else:
raise AppleScriptError(f"Could not create folder {name}")

Expand Down Expand Up @@ -501,10 +497,15 @@ def make_album_folders(self, album_name, folder_path):
return album

def delete_folder(self, folder: "Folder"):
"""Deletes folder
"""Deletes folder (and all its sub-folders and albums)
Args:
folder: a Folder object for folder to delete
Notes:
On macOS 10.15 & above, only top-level folders can be deleted.
Sub-folders cannot be deleted due to a bug in Photos' AppleScript
implementation.
"""
return run_script("photosLibraryDeleteFolder", folder.idstring)

Expand Down Expand Up @@ -826,7 +827,6 @@ def __init__(
idstring: idstring of folder:
"folder id(\"E0CD4B6C-CB43-46A6-B8A3-67D1FB4D0F3D/L0/020\") of folder id(\"CB051A4C-2CB7-4B90-B59B-08CC4D0C2823/L0/020\")"
"""

if sum(bool(x) for x in (path, uuid, idstring)) != 1:
raise ValueError(
"One (and only one) of path, uuid, or idstring must be specified"
Expand All @@ -847,7 +847,9 @@ def __init__(
elif self._id is not None:
# if uuid was passed, _id will have been initialized above
# second argument is False so search is not limited to top-level folders
self._idstring = run_script("photosLibraryGetFolderIDStringForID", self._id, False)
self._idstring = run_script(
"photosLibraryGetFolderIDStringForID", self._id, False
)
if self._idstring == kMissingValue:
raise ValueError(f"Folder id {self._id} does not exist")

Expand Down Expand Up @@ -913,11 +915,7 @@ def parent_id(self):
def parent(self):
"""Return parent Folder object"""
parent_idstring = self.parent_id
return (
Folder(idstring=parent_idstring)
if parent_idstring != kMissingValue
else None
)
return Folder(idstring=parent_idstring) if parent_idstring is not None else None

def path_str(self, delim="/"):
"""Return internal library path to folder as string.
Expand Down Expand Up @@ -972,7 +970,7 @@ def folder(self, name):
"""
return next((folder for folder in self.subfolders if folder.name == name), None)

def create_album(self, name):
def create_album(self, name: str) -> "Album":
"""Creates an album in this folder
Args:
Expand All @@ -981,22 +979,22 @@ def create_album(self, name):
Returns:
Album object for newly created album
"""
return PhotosLibrary().create_album(name, folder=self)
return PhotosLibrary().create_album(name=name, folder=self)

def create_folder(self, name):
def create_folder(self, name: str) -> "Folder":
"""creates a folder in this folder
Returns:
Folder object for newly created folder
"""
return PhotosLibrary().create_folder(name, folder=self)
return PhotosLibrary().create_folder(name=name, folder=self)

def spotlight(self):
"""spotlight the folder in Photos"""
run_script("folderSpotlight", self.id)
run_script("folderSpotlight", self._idstring)

def __len__(self):
return run_script("folderCount", self.id)
return run_script("folderCount", self._idstring)


class Photo:
Expand Down
6 changes: 3 additions & 3 deletions photoscript/photoscript.applescript
Original file line number Diff line number Diff line change
Expand Up @@ -647,13 +647,13 @@ on photosLibraryCreateFolder(folderName)
end tell
end photosLibraryCreateFolder

on photosLibraryCreateFolderAtFolder(folderIDString, folderName)
on photosLibraryCreateFolderAtFolder(folderName, folderIDString)
(* Creates folder named folderName inside folder folderIDString
does not check for duplicate folder
Args:
folderIDString: string; id of folder to create folder in
folderName: string; name of folder to create
folderIDString: string; id of folder to create folder in
Returns:
folder id string of newly created folder or missing value if error
Expand Down Expand Up @@ -1255,7 +1255,7 @@ end folderGetPath
on folderGetPathFolderIDScript(folderIDString)
(* Return list of folder ID scripts for the folder and it's parents *)
photosLibraryWaitForPhotos(WAIT_FOR_PHOTOS)
set thePath to {folderIDString}
set thePath to {}
set theParent to folderParent(folderIDString)
repeat while theParent is not missing value
set thePath to {theParent} & thePath
Expand Down
2 changes: 1 addition & 1 deletion photoscript/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ def get_os_version() -> tuple[int, int, int]:
f"Could not parse version string: {platform.mac_ver()} {version}"
)
)
return tuple(int(x) for x in (ver, major, minor))
return int(ver), int(major), int(minor)
1 change: 1 addition & 0 deletions tests/photoscript_config_catalina.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
FOLDER_NAMES_TOP = ["Travel", "Folder1"]

FOLDER_1_UUID = "1FB9BF4B-3CF5-45DE-B4E7-C92047341196/L0/020"
FOLDER_1_IDSTRING = 'folder id("1FB9BF4B-3CF5-45DE-B4E7-C92047341196/L0/020")'
FOLDER_1_UUID_OSXPHOTOS = "1FB9BF4B-3CF5-45DE-B4E7-C92047341196"
FOLDER_1_NAME = "Folder1"
FOLDER_1_LEN = 1
Expand Down
33 changes: 30 additions & 3 deletions tests/test_3_folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
OS_VER = get_os_version()[1]
if OS_VER == "15":
from tests.photoscript_config_catalina import (
FOLDER_1_IDSTRING,
FOLDER_1_LEN,
FOLDER_1_NAME,
FOLDER_1_SUBFOLDERS,
Expand Down Expand Up @@ -66,6 +67,32 @@ def test_folder_init_osxphotos_uuid():
assert folder.uuid == FOLDER_1_UUID_OSXPHOTOS


def test_folder_init_path():
"""Test init with path"""
folder = photoscript.Folder(path=[FOLDER_1_NAME])
assert isinstance(folder, photoscript.Folder)
assert folder.id == FOLDER_1_UUID


def test_folder_init_idstring():
"""Test init with idstring"""
folder = photoscript.Folder(idstring=FOLDER_1_IDSTRING)
assert isinstance(folder, photoscript.Folder)
assert folder.id == FOLDER_1_UUID


def test_folder_init_too_many_1():
"""Test init with too many args"""
with pytest.raises(ValueError):
assert photoscript.Folder(uuid=FOLDER_1_UUID, idstring=FOLDER_1_IDSTRING)


def test_folder_init_too_many_2():
"""Test init with too many args"""
with pytest.raises(ValueError):
assert photoscript.Folder(path=[FOLDER_1_NAME], idstring=FOLDER_1_IDSTRING)


def test_folder_init_bad_uuid():
"""Test init with bad UUID"""
with pytest.raises(ValueError):
Expand Down Expand Up @@ -130,7 +157,7 @@ def test_folder_parent_id(photoslib):
def test_folder_parent_id_not_toplevel(photoslib):
"""Test parent id with non-top-level folder"""
folder = photoslib.folder(FOLDER_2_NAME, top_level=False)
assert folder.parent_id == FOLDER_1_UUID
assert folder.parent_id == FOLDER_1_IDSTRING


def test_folder_parent(photoslib):
Expand All @@ -143,7 +170,7 @@ def test_folder_parent(photoslib):

def test_folder_parent_top_level(photoslib):
"""Test folder parent with top-level folder"""
folder = photoslib.folder(FOLDER_NAMES_TOP[0])
folder = photoslib.folder(name=FOLDER_NAMES_TOP[0])
assert folder.parent is None


Expand All @@ -158,7 +185,7 @@ def test_folder_path_str(photoslib):

def test_folder_path(photoslib):
"""Test folder path"""
folder = photoslib.folder(FOLDER_2_NAME, top_level=False)
folder = photoslib.folder(name=FOLDER_2_NAME, top_level=False)
path = folder.path()
assert len(path) == len(FOLDER_2_PATH)
for i, p in enumerate(path):
Expand Down

0 comments on commit b14e8b8

Please sign in to comment.