Refactoring with sourceryAI

This commit is contained in:
Rhet Turnbull
2020-06-01 21:06:09 -07:00
parent ec727cc556
commit 5c7a0c3a24
13 changed files with 182 additions and 216 deletions

View File

@@ -20,11 +20,7 @@ from osxphotos.__main__ import get_photos_db, _list_libraries
def main(): def main():
db = None db = None
if len(sys.argv) > 1: db = sys.argv[1] if len(sys.argv) > 1 else get_photos_db()
db = sys.argv[1]
else:
db = get_photos_db()
if db: if db:
print("loading database") print("loading database")
tic = time.perf_counter() tic = time.perf_counter()

View File

@@ -534,10 +534,7 @@ def info(ctx, cli_obj, db, json_, photos_library):
return return
pdb = osxphotos.PhotosDB(dbfile=db) pdb = osxphotos.PhotosDB(dbfile=db)
info = {} info = {"database_path": pdb.db_path, "database_version": pdb.db_version}
info["database_path"] = pdb.db_path
info["database_version"] = pdb.db_version
photos = pdb.photos() photos = pdb.photos()
not_shared_photos = [p for p in photos if not p.shared] not_shared_photos = [p for p in photos if not p.shared]
info["photo_count"] = len(not_shared_photos) info["photo_count"] = len(not_shared_photos)
@@ -1175,7 +1172,7 @@ def export(
(export_as_hardlink, exiftool), (export_as_hardlink, exiftool),
(any(place), no_place), (any(place), no_place),
] ]
if any([all(bb) for bb in exclusive]): if any(all(bb) for bb in exclusive):
click.echo("Incompatible export options", err=True) click.echo("Incompatible export options", err=True)
click.echo(cli.commands["export"].get_help(ctx), err=True) click.echo(cli.commands["export"].get_help(ctx), err=True)
return return
@@ -1186,11 +1183,6 @@ def export(
not x for x in [skip_edited, skip_bursts, skip_live, skip_raw] not x for x in [skip_edited, skip_bursts, skip_live, skip_raw]
] ]
# though the command line option is current_name, internally all processing
# logic uses original_name which is the boolean inverse of current_name
# because the original code used --original-name as an option
original_name = not current_name
# verify exiftool installed an in path # verify exiftool installed an in path
if exiftool: if exiftool:
try: try:
@@ -1283,10 +1275,6 @@ def export(
) )
results_exported = [] results_exported = []
results_new = []
results_updated = []
results_skipped = []
results_exif_updated = []
if photos: if photos:
if export_bursts: if export_bursts:
# add the burst_photos to the export set # add the burst_photos to the export set
@@ -1300,7 +1288,50 @@ def export(
photo_str = "photos" if num_photos > 1 else "photo" photo_str = "photos" if num_photos > 1 else "photo"
click.echo(f"Exporting {num_photos} {photo_str} to {dest}...") click.echo(f"Exporting {num_photos} {photo_str} to {dest}...")
start_time = time.perf_counter() start_time = time.perf_counter()
if not verbose_: # though the command line option is current_name, internally all processing
# logic uses original_name which is the boolean inverse of current_name
# because the original code used --original-name as an option
original_name = not current_name
results_new = []
results_updated = []
results_skipped = []
results_exif_updated = []
if verbose_:
for p in photos:
results = export_photo(
photo=p,
dest=dest,
verbose_=verbose_,
export_by_date=export_by_date,
sidecar=sidecar,
update=update,
export_as_hardlink=export_as_hardlink,
overwrite=overwrite,
export_edited=export_edited,
original_name=original_name,
export_live=export_live,
download_missing=download_missing,
exiftool=exiftool,
directory=directory,
filename_template=filename_template,
no_extended_attributes=no_extended_attributes,
export_raw=export_raw,
album_keyword=album_keyword,
person_keyword=person_keyword,
keyword_template=keyword_template,
export_db=export_db,
fileutil=fileutil,
dry_run=dry_run,
edited_suffix=edited_suffix,
)
results_exported.extend(results.exported)
results_new.extend(results.new)
results_updated.extend(results.updated)
results_skipped.extend(results.skipped)
results_exif_updated.extend(results.exif_updated)
else:
# show progress bar # show progress bar
with click.progressbar(photos) as bar: with click.progressbar(photos) as bar:
for p in bar: for p in bar:
@@ -1335,47 +1366,9 @@ def export(
results_updated.extend(results.updated) results_updated.extend(results.updated)
results_skipped.extend(results.skipped) results_skipped.extend(results.skipped)
results_exif_updated.extend(results.exif_updated) results_exif_updated.extend(results.exif_updated)
else:
for p in photos:
results = export_photo(
photo=p,
dest=dest,
verbose_=verbose_,
export_by_date=export_by_date,
sidecar=sidecar,
update=update,
export_as_hardlink=export_as_hardlink,
overwrite=overwrite,
export_edited=export_edited,
original_name=original_name,
export_live=export_live,
download_missing=download_missing,
exiftool=exiftool,
directory=directory,
filename_template=filename_template,
no_extended_attributes=no_extended_attributes,
export_raw=export_raw,
album_keyword=album_keyword,
person_keyword=person_keyword,
keyword_template=keyword_template,
export_db=export_db,
fileutil=fileutil,
dry_run=dry_run,
edited_suffix=edited_suffix,
)
results_exported.extend(results.exported)
results_new.extend(results.new)
results_updated.extend(results.updated)
results_skipped.extend(results.skipped)
results_exif_updated.extend(results.exif_updated)
stop_time = time.perf_counter() stop_time = time.perf_counter()
# print summary results # print summary results
if not update: if update:
photo_str = "photos" if len(results_exported) != 1 else "photo"
click.echo(f"Exported: {len(results_exported)} {photo_str}")
click.echo(f"Elapsed time: {stop_time-start_time} seconds")
else:
photo_str_new = "photos" if len(results_new) != 1 else "photo" photo_str_new = "photos" if len(results_new) != 1 else "photo"
photo_str_updated = "photos" if len(results_new) != 1 else "photo" photo_str_updated = "photos" if len(results_new) != 1 else "photo"
photo_str_skipped = "photos" if len(results_skipped) != 1 else "photo" photo_str_skipped = "photos" if len(results_skipped) != 1 else "photo"
@@ -1388,8 +1381,10 @@ def export(
+ f"skipped: {len(results_skipped)} {photo_str_skipped}, " + f"skipped: {len(results_skipped)} {photo_str_skipped}, "
+ f"updated EXIF data: {len(results_exif_updated)} {photo_str_exif_updated}" + f"updated EXIF data: {len(results_exif_updated)} {photo_str_exif_updated}"
) )
else:
photo_str = "photos" if len(results_exported) != 1 else "photo"
click.echo(f"Exported: {len(results_exported)} {photo_str}")
click.echo(f"Elapsed time: {stop_time-start_time} seconds") click.echo(f"Elapsed time: {stop_time-start_time} seconds")
else: else:
click.echo("Did not find any photos to export") click.echo("Did not find any photos to export")
@@ -1412,8 +1407,8 @@ def help(ctx, topic, **kw):
def print_photo_info(photos, json=False): def print_photo_info(photos, json=False):
if json:
dump = [] dump = []
if json:
for p in photos: for p in photos:
dump.append(p.json()) dump.append(p.json())
click.echo(f"[{', '.join(dump)}]") click.echo(f"[{', '.join(dump)}]")
@@ -1422,7 +1417,6 @@ def print_photo_info(photos, json=False):
csv_writer = csv.writer( csv_writer = csv.writer(
sys.stdout, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL sys.stdout, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
) )
dump = []
# add headers # add headers
dump.append( dump.append(
[ [
@@ -1990,10 +1984,7 @@ def get_filenames_from_template(photo, filename_template, original_name):
) )
filenames = [f"{file_}{photo_ext}" for file_ in filenames] filenames = [f"{file_}{photo_ext}" for file_ in filenames]
else: else:
if original_name: filenames = [photo.original_filename] if original_name else [photo.filename]
filenames = [photo.original_filename]
else:
filenames = [photo.filename]
return filenames return filenames
@@ -2019,13 +2010,18 @@ def get_dirnames_from_template(photo, directory, export_by_date, dest, dry_run):
dest_path = os.path.join( dest_path = os.path.join(
dest, date_created.year, date_created.mm, date_created.dd dest, date_created.year, date_created.mm, date_created.dd
) )
if not dry_run and not os.path.isdir(dest_path): if not (dry_run or os.path.isdir(dest_path)):
os.makedirs(dest_path) os.makedirs(dest_path)
dest_paths = [dest_path] dest_paths = [dest_path]
elif directory: elif directory:
# got a directory template, render it and check results are valid # got a directory template, render it and check results are valid
dirnames, unmatched = photo.render_template(directory) dirnames, unmatched = photo.render_template(directory)
if not dirnames or unmatched: if not dirnames:
raise click.BadOptionUsage(
"directory",
f"Invalid template '{directory}': results={dirnames} unmatched={unmatched}",
)
elif unmatched:
raise click.BadOptionUsage( raise click.BadOptionUsage(
"directory", "directory",
f"Invalid template '{directory}': results={dirnames} unmatched={unmatched}", f"Invalid template '{directory}': results={dirnames} unmatched={unmatched}",
@@ -2036,7 +2032,7 @@ def get_dirnames_from_template(photo, directory, export_by_date, dest, dry_run):
dest_path = os.path.join(dest, dirname) dest_path = os.path.join(dest, dirname)
if not is_valid_filepath(dest_path, platform="auto"): if not is_valid_filepath(dest_path, platform="auto"):
raise ValueError(f"Invalid file path: '{dest_path}'") raise ValueError(f"Invalid file path: '{dest_path}'")
if not dry_run and not os.path.isdir(dest_path): if not (dry_run or os.path.isdir(dest_path)):
os.makedirs(dest_path) os.makedirs(dest_path)
dest_paths.append(dest_path) dest_paths.append(dest_path)
else: else:

View File

@@ -445,10 +445,7 @@ class ExportDB(ExportDB_ABC):
dt = datetime.datetime.utcnow().isoformat() dt = datetime.datetime.utcnow().isoformat()
python_path = sys.executable python_path = sys.executable
cmd = sys.argv[0] cmd = sys.argv[0]
if len(sys.argv) > 1: args = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else ""
args = " ".join(sys.argv[1:])
else:
args = ""
cwd = os.getcwd() cwd = os.getcwd()
conn = self._conn conn = self._conn
try: try:

View File

@@ -1,3 +1,3 @@
""" version info """ """ version info """
__version__ = "0.29.9" __version__ = "0.29.10"

View File

@@ -62,9 +62,6 @@ class AlbumInfo:
try: try:
return self._folder_names return self._folder_names
except AttributeError: except AttributeError:
if self._db._db_version <= _PHOTOS_4_VERSION:
self._folder_names = self._db._album_folder_hierarchy_list(self._uuid)
else:
self._folder_names = self._db._album_folder_hierarchy_list(self._uuid) self._folder_names = self._db._album_folder_hierarchy_list(self._uuid)
return self._folder_names return self._folder_names

View File

@@ -12,53 +12,44 @@ class DateTimeFormatter:
@property @property
def date(self): def date(self):
""" ISO date in form 2020-03-22 """ """ ISO date in form 2020-03-22 """
date = self.dt.date().isoformat() return self.dt.date().isoformat()
return date
@property @property
def year(self): def year(self):
""" 4 digit year """ """ 4 digit year """
year = f"{self.dt.year}" return f"{self.dt.year}"
return year
@property @property
def yy(self): def yy(self):
""" 2 digit year """ """ 2 digit year """
yy = f"{self.dt.strftime('%y')}" return f"{self.dt.strftime('%y')}"
return yy
@property @property
def mm(self): def mm(self):
""" 2 digit month """ """ 2 digit month """
mm = f"{self.dt.strftime('%m')}" return f"{self.dt.strftime('%m')}"
return mm
@property @property
def month(self): def month(self):
""" Month as locale's full name """ """ Month as locale's full name """
month = f"{self.dt.strftime('%B')}" return f"{self.dt.strftime('%B')}"
return month
@property @property
def mon(self): def mon(self):
""" Month as locale's abbreviated name """ """ Month as locale's abbreviated name """
mon = f"{self.dt.strftime('%b')}" return f"{self.dt.strftime('%b')}"
return mon
@property @property
def dd(self): def dd(self):
""" 2-digit day of the month """ """ 2-digit day of the month """
dd = f"{self.dt.strftime('%d')}" return f"{self.dt.strftime('%d')}"
return dd
@property @property
def dow(self): def dow(self):
""" Day of week as locale's name """ """ Day of week as locale's name """
dow = f"{self.dt.strftime('%A')}" return f"{self.dt.strftime('%A')}"
return dow
@property @property
def doy(self): def doy(self):
""" Julian day of year starting from 001 """ """ Julian day of year starting from 001 """
doy = f"{self.dt.strftime('%j')}" return f"{self.dt.strftime('%j')}"
return doy

View File

@@ -59,11 +59,7 @@ class _ExifToolProc:
) )
return return
if exiftool: self._exiftool = exiftool if exiftool else get_exiftool_path()
self._exiftool = exiftool
else:
self._exiftool = get_exiftool_path()
self._process_running = False self._process_running = False
self._start_proc() self._start_proc()
@@ -156,8 +152,7 @@ class ExifTool:
if value is None: if value is None:
value = "" value = ""
command = [] command = [f"-{tag}={value}"]
command.append(f"-{tag}={value}")
if self.overwrite: if self.overwrite:
command.append("-overwrite_original") command.append("-overwrite_original")
self.run_commands(*command) self.run_commands(*command)
@@ -193,7 +188,7 @@ class ExifTool:
no_file: (bool) do not pass the filename to exiftool (default=False) no_file: (bool) do not pass the filename to exiftool (default=False)
by default, all commands will be run against self.file by default, all commands will be run against self.file
use no_file=True to run a command without passing the filename """ use no_file=True to run a command without passing the filename """
if not hasattr(self, "_process") or not self._process: if not (hasattr(self, "_process") and self._process):
raise ValueError("exiftool process is not running") raise ValueError("exiftool process is not running")
if not commands: if not commands:
@@ -245,8 +240,7 @@ class ExifTool:
def json(self): def json(self):
""" returns JSON string containing all EXIF tags and values from exiftool """ """ returns JSON string containing all EXIF tags and values from exiftool """
json_str = self.run_commands("-json") return self.run_commands("-json")
return json_str
def _read_exif(self): def _read_exif(self):
""" read exif data from file """ """ read exif data from file """
@@ -254,5 +248,4 @@ class ExifTool:
self.data = {k: v for k, v in data.items()} self.data = {k: v for k, v in data.items()}
def __str__(self): def __str__(self):
str_ = f"file: {self.file}\nexiftool: {self._exiftoolproc._exiftool}" return f"file: {self.file}\nexiftool: {self._exiftoolproc._exiftool}"
return str_

View File

@@ -11,6 +11,7 @@ from abc import ABC, abstractmethod
class FileUtilABC(ABC): class FileUtilABC(ABC):
""" Abstract base class for FileUtil """ """ Abstract base class for FileUtil """
@classmethod @classmethod
@abstractmethod @abstractmethod
def hardlink(cls, src, dest): def hardlink(cls, src, dest):
@@ -39,6 +40,7 @@ class FileUtilABC(ABC):
class FileUtilMacOS(FileUtilABC): class FileUtilMacOS(FileUtilABC):
""" Various file utilities """ """ Various file utilities """
@classmethod @classmethod
def hardlink(cls, src, dest): def hardlink(cls, src, dest):
""" Hardlinks a file from src path to dest path """ Hardlinks a file from src path to dest path
@@ -119,9 +121,7 @@ class FileUtilMacOS(FileUtilABC):
if s1[0] != stat.S_IFREG or s2[0] != stat.S_IFREG: if s1[0] != stat.S_IFREG or s2[0] != stat.S_IFREG:
return False return False
if s1 == s2: return s1 == s2
return True
return False
@classmethod @classmethod
def file_sig(cls, f1): def file_sig(cls, f1):
@@ -135,14 +135,17 @@ class FileUtilMacOS(FileUtilABC):
class FileUtil(FileUtilMacOS): class FileUtil(FileUtilMacOS):
""" Various file utilities """ """ Various file utilities """
pass pass
class FileUtilNoOp(FileUtil): class FileUtilNoOp(FileUtil):
""" No-Op implementation of FileUtil for testing / dry-run mode """ No-Op implementation of FileUtil for testing / dry-run mode
all methods with exception of cmp_sig and file_cmp are no-op all methods with exception of cmp_sig and file_cmp are no-op
cmp_sig functions as FileUtil.cmp_sig does cmp_sig functions as FileUtil.cmp_sig does
file_cmp returns mock data file_cmp returns mock data
""" """
@staticmethod @staticmethod
def noop(*args): def noop(*args):
pass pass

View File

@@ -1153,7 +1153,7 @@ def _xmp_sidecar(
def _write_sidecar(self, filename, sidecar_str): def _write_sidecar(self, filename, sidecar_str):
""" write sidecar_str to filename """ write sidecar_str to filename
used for exporting sidecar info """ used for exporting sidecar info """
if not filename and not sidecar_str: if not (filename or sidecar_str):
raise ( raise (
ValueError( ValueError(
f"filename {filename} and sidecar_str {sidecar_str} must not be None" f"filename {filename} and sidecar_str {sidecar_str} must not be None"

View File

@@ -294,18 +294,17 @@ class PhotosDB:
@property @property
def keywords_as_dict(self): def keywords_as_dict(self):
""" return keywords as dict of keyword, count in reverse sorted order (descending) """ """ return keywords as dict of keyword, count in reverse sorted order (descending) """
keywords = {} keywords = {
for k in self._dbkeywords_keyword.keys(): k: len(self._dbkeywords_keyword[k]) for k in self._dbkeywords_keyword.keys()
keywords[k] = len(self._dbkeywords_keyword[k]) }
keywords = dict(sorted(keywords.items(), key=lambda kv: kv[1], reverse=True)) keywords = dict(sorted(keywords.items(), key=lambda kv: kv[1], reverse=True))
return keywords return keywords
@property @property
def persons_as_dict(self): def persons_as_dict(self):
""" return persons as dict of person, count in reverse sorted order (descending) """ """ return persons as dict of person, count in reverse sorted order (descending) """
persons = {} persons = {k: len(self._dbfaces_person[k]) for k in self._dbfaces_person.keys()}
for k in self._dbfaces_person.keys():
persons[k] = len(self._dbfaces_person[k])
persons = dict(sorted(persons.items(), key=lambda kv: kv[1], reverse=True)) persons = dict(sorted(persons.items(), key=lambda kv: kv[1], reverse=True))
return persons return persons
@@ -413,13 +412,12 @@ class PhotosDB:
def album_info(self): def album_info(self):
""" return list of AlbumInfo objects for each album in the photos database """ """ return list of AlbumInfo objects for each album in the photos database """
albums = [ return [
AlbumInfo(db=self, uuid=album) AlbumInfo(db=self, uuid=album)
for album in self._dbalbums_album.keys() for album in self._dbalbums_album.keys()
if self._dbalbum_details[album]["cloudownerhashedpersonid"] is None if self._dbalbum_details[album]["cloudownerhashedpersonid"] is None
and not self._dbalbum_details[album]["intrash"] and not self._dbalbum_details[album]["intrash"]
] ]
return albums
@property @property
def album_info_shared(self): def album_info_shared(self):
@@ -433,13 +431,12 @@ class PhotosDB:
) )
return [] return []
albums_shared = [ return [
AlbumInfo(db=self, uuid=album) AlbumInfo(db=self, uuid=album)
for album in self._dbalbums_album.keys() for album in self._dbalbums_album.keys()
if self._dbalbum_details[album]["cloudownerhashedpersonid"] is not None if self._dbalbum_details[album]["cloudownerhashedpersonid"] is not None
and not self._dbalbum_details[album]["intrash"] and not self._dbalbum_details[album]["intrash"]
] ]
return albums_shared
@property @property
def albums(self): def albums(self):

View File

@@ -1,5 +1,6 @@
""" Custom template system for osxphotos (implemented in PhotoInfo.render_template) """ """ Custom template system for osxphotos (implemented in PhotoInfo.render_template) """
# Rolled my own template system because: # Rolled my own template system because:
# 1. Needed to handle multiple values (e.g. album, keyword) # 1. Needed to handle multiple values (e.g. album, keyword)
# 2. Needed to handle default values if template not found # 2. Needed to handle default values if template not found
@@ -8,7 +9,6 @@
# 4. Couldn't figure out how to do #1 and #2 with str.format() # 4. Couldn't figure out how to do #1 and #2 with str.format()
# #
# This code isn't elegant but it seems to work well. PRs gladly accepted. # This code isn't elegant but it seems to work well. PRs gladly accepted.
import locale import locale
import os import os
import re import re
@@ -71,7 +71,7 @@ TEMPLATE_SUBSTITUTIONS_MULTI_VALUED = {
# Just the multi-valued substitution names without the braces # Just the multi-valued substitution names without the braces
MULTI_VALUE_SUBSTITUTIONS = [ MULTI_VALUE_SUBSTITUTIONS = [
field.replace("{", "").replace("}", "") field.replace("{", "").replace("}", "")
for field in TEMPLATE_SUBSTITUTIONS_MULTI_VALUED.keys() for field in TEMPLATE_SUBSTITUTIONS_MULTI_VALUED
] ]
@@ -289,12 +289,16 @@ class PhotoTemplate:
if field == "modified.yy": if field == "modified.yy":
return ( return (
DateTimeFormatter(self.photo.date_modified).yy if self.photo.date_modified else None DateTimeFormatter(self.photo.date_modified).yy
if self.photo.date_modified
else None
) )
if field == "modified.mm": if field == "modified.mm":
return ( return (
DateTimeFormatter(self.photo.date_modified).mm if self.photo.date_modified else None DateTimeFormatter(self.photo.date_modified).mm
if self.photo.date_modified
else None
) )
if field == "modified.month": if field == "modified.month":
@@ -313,7 +317,9 @@ class PhotoTemplate:
if field == "modified.dd": if field == "modified.dd":
return ( return (
DateTimeFormatter(self.photo.date_modified).dd if self.photo.date_modified else None DateTimeFormatter(self.photo.date_modified).dd
if self.photo.date_modified
else None
) )
if field == "modified.doy": if field == "modified.doy":

View File

@@ -87,6 +87,8 @@ class PLRevGeoLocationInfo:
self.postalAddress = postalAddress self.postalAddress = postalAddress
def __eq__(self, other): def __eq__(self, other):
return all(
getattr(self, field) == getattr(other, field)
for field in [ for field in [
"addressString", "addressString",
"countryCode", "countryCode",
@@ -96,10 +98,8 @@ class PLRevGeoLocationInfo:
"version", "version",
"geoServiceProvider", "geoServiceProvider",
"postalAddress", "postalAddress",
]: ]
if getattr(self, field) != getattr(other, field): )
return False
return True
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
@@ -151,21 +151,17 @@ class PLRevGeoMapItem:
self.finalPlaceInfos = finalPlaceInfos self.finalPlaceInfos = finalPlaceInfos
def __eq__(self, other): def __eq__(self, other):
for field in ["sortedPlaceInfos", "finalPlaceInfos"]: return all(
if getattr(self, field) != getattr(other, field): getattr(self, field) == getattr(other, field)
return False for field in ["sortedPlaceInfos", "finalPlaceInfos"]
return True )
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
def __str__(self): def __str__(self):
sortedPlaceInfos = [] sortedPlaceInfos = [str(place) for place in self.sortedPlaceInfos]
finalPlaceInfos = [] finalPlaceInfos = [str(place) for place in self.finalPlaceInfos]
for place in self.sortedPlaceInfos:
sortedPlaceInfos.append(str(place))
for place in self.finalPlaceInfos:
finalPlaceInfos.append(str(place))
return ( return (
f"finalPlaceInfos: {finalPlaceInfos}, sortedPlaceInfos: {sortedPlaceInfos}" f"finalPlaceInfos: {finalPlaceInfos}, sortedPlaceInfos: {sortedPlaceInfos}"
) )
@@ -192,10 +188,10 @@ class PLRevGeoMapItemAdditionalPlaceInfo:
self.dominantOrderType = dominantOrderType self.dominantOrderType = dominantOrderType
def __eq__(self, other): def __eq__(self, other):
for field in ["area", "name", "placeType", "dominantOrderType"]: return all(
if getattr(self, field) != getattr(other, field): getattr(self, field) == getattr(other, field)
return False for field in ["area", "name", "placeType", "dominantOrderType"]
return True )
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
@@ -245,6 +241,8 @@ class CNPostalAddress:
self._subLocality = _subLocality self._subLocality = _subLocality
def __eq__(self, other): def __eq__(self, other):
return all(
getattr(self, field) == getattr(other, field)
for field in [ for field in [
"_ISOCountryCode", "_ISOCountryCode",
"_city", "_city",
@@ -254,10 +252,8 @@ class CNPostalAddress:
"_street", "_street",
"_subAdministrativeArea", "_subAdministrativeArea",
"_subLocality", "_subLocality",
]: ]
if getattr(self, field) != getattr(other, field): )
return False
return True
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
@@ -490,16 +486,14 @@ class PlaceInfo4(PlaceInfo):
"names": self.names, "names": self.names,
"country_code": self.country_code, "country_code": self.country_code,
} }
strval = "PlaceInfo(" + ", ".join([f"{k}='{v}'" for k, v in info.items()]) + ")" return "PlaceInfo(" + ", ".join([f"{k}='{v}'" for k, v in info.items()]) + ")"
return strval
def as_dict(self): def as_dict(self):
info = { return {
"name": self.name, "name": self.name,
"names": self.names._asdict(), "names": self.names._asdict(),
"country_code": self.country_code, "country_code": self.country_code,
} }
return info
class PlaceInfo5(PlaceInfo): class PlaceInfo5(PlaceInfo):
@@ -541,7 +535,7 @@ class PlaceInfo5(PlaceInfo):
@property @property
def address(self): def address(self):
addr = self._plrevgeoloc.postalAddress addr = self._plrevgeoloc.postalAddress
address = PostalAddress( return PostalAddress(
street=addr._street, street=addr._street,
sub_locality=addr._subLocality, sub_locality=addr._subLocality,
city=addr._city, city=addr._city,
@@ -551,7 +545,6 @@ class PlaceInfo5(PlaceInfo):
country=addr._country, country=addr._country,
iso_country_code=addr._ISOCountryCode, iso_country_code=addr._ISOCountryCode,
) )
return address
def _process_place_info(self): def _process_place_info(self):
""" Process sortedPlaceInfos to set self._name and self._names """ """ Process sortedPlaceInfos to set self._name and self._names """
@@ -630,11 +623,10 @@ class PlaceInfo5(PlaceInfo):
"address_str": self.address_str, "address_str": self.address_str,
"address": str(self.address), "address": str(self.address),
} }
strval = "PlaceInfo(" + ", ".join([f"{k}='{v}'" for k, v in info.items()]) + ")" return "PlaceInfo(" + ", ".join([f"{k}='{v}'" for k, v in info.items()]) + ")"
return strval
def as_dict(self): def as_dict(self):
info = { return {
"name": self.name, "name": self.name,
"names": self.names._asdict(), "names": self.names._asdict(),
"country_code": self.country_code, "country_code": self.country_code,
@@ -642,4 +634,3 @@ class PlaceInfo5(PlaceInfo):
"address_str": self.address_str, "address_str": self.address_str,
"address": self.address._asdict(), "address": self.address._asdict(),
} }
return info

View File

@@ -261,10 +261,9 @@ def get_preferred_uti_extension(uti):
# reference: https://developer.apple.com/documentation/coreservices/1442744-uttypecopypreferredtagwithclass?language=objc # reference: https://developer.apple.com/documentation/coreservices/1442744-uttypecopypreferredtagwithclass?language=objc
ext = CoreServices.UTTypeCopyPreferredTagWithClass( return CoreServices.UTTypeCopyPreferredTagWithClass(
uti, CoreServices.kUTTagClassFilenameExtension uti, CoreServices.kUTTagClassFilenameExtension
) )
return ext
def findfiles(pattern, path_): def findfiles(pattern, path_):