Unicode refactor (#1101)
* Began refactoring for improving unicode handling * Added platform and unicode modules * Added tests for unicode utilities * Added tests for unicode utilities * Added tests for unicode utilities * Added tests for unicode utilities * Fixed unicode tests for linux * Fixed unicode tests for linux * Fixed duplicate alubm name with --add-to-album * Fixed test for linux * Fix for duplicate unicode kewyords, see #907, #1085
This commit is contained in:
parent
7ccfe26e37
commit
bb8e164f21
@ -7,7 +7,7 @@ from typing import Callable
|
|||||||
|
|
||||||
from osxphotos import ExportResults, PhotoInfo
|
from osxphotos import ExportResults, PhotoInfo
|
||||||
from osxphotos.exiftool import ExifTool
|
from osxphotos.exiftool import ExifTool
|
||||||
from osxphotos.utils import normalize_unicode
|
from osxphotos.unicode import normalize_unicode
|
||||||
|
|
||||||
# Update this for your custom keyword to rating mapping
|
# Update this for your custom keyword to rating mapping
|
||||||
RATINGS = {
|
RATINGS = {
|
||||||
|
|||||||
@ -21,10 +21,10 @@ from .photosdb._photosdb_process_comments import CommentInfo, LikeInfo
|
|||||||
from .phototables import PhotoTables
|
from .phototables import PhotoTables
|
||||||
from .phototemplate import PhotoTemplate
|
from .phototemplate import PhotoTemplate
|
||||||
from .placeinfo import PlaceInfo
|
from .placeinfo import PlaceInfo
|
||||||
|
from .platform import is_macos
|
||||||
from .queryoptions import QueryOptions
|
from .queryoptions import QueryOptions
|
||||||
from .scoreinfo import ScoreInfo
|
from .scoreinfo import ScoreInfo
|
||||||
from .searchinfo import SearchInfo
|
from .searchinfo import SearchInfo
|
||||||
from .utils import is_macos
|
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from .photosalbum import PhotosAlbum, PhotosAlbumPhotoScript
|
from .photosalbum import PhotosAlbum, PhotosAlbumPhotoScript
|
||||||
|
|||||||
@ -18,9 +18,6 @@ OSXPHOTOS_URL = "https://github.com/RhetTbull/osxphotos"
|
|||||||
# Apple Epoch is Jan 1, 2001
|
# Apple Epoch is Jan 1, 2001
|
||||||
TIME_DELTA = (datetime(2001, 1, 1, 0, 0) - datetime(1970, 1, 1, 0, 0)).total_seconds()
|
TIME_DELTA = (datetime(2001, 1, 1, 0, 0) - datetime(1970, 1, 1, 0, 0)).total_seconds()
|
||||||
|
|
||||||
# Unicode format to use for comparing strings
|
|
||||||
UNICODE_FORMAT = "NFC"
|
|
||||||
|
|
||||||
# which Photos library database versions have been tested
|
# which Photos library database versions have been tested
|
||||||
# Photos 2.0 (10.12.6) == 2622
|
# Photos 2.0 (10.12.6) == 2622
|
||||||
# Photos 3.0 (10.13.6) == 3301
|
# Photos 3.0 (10.13.6) == 3301
|
||||||
|
|||||||
@ -13,7 +13,7 @@ from osxphotos.debug import (
|
|||||||
set_debug,
|
set_debug,
|
||||||
wrap_function,
|
wrap_function,
|
||||||
)
|
)
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
# apply any debug functions
|
# apply any debug functions
|
||||||
# need to do this before importing anything else so that the debug functions
|
# need to do this before importing anything else so that the debug functions
|
||||||
|
|||||||
@ -7,8 +7,9 @@ import datetime
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
from osxphotos.platform import assert_macos
|
||||||
from osxphotos.queryoptions import IncompatibleQueryOptions, query_options_from_kwargs
|
from osxphotos.queryoptions import IncompatibleQueryOptions, query_options_from_kwargs
|
||||||
from osxphotos.utils import assert_macos, pluralize
|
from osxphotos.utils import pluralize
|
||||||
|
|
||||||
from .cli_params import QUERY_OPTIONS, THEME_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
|
from .cli_params import QUERY_OPTIONS, THEME_OPTION, TIMESTAMP_OPTION, VERBOSE_OPTION
|
||||||
from .click_rich_echo import rich_click_echo as echo
|
from .click_rich_echo import rich_click_echo as echo
|
||||||
|
|||||||
@ -12,8 +12,8 @@ import click
|
|||||||
|
|
||||||
import osxphotos
|
import osxphotos
|
||||||
from osxphotos.phototemplate import RenderOptions
|
from osxphotos.phototemplate import RenderOptions
|
||||||
|
from osxphotos.platform import assert_macos
|
||||||
from osxphotos.sqlitekvstore import SQLiteKVStore
|
from osxphotos.sqlitekvstore import SQLiteKVStore
|
||||||
from osxphotos.utils import assert_macos
|
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import click
|
|||||||
|
|
||||||
from osxphotos._constants import PROFILE_SORT_KEYS
|
from osxphotos._constants import PROFILE_SORT_KEYS
|
||||||
from osxphotos._version import __version__
|
from osxphotos._version import __version__
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
from .about import about
|
from .about import about
|
||||||
from .albums import albums
|
from .albums import albums
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from typing import Any, Callable
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from ..utils import is_macos
|
from ..platform import is_macos
|
||||||
from .common import OSXPHOTOS_HIDDEN, print_version
|
from .common import OSXPHOTOS_HIDDEN, print_version
|
||||||
from .param_types import *
|
from .param_types import *
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,8 @@ from xdg import xdg_config_home, xdg_data_home
|
|||||||
import osxphotos
|
import osxphotos
|
||||||
from osxphotos._constants import APP_NAME
|
from osxphotos._constants import APP_NAME
|
||||||
from osxphotos._version import __version__
|
from osxphotos._version import __version__
|
||||||
from osxphotos.utils import get_latest_version, get_macos_version
|
from osxphotos.platform import get_macos_version
|
||||||
|
from osxphotos.utils import get_latest_version
|
||||||
|
|
||||||
# used to show/hide hidden commands
|
# used to show/hide hidden commands
|
||||||
OSXPHOTOS_HIDDEN = not bool(os.getenv("OSXPHOTOS_SHOW_HIDDEN", default=False))
|
OSXPHOTOS_HIDDEN = not bool(os.getenv("OSXPHOTOS_SHOW_HIDDEN", default=False))
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"""Detect dark mode on MacOS >= 10.14 or fake it elsewhere"""
|
"""Detect dark mode on MacOS >= 10.14 or fake it elsewhere"""
|
||||||
|
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|||||||
@ -45,16 +45,11 @@ from osxphotos.path_utils import is_valid_filepath, sanitize_filename, sanitize_
|
|||||||
from osxphotos.photoexporter import ExportOptions, ExportResults, PhotoExporter
|
from osxphotos.photoexporter import ExportOptions, ExportResults, PhotoExporter
|
||||||
from osxphotos.photoinfo import PhotoInfoNone
|
from osxphotos.photoinfo import PhotoInfoNone
|
||||||
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
|
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
|
||||||
|
from osxphotos.platform import get_macos_version, is_macos
|
||||||
from osxphotos.queryoptions import load_uuid_from_file, query_options_from_kwargs
|
from osxphotos.queryoptions import load_uuid_from_file, query_options_from_kwargs
|
||||||
|
from osxphotos.unicode import normalize_fs_path
|
||||||
from osxphotos.uti import get_preferred_uti_extension
|
from osxphotos.uti import get_preferred_uti_extension
|
||||||
from osxphotos.utils import (
|
from osxphotos.utils import format_sec_to_hhmmss, pluralize, under_test
|
||||||
format_sec_to_hhmmss,
|
|
||||||
get_macos_version,
|
|
||||||
is_macos,
|
|
||||||
normalize_fs_path,
|
|
||||||
pluralize,
|
|
||||||
under_test,
|
|
||||||
)
|
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from osxmetadata import (
|
from osxmetadata import (
|
||||||
|
|||||||
@ -20,7 +20,7 @@ from osxphotos.phototemplate import (
|
|||||||
TEMPLATE_SUBSTITUTIONS_PATHLIB,
|
TEMPLATE_SUBSTITUTIONS_PATHLIB,
|
||||||
get_template_help,
|
get_template_help,
|
||||||
)
|
)
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from osxmetadata import MDITEM_ATTRIBUTE_DATA, MDITEM_ATTRIBUTE_SHORT_NAMES
|
from osxmetadata import MDITEM_ATTRIBUTE_DATA, MDITEM_ATTRIBUTE_SHORT_NAMES
|
||||||
|
|||||||
@ -41,8 +41,10 @@ from osxphotos.exiftool import ExifToolCaching, get_exiftool_path
|
|||||||
from osxphotos.photoinfo import PhotoInfoNone
|
from osxphotos.photoinfo import PhotoInfoNone
|
||||||
from osxphotos.photosalbum import PhotosAlbumPhotoScript
|
from osxphotos.photosalbum import PhotosAlbumPhotoScript
|
||||||
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
|
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
|
||||||
|
from osxphotos.platform import assert_macos
|
||||||
from osxphotos.sqlitekvstore import SQLiteKVStore
|
from osxphotos.sqlitekvstore import SQLiteKVStore
|
||||||
from osxphotos.utils import assert_macos, pluralize
|
from osxphotos.unicode import normalize_unicode
|
||||||
|
from osxphotos.utils import pluralize
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
@ -357,11 +359,12 @@ def set_photo_metadata(
|
|||||||
merge_keywords: bool,
|
merge_keywords: bool,
|
||||||
) -> MetaData:
|
) -> MetaData:
|
||||||
"""Set metadata (title, description, keywords) for a Photo object"""
|
"""Set metadata (title, description, keywords) for a Photo object"""
|
||||||
photo.title = metadata.title
|
photo.title = normalize_unicode(metadata.title)
|
||||||
photo.description = metadata.description
|
photo.description = normalize_unicode(metadata.description)
|
||||||
keywords = metadata.keywords.copy()
|
keywords = metadata.keywords.copy()
|
||||||
|
keywords =normalize_unicode(keywords)
|
||||||
if merge_keywords:
|
if merge_keywords:
|
||||||
if old_keywords := photo.keywords:
|
if old_keywords := normalize_unicode(photo.keywords):
|
||||||
keywords.extend(old_keywords)
|
keywords.extend(old_keywords)
|
||||||
keywords = list(set(keywords))
|
keywords = list(set(keywords))
|
||||||
photo.keywords = keywords
|
photo.keywords = keywords
|
||||||
@ -420,7 +423,7 @@ def set_photo_title(
|
|||||||
verbose(
|
verbose(
|
||||||
f"Setting title of photo [filename]{filepath.name}[/] to '{title_text[0]}'"
|
f"Setting title of photo [filename]{filepath.name}[/] to '{title_text[0]}'"
|
||||||
)
|
)
|
||||||
photo.title = title_text[0]
|
photo.title = normalize_unicode(title_text[0])
|
||||||
return title_text[0]
|
return title_text[0]
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
@ -448,7 +451,7 @@ def set_photo_description(
|
|||||||
verbose(
|
verbose(
|
||||||
f"Setting description of photo [filename]{filepath.name}[/] to '{description_text[0]}'"
|
f"Setting description of photo [filename]{filepath.name}[/] to '{description_text[0]}'"
|
||||||
)
|
)
|
||||||
photo.description = description_text[0]
|
photo.description = normalize_unicode(description_text[0])
|
||||||
return description_text[0]
|
return description_text[0]
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
@ -469,8 +472,9 @@ def set_photo_keywords(
|
|||||||
kw = render_photo_template(filepath, relative_filepath, keyword, exiftool_path)
|
kw = render_photo_template(filepath, relative_filepath, keyword, exiftool_path)
|
||||||
keywords.extend(kw)
|
keywords.extend(kw)
|
||||||
if keywords:
|
if keywords:
|
||||||
|
keywords = normalize_unicode(keywords)
|
||||||
if merge:
|
if merge:
|
||||||
if old_keywords := photo.keywords:
|
if old_keywords := normalize_unicode(photo.keywords):
|
||||||
keywords.extend(old_keywords)
|
keywords.extend(old_keywords)
|
||||||
keywords = list(set(keywords))
|
keywords = list(set(keywords))
|
||||||
verbose(f"Setting keywords of photo [filename]{filepath.name}[/] to {keywords}")
|
verbose(f"Setting keywords of photo [filename]{filepath.name}[/] to {keywords}")
|
||||||
|
|||||||
@ -20,8 +20,9 @@ from rich.panel import Panel
|
|||||||
|
|
||||||
from osxphotos import PhotoInfo, PhotosDB
|
from osxphotos import PhotoInfo, PhotosDB
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON, search_category_factory
|
from osxphotos._constants import _UNKNOWN_PERSON, search_category_factory
|
||||||
|
from osxphotos.platform import assert_macos
|
||||||
from osxphotos.rich_utils import add_rich_markup_tag
|
from osxphotos.rich_utils import add_rich_markup_tag
|
||||||
from osxphotos.utils import assert_macos, dd_to_dms_str
|
from osxphotos.utils import dd_to_dms_str
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
|
|||||||
@ -12,8 +12,8 @@ from osxphotos.cli.click_rich_echo import (
|
|||||||
)
|
)
|
||||||
from osxphotos.debug import set_debug
|
from osxphotos.debug import set_debug
|
||||||
from osxphotos.phototemplate import RenderOptions
|
from osxphotos.phototemplate import RenderOptions
|
||||||
|
from osxphotos.platform import assert_macos, is_macos
|
||||||
from osxphotos.queryoptions import query_options_from_kwargs
|
from osxphotos.queryoptions import query_options_from_kwargs
|
||||||
from osxphotos.utils import assert_macos, is_macos
|
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from osxphotos.photosalbum import PhotosAlbum
|
from osxphotos.photosalbum import PhotosAlbum
|
||||||
|
|||||||
@ -20,13 +20,13 @@ from osxphotos._constants import _PHOTOS_4_VERSION
|
|||||||
from osxphotos.cli.click_rich_echo import rich_echo_error as echo_error
|
from osxphotos.cli.click_rich_echo import rich_echo_error as echo_error
|
||||||
from osxphotos.photoinfo import PhotoInfo
|
from osxphotos.photoinfo import PhotoInfo
|
||||||
from osxphotos.photosdb import PhotosDB
|
from osxphotos.photosdb import PhotosDB
|
||||||
|
from osxphotos.platform import assert_macos, is_macos
|
||||||
from osxphotos.pyrepl import embed_repl
|
from osxphotos.pyrepl import embed_repl
|
||||||
from osxphotos.queryoptions import (
|
from osxphotos.queryoptions import (
|
||||||
IncompatibleQueryOptions,
|
IncompatibleQueryOptions,
|
||||||
QueryOptions,
|
QueryOptions,
|
||||||
query_options_from_kwargs,
|
query_options_from_kwargs,
|
||||||
)
|
)
|
||||||
from osxphotos.utils import assert_macos, is_macos
|
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import photoscript
|
import photoscript
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import click
|
|||||||
from osxphotos._constants import UUID_PATTERN
|
from osxphotos._constants import UUID_PATTERN
|
||||||
from osxphotos.export_db_utils import get_uuid_for_filepath
|
from osxphotos.export_db_utils import get_uuid_for_filepath
|
||||||
from osxphotos.photosdb.photosdb_utils import get_photos_library_version
|
from osxphotos.photosdb.photosdb_utils import get_photos_library_version
|
||||||
from osxphotos.utils import assert_macos, get_last_library_path
|
from osxphotos.platform import assert_macos
|
||||||
|
from osxphotos.utils import get_last_library_path
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
|
|||||||
@ -15,13 +15,14 @@ from osxphotos.photoinfo import PhotoInfoNone
|
|||||||
from osxphotos.photosalbum import PhotosAlbum
|
from osxphotos.photosalbum import PhotosAlbum
|
||||||
from osxphotos.photosdb.photosdb_utils import get_db_version
|
from osxphotos.photosdb.photosdb_utils import get_db_version
|
||||||
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
|
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
|
||||||
|
from osxphotos.platform import assert_macos
|
||||||
from osxphotos.queryoptions import (
|
from osxphotos.queryoptions import (
|
||||||
IncompatibleQueryOptions,
|
IncompatibleQueryOptions,
|
||||||
QueryOptions,
|
QueryOptions,
|
||||||
query_options_from_kwargs,
|
query_options_from_kwargs,
|
||||||
)
|
)
|
||||||
from osxphotos.sqlitekvstore import SQLiteKVStore
|
from osxphotos.sqlitekvstore import SQLiteKVStore
|
||||||
from osxphotos.utils import assert_macos, pluralize
|
from osxphotos.utils import pluralize
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,8 @@ from osxphotos.photodates import (
|
|||||||
update_photo_time_for_new_timezone,
|
update_photo_time_for_new_timezone,
|
||||||
)
|
)
|
||||||
from osxphotos.phototz import PhotoTimeZone, PhotoTimeZoneUpdater
|
from osxphotos.phototz import PhotoTimeZone, PhotoTimeZoneUpdater
|
||||||
from osxphotos.utils import assert_macos, noop, pluralize
|
from osxphotos.platform import assert_macos
|
||||||
|
from osxphotos.utils import noop, pluralize
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from osxphotos.utils import assert_macos
|
from osxphotos.platform import assert_macos
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,8 @@ from osxphotos import PhotosDB
|
|||||||
from osxphotos.exiftool import ExifTool
|
from osxphotos.exiftool import ExifTool
|
||||||
|
|
||||||
from .datetime_utils import datetime_naive_to_local, datetime_to_new_tz
|
from .datetime_utils import datetime_naive_to_local, datetime_to_new_tz
|
||||||
from .utils import assert_macos, noop
|
from .platform import assert_macos
|
||||||
|
from .utils import noop
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import osxphotos
|
|||||||
from ._constants import OSXPHOTOS_EXPORT_DB, SQLITE_CHECK_SAME_THREAD
|
from ._constants import OSXPHOTOS_EXPORT_DB, SQLITE_CHECK_SAME_THREAD
|
||||||
from ._version import __version__
|
from ._version import __version__
|
||||||
from .fileutil import FileUtil
|
from .fileutil import FileUtil
|
||||||
from .utils import normalize_fs_path
|
from .unicode import normalize_fs_path
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"ExportDB",
|
"ExportDB",
|
||||||
|
|||||||
@ -10,7 +10,8 @@ from abc import ABC, abstractmethod
|
|||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
from .imageconverter import ImageConverter
|
from .imageconverter import ImageConverter
|
||||||
from .utils import is_macos, normalize_fs_path
|
from .platform import is_macos
|
||||||
|
from .unicode import normalize_fs_path
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import sys
|
|||||||
# needed to capture system-level stderr
|
# needed to capture system-level stderr
|
||||||
from wurlitzer import pipes
|
from wurlitzer import pipes
|
||||||
|
|
||||||
from .utils import is_macos
|
from .platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import Metal
|
import Metal
|
||||||
|
|||||||
@ -13,7 +13,7 @@ filename is needed.
|
|||||||
|
|
||||||
import pathvalidate
|
import pathvalidate
|
||||||
|
|
||||||
from osxphotos.utils import normalize_unicode
|
from osxphotos.unicode import normalize_unicode
|
||||||
|
|
||||||
from ._constants import MAX_DIRNAME_LEN, MAX_FILENAME_LEN
|
from ._constants import MAX_DIRNAME_LEN, MAX_FILENAME_LEN
|
||||||
|
|
||||||
|
|||||||
@ -36,17 +36,17 @@ from .exiftool import ExifTool, ExifToolCaching, exiftool_can_write, get_exiftoo
|
|||||||
from .export_db import ExportDB, ExportDBTemp
|
from .export_db import ExportDB, ExportDBTemp
|
||||||
from .fileutil import FileUtil
|
from .fileutil import FileUtil
|
||||||
from .phototemplate import RenderOptions
|
from .phototemplate import RenderOptions
|
||||||
|
from .platform import is_macos
|
||||||
from .rich_utils import add_rich_markup_tag
|
from .rich_utils import add_rich_markup_tag
|
||||||
|
from .unicode import normalize_fs_path
|
||||||
from .uti import get_preferred_uti_extension
|
from .uti import get_preferred_uti_extension
|
||||||
from .utils import (
|
from .utils import (
|
||||||
hexdigest,
|
hexdigest,
|
||||||
increment_filename,
|
increment_filename,
|
||||||
increment_filename_with_count,
|
increment_filename_with_count,
|
||||||
is_macos,
|
|
||||||
lineno,
|
lineno,
|
||||||
list_directory,
|
list_directory,
|
||||||
lock_filename,
|
lock_filename,
|
||||||
normalize_fs_path,
|
|
||||||
unlock_filename,
|
unlock_filename,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -61,11 +61,12 @@ from .photoexporter import ExportOptions, PhotoExporter
|
|||||||
from .phototables import PhotoTables
|
from .phototables import PhotoTables
|
||||||
from .phototemplate import PhotoTemplate, RenderOptions
|
from .phototemplate import PhotoTemplate, RenderOptions
|
||||||
from .placeinfo import PlaceInfo4, PlaceInfo5
|
from .placeinfo import PlaceInfo4, PlaceInfo5
|
||||||
|
from .platform import assert_macos, is_macos
|
||||||
from .query_builder import get_query
|
from .query_builder import get_query
|
||||||
from .scoreinfo import ScoreInfo
|
from .scoreinfo import ScoreInfo
|
||||||
from .searchinfo import SearchInfo
|
from .searchinfo import SearchInfo
|
||||||
from .uti import get_preferred_uti_extension, get_uti_for_extension
|
from .uti import get_preferred_uti_extension, get_uti_for_extension
|
||||||
from .utils import _get_resource_loc, assert_macos, hexdigest, is_macos, list_directory
|
from .utils import _get_resource_loc, hexdigest, list_directory
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from osxmetadata import OSXMetaData
|
from osxmetadata import OSXMetaData
|
||||||
|
|||||||
@ -23,6 +23,8 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from .platform import get_macos_version
|
||||||
|
|
||||||
assert sys.platform == "darwin"
|
assert sys.platform == "darwin"
|
||||||
|
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
@ -37,7 +39,7 @@ from wurlitzer import pipes
|
|||||||
|
|
||||||
from .fileutil import FileUtil
|
from .fileutil import FileUtil
|
||||||
from .uti import get_preferred_uti_extension
|
from .uti import get_preferred_uti_extension
|
||||||
from .utils import get_macos_version, increment_filename
|
from .utils import increment_filename
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"NSURL_to_path",
|
"NSURL_to_path",
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
""" PhotosAlbum class to create an album in default Photos library and add photos to it """
|
""" PhotosAlbum class to create an album in default Photos library and add photos to it """
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import unicodedata
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from more_itertools import chunked
|
from more_itertools import chunked
|
||||||
|
|
||||||
from .photoinfo import PhotoInfo
|
from .photoinfo import PhotoInfo
|
||||||
from .utils import assert_macos, noop, pluralize
|
from .platform import assert_macos
|
||||||
|
from .utils import noop, pluralize
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
@ -15,19 +19,36 @@ from photoscript import Album, Folder, Photo, PhotosLibrary
|
|||||||
__all__ = ["PhotosAlbum", "PhotosAlbumPhotoScript"]
|
__all__ = ["PhotosAlbum", "PhotosAlbumPhotoScript"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_unicode_variants(s: str) -> list[str]:
|
||||||
|
"""Get all unicode variants of string"""
|
||||||
|
variants = []
|
||||||
|
for form in ["NFC", "NFD", "NFKC", "NFKD"]:
|
||||||
|
normalized = unicodedata.normalize(form, s)
|
||||||
|
variants.append(normalized)
|
||||||
|
return variants
|
||||||
|
|
||||||
|
|
||||||
def folder_by_path(folders: List[str], verbose: Optional[callable] = None) -> Folder:
|
def folder_by_path(folders: List[str], verbose: Optional[callable] = None) -> Folder:
|
||||||
"""Get (and create if necessary) a Photos Folder by path (passed as list of folder names)"""
|
"""Get (and create if necessary) a Photos Folder by path (passed as list of folder names)"""
|
||||||
library = PhotosLibrary()
|
library = PhotosLibrary()
|
||||||
verbose = verbose or noop
|
verbose = verbose or noop
|
||||||
top_folder_name = folders.pop(0)
|
top_folder_name = folders.pop(0)
|
||||||
top_folder = library.folder(top_folder_name, top_level=True)
|
|
||||||
if not top_folder:
|
for folder_variant in get_unicode_variants(top_folder_name):
|
||||||
|
top_folder = library.folder(folder_variant, top_level=True)
|
||||||
|
if top_folder is not None:
|
||||||
|
break
|
||||||
|
else:
|
||||||
verbose(f"Creating folder '{top_folder_name}'")
|
verbose(f"Creating folder '{top_folder_name}'")
|
||||||
top_folder = library.create_folder(top_folder_name)
|
top_folder = library.create_folder(top_folder_name)
|
||||||
|
|
||||||
current_folder = top_folder
|
current_folder = top_folder
|
||||||
for folder_name in folders:
|
for folder_name in folders:
|
||||||
folder = current_folder.folder(folder_name)
|
for folder_variant in get_unicode_variants(folder_name):
|
||||||
if not folder:
|
folder = current_folder.folder(folder_variant)
|
||||||
|
if folder is not None:
|
||||||
|
break
|
||||||
|
else:
|
||||||
verbose(f"Creating folder '{folder_name}'")
|
verbose(f"Creating folder '{folder_name}'")
|
||||||
folder = current_folder.create_folder(folder_name)
|
folder = current_folder.create_folder(folder_name)
|
||||||
current_folder = folder
|
current_folder = folder
|
||||||
@ -44,15 +65,24 @@ def album_by_path(
|
|||||||
# have folders
|
# have folders
|
||||||
album_name = folders_album.pop()
|
album_name = folders_album.pop()
|
||||||
folder = folder_by_path(folders_album, verbose)
|
folder = folder_by_path(folders_album, verbose)
|
||||||
album = folder.album(album_name)
|
for album_variant in get_unicode_variants(album_name):
|
||||||
if album is None:
|
# Get album if it exists
|
||||||
|
# need to check every unicode variant to avoid creating duplicate albums with same visual representation (#1085)
|
||||||
|
album = folder.album(album_variant)
|
||||||
|
if album is not None:
|
||||||
|
break
|
||||||
|
else:
|
||||||
verbose(f"Creating album '{album_name}'")
|
verbose(f"Creating album '{album_name}'")
|
||||||
album = folder.create_album(album_name)
|
album = folder.create_album(album_name)
|
||||||
else:
|
else:
|
||||||
# only have album name
|
# only have album name
|
||||||
album_name = folders_album[0]
|
album_name = folders_album[0]
|
||||||
album = library.album(album_name, top_level=True)
|
for album_variant in get_unicode_variants(album_name):
|
||||||
if album is None:
|
album = library.album(album_variant, top_level=True)
|
||||||
|
if album is not None:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# album doesn't exist, create it
|
||||||
verbose(f"Creating album '{album_name}'")
|
verbose(f"Creating album '{album_name}'")
|
||||||
album = library.create_album(album_name)
|
album = library.create_album(album_name)
|
||||||
|
|
||||||
@ -101,15 +131,10 @@ class PhotosAlbum:
|
|||||||
try:
|
try:
|
||||||
photos.append(photoscript.Photo(p.uuid))
|
photos.append(photoscript.Photo(p.uuid))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(
|
|
||||||
f"Error creating Photo object for photo {self._format_uuid(p.uuid)}: {e}"
|
|
||||||
)
|
|
||||||
self.verbose(
|
self.verbose(
|
||||||
f"Error creating Photo object for photo {self._format_uuid(p.uuid)}: {e}"
|
f"Error creating Photo object for photo {self._format_uuid(p.uuid)}: {e}"
|
||||||
)
|
)
|
||||||
print(f"photos: {photos}")
|
|
||||||
for photolist in chunked(photos, 10):
|
for photolist in chunked(photos, 10):
|
||||||
print(f"photolist: {photolist}")
|
|
||||||
self.album.add(photolist)
|
self.album.add(photolist)
|
||||||
photo_len = len(photo_list)
|
photo_len = len(photo_list)
|
||||||
self.verbose(
|
self.verbose(
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from .utils import assert_macos
|
from .platform import assert_macos
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION, TIME_DELTA
|
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION, TIME_DELTA
|
||||||
from ..sqlite_utils import sqlite_open_ro
|
from ..sqlite_utils import sqlite_open_ro
|
||||||
from ..utils import normalize_unicode
|
from ..unicode import normalize_unicode
|
||||||
|
|
||||||
|
|
||||||
def _process_comments(self):
|
def _process_comments(self):
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION
|
from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION
|
||||||
from ..sqlite_utils import sqlite_open_ro
|
from ..sqlite_utils import sqlite_open_ro
|
||||||
from ..utils import normalize_unicode
|
from ..unicode import normalize_unicode
|
||||||
from .photosdb_utils import get_db_version
|
from .photosdb_utils import get_db_version
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from functools import lru_cache
|
|||||||
|
|
||||||
from .._constants import _PHOTOS_4_VERSION, search_category_factory
|
from .._constants import _PHOTOS_4_VERSION, search_category_factory
|
||||||
from ..sqlite_utils import sqlite_db_is_locked, sqlite_open_ro
|
from ..sqlite_utils import sqlite_db_is_locked, sqlite_open_ro
|
||||||
from ..utils import normalize_unicode
|
from ..unicode import normalize_unicode
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This module should be imported in the class defintion of PhotosDB in photosdb.py
|
This module should be imported in the class defintion of PhotosDB in photosdb.py
|
||||||
|
|||||||
@ -56,17 +56,12 @@ from ..fileutil import FileUtil
|
|||||||
from ..personinfo import PersonInfo
|
from ..personinfo import PersonInfo
|
||||||
from ..photoinfo import PhotoInfo
|
from ..photoinfo import PhotoInfo
|
||||||
from ..phototemplate import RenderOptions
|
from ..phototemplate import RenderOptions
|
||||||
|
from ..platform import get_macos_version, is_macos
|
||||||
from ..queryoptions import QueryOptions
|
from ..queryoptions import QueryOptions
|
||||||
from ..rich_utils import add_rich_markup_tag
|
from ..rich_utils import add_rich_markup_tag
|
||||||
from ..sqlite_utils import sqlite_db_is_locked, sqlite_open_ro
|
from ..sqlite_utils import sqlite_db_is_locked, sqlite_open_ro
|
||||||
from ..utils import (
|
from ..unicode import normalize_unicode
|
||||||
_check_file_exists,
|
from ..utils import _check_file_exists, get_last_library_path, noop
|
||||||
get_last_library_path,
|
|
||||||
get_macos_version,
|
|
||||||
is_macos,
|
|
||||||
noop,
|
|
||||||
normalize_unicode,
|
|
||||||
)
|
|
||||||
from .photosdb_utils import get_db_model_version, get_db_version
|
from .photosdb_utils import get_db_model_version, get_db_version
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
|
|||||||
@ -11,8 +11,7 @@ from collections import namedtuple # pylint: disable=syntax-error
|
|||||||
import yaml
|
import yaml
|
||||||
from bpylist2 import archiver
|
from bpylist2 import archiver
|
||||||
|
|
||||||
from ._constants import UNICODE_FORMAT
|
from .unicode import normalize_unicode
|
||||||
from .utils import normalize_unicode
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"PLRevGeoLocationInfo",
|
"PLRevGeoLocationInfo",
|
||||||
|
|||||||
29
osxphotos/platform.py
Normal file
29
osxphotos/platform.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
"""Functions for multi-platform support"""
|
||||||
|
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
|
||||||
|
is_macos = sys.platform == "darwin"
|
||||||
|
|
||||||
|
|
||||||
|
def assert_macos():
|
||||||
|
assert is_macos, "This feature only runs on macOS"
|
||||||
|
|
||||||
|
|
||||||
|
def get_macos_version():
|
||||||
|
assert_macos()
|
||||||
|
# returns tuple of str containing OS version
|
||||||
|
# e.g. 10.13.6 = ("10", "13", "6")
|
||||||
|
version = platform.mac_ver()[0].split(".")
|
||||||
|
if len(version) == 2:
|
||||||
|
(ver, major) = version
|
||||||
|
minor = "0"
|
||||||
|
elif len(version) == 3:
|
||||||
|
(ver, major, minor) = version
|
||||||
|
else:
|
||||||
|
raise (
|
||||||
|
ValueError(
|
||||||
|
f"Could not parse version string: {platform.mac_ver()} {version}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return (ver, major, minor)
|
||||||
@ -4,7 +4,7 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from .utils import assert_macos, get_macos_version
|
from .platform import assert_macos, get_macos_version
|
||||||
|
|
||||||
assert_macos()
|
assert_macos()
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from .utils import is_macos
|
from .platform import is_macos
|
||||||
|
|
||||||
|
|
||||||
def format_offset_time(offset: int) -> str:
|
def format_offset_time(offset: int) -> str:
|
||||||
|
|||||||
90
osxphotos/unicode.py
Normal file
90
osxphotos/unicode.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
"""Utilities for working with unicode strings."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
import unicodedata
|
||||||
|
from typing import Literal, TypeVar, Union
|
||||||
|
|
||||||
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
|
# Unicode format to use for comparing strings
|
||||||
|
DEFAULT_UNICODE_FORM = "NFC"
|
||||||
|
|
||||||
|
# global unicode format
|
||||||
|
_GLOBAL_UNICODE_FORM = DEFAULT_UNICODE_FORM
|
||||||
|
|
||||||
|
# global unicode format to use for filesystem paths
|
||||||
|
_GLOBAL_UNICODE_FS_FORM = "NFD" if is_macos else "NFC"
|
||||||
|
|
||||||
|
PathType = TypeVar("PathType", bound=Union[str, pathlib.Path])
|
||||||
|
|
||||||
|
UnicodeDataType = TypeVar(
|
||||||
|
"UnicodeDataType", bound=Union[str, list[str], tuple[str, ...], None]
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"get_unicode_form",
|
||||||
|
"set_unicode_form",
|
||||||
|
"get_unicode_fs_form",
|
||||||
|
"set_unicode_fs_form",
|
||||||
|
"normalize_fs_path",
|
||||||
|
"normalize_unicode",
|
||||||
|
"DEFAULT_UNICODE_FORM",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_unicode_form() -> Literal["NFC", "NFKC", "NFD", "NFKD"]:
|
||||||
|
"""Return the global unicode format"""
|
||||||
|
global _GLOBAL_UNICODE_FORM
|
||||||
|
return _GLOBAL_UNICODE_FORM
|
||||||
|
|
||||||
|
|
||||||
|
def set_unicode_form(format: Literal["NFC", "NFKC", "NFD", "NFKD"]) -> None:
|
||||||
|
"""Set the global unicode format"""
|
||||||
|
|
||||||
|
if format not in ["NFC", "NFKC", "NFD", "NFKD"]:
|
||||||
|
raise ValueError(f"Invalid unicode format: {format}")
|
||||||
|
|
||||||
|
global _GLOBAL_UNICODE_FORM
|
||||||
|
_GLOBAL_UNICODE_FORM = format
|
||||||
|
|
||||||
|
|
||||||
|
def get_unicode_fs_form() -> Literal["NFC", "NFKC", "NFD", "NFKD"]:
|
||||||
|
"""Return the global unicode filesystem format"""
|
||||||
|
global _GLOBAL_UNICODE_FS_FORM
|
||||||
|
return _GLOBAL_UNICODE_FS_FORM
|
||||||
|
|
||||||
|
|
||||||
|
def set_unicode_fs_form(format: str) -> Literal["NFC", "NFKC", "NFD", "NFKD"]:
|
||||||
|
"""Set the global unicode filesystem format"""
|
||||||
|
|
||||||
|
if format not in ["NFC", "NFKC", "NFD", "NFKD"]:
|
||||||
|
raise ValueError(f"Invalid unicode format: {format}")
|
||||||
|
|
||||||
|
global _GLOBAL_UNICODE_FS_FORM
|
||||||
|
_GLOBAL_UNICODE_FS_FORM = format
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_fs_path(path: PathType) -> PathType:
|
||||||
|
"""Normalize filesystem paths with unicode in them"""
|
||||||
|
form = get_unicode_fs_form()
|
||||||
|
if isinstance(path, pathlib.Path):
|
||||||
|
return pathlib.Path(unicodedata.normalize(form, str(path)))
|
||||||
|
else:
|
||||||
|
return unicodedata.normalize(form, path)
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_unicode(value: UnicodeDataType) -> UnicodeDataType:
|
||||||
|
"""normalize unicode data"""
|
||||||
|
form = get_unicode_form()
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
if isinstance(value, tuple):
|
||||||
|
return tuple(unicodedata.normalize(form, v) for v in value)
|
||||||
|
elif isinstance(value, list):
|
||||||
|
return [unicodedata.normalize(form, v) for v in value]
|
||||||
|
elif isinstance(value, str):
|
||||||
|
return unicodedata.normalize(form, value)
|
||||||
|
else:
|
||||||
|
return value
|
||||||
@ -24,7 +24,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from .utils import assert_macos, get_macos_version, is_macos
|
from .platform import assert_macos, get_macos_version, is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import CoreServices
|
import CoreServices
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
""" Utility functions used in osxphotos """
|
""" Utility functions used in osxphotos """
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import hashlib
|
import hashlib
|
||||||
@ -9,30 +11,29 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import pathlib
|
import pathlib
|
||||||
import platform
|
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import unicodedata
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from plistlib import load as plistload
|
from plistlib import load as plistload
|
||||||
from typing import Any, Callable, List, Optional, Tuple, TypeVar, Union
|
from typing import Callable, List, Optional, Tuple, TypeVar, Union
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import shortuuid
|
import shortuuid
|
||||||
|
|
||||||
from ._constants import UNICODE_FORMAT
|
from osxphotos.platform import get_macos_version, is_macos
|
||||||
|
from osxphotos.unicode import normalize_fs_path
|
||||||
|
|
||||||
|
T = TypeVar("T", bound=Union[str, pathlib.Path])
|
||||||
|
|
||||||
logger = logging.getLogger("osxphotos")
|
logger = logging.getLogger("osxphotos")
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"is_macos",
|
|
||||||
"assert_macos",
|
|
||||||
"dd_to_dms_str",
|
"dd_to_dms_str",
|
||||||
"expand_and_validate_filepath",
|
"expand_and_validate_filepath",
|
||||||
"get_last_library_path",
|
"get_last_library_path",
|
||||||
"get_macos_version",
|
|
||||||
"get_system_library_path",
|
"get_system_library_path",
|
||||||
"hexdigest",
|
"hexdigest",
|
||||||
"increment_filename_with_count",
|
"increment_filename_with_count",
|
||||||
@ -43,8 +44,6 @@ __all__ = [
|
|||||||
"load_function",
|
"load_function",
|
||||||
"lock_filename",
|
"lock_filename",
|
||||||
"noop",
|
"noop",
|
||||||
"normalize_fs_path",
|
|
||||||
"normalize_unicode",
|
|
||||||
"pluralize",
|
"pluralize",
|
||||||
"shortuuid_to_uuid",
|
"shortuuid_to_uuid",
|
||||||
"uuid_to_shortuuid",
|
"uuid_to_shortuuid",
|
||||||
@ -54,13 +53,6 @@ __all__ = [
|
|||||||
VERSION_INFO_URL = "https://pypi.org/pypi/osxphotos/json"
|
VERSION_INFO_URL = "https://pypi.org/pypi/osxphotos/json"
|
||||||
|
|
||||||
|
|
||||||
is_macos = sys.platform == "darwin"
|
|
||||||
|
|
||||||
|
|
||||||
def assert_macos():
|
|
||||||
assert is_macos, "This feature only runs on macOS"
|
|
||||||
|
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import CoreFoundation
|
import CoreFoundation
|
||||||
|
|
||||||
@ -78,25 +70,6 @@ def lineno(filename):
|
|||||||
return f"{filename}: {line}"
|
return f"{filename}: {line}"
|
||||||
|
|
||||||
|
|
||||||
def get_macos_version():
|
|
||||||
assert_macos()
|
|
||||||
# returns tuple of str containing OS version
|
|
||||||
# e.g. 10.13.6 = ("10", "13", "6")
|
|
||||||
version = platform.mac_ver()[0].split(".")
|
|
||||||
if len(version) == 2:
|
|
||||||
(ver, major) = version
|
|
||||||
minor = "0"
|
|
||||||
elif len(version) == 3:
|
|
||||||
(ver, major, minor) = version
|
|
||||||
else:
|
|
||||||
raise (
|
|
||||||
ValueError(
|
|
||||||
f"Could not parse version string: {platform.mac_ver()} {version}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return (ver, major, minor)
|
|
||||||
|
|
||||||
|
|
||||||
def _check_file_exists(filename):
|
def _check_file_exists(filename):
|
||||||
"""returns true if file exists and is not a directory
|
"""returns true if file exists and is not a directory
|
||||||
otherwise returns false"""
|
otherwise returns false"""
|
||||||
@ -280,16 +253,6 @@ def list_photo_libraries():
|
|||||||
return lib_list
|
return lib_list
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T", bound=Union[str, pathlib.Path])
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_fs_path(path: T) -> T:
|
|
||||||
"""Normalize filesystem paths with unicode in them"""
|
|
||||||
form = "NFD" if is_macos else "NFC"
|
|
||||||
if isinstance(path, pathlib.Path):
|
|
||||||
return pathlib.Path(unicodedata.normalize(form, str(path)))
|
|
||||||
else:
|
|
||||||
return unicodedata.normalize(form, path)
|
|
||||||
|
|
||||||
|
|
||||||
# def findfiles(pattern, path):
|
# def findfiles(pattern, path):
|
||||||
@ -378,18 +341,6 @@ def list_directory(
|
|||||||
return files
|
return files
|
||||||
|
|
||||||
|
|
||||||
def normalize_unicode(value) -> Any:
|
|
||||||
"""normalize unicode data"""
|
|
||||||
if value is None:
|
|
||||||
return None
|
|
||||||
if isinstance(value, (tuple, list)):
|
|
||||||
return tuple(unicodedata.normalize(UNICODE_FORMAT, v) for v in value)
|
|
||||||
elif isinstance(value, str):
|
|
||||||
return unicodedata.normalize(UNICODE_FORMAT, value)
|
|
||||||
else:
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def increment_filename_with_count(
|
def increment_filename_with_count(
|
||||||
filepath: Union[str, pathlib.Path], count: int = 0, lock: bool = False
|
filepath: Union[str, pathlib.Path], count: int = 0, lock: bool = False
|
||||||
) -> Tuple[str, int]:
|
) -> Tuple[str, int]:
|
||||||
|
|||||||
@ -23,7 +23,7 @@ One test for locale does not run on GitHub's automated workflow and will look fo
|
|||||||
|
|
||||||
A couple of tests require interaction with Photos and configuring a specific test library. Currently these run only on Catalina. The tests must be specified by using a pytest flag. Only one of these interactive tests can be run at a time. The current flags are:
|
A couple of tests require interaction with Photos and configuring a specific test library. Currently these run only on Catalina. The tests must be specified by using a pytest flag. Only one of these interactive tests can be run at a time. The current flags are:
|
||||||
|
|
||||||
--addalbum: test --add-to-album options
|
--addalbum: test --add-to-album options (pytest -vv tests/test_photosalbum_unicode.py tests/test_cli_add_to_album.py --addalbum)
|
||||||
--timewarp: test `osxphotos timewarp`
|
--timewarp: test `osxphotos timewarp`
|
||||||
--test-import: test `osxphotos import`
|
--test-import: test `osxphotos import`
|
||||||
--test-sync: test `osxphotos sync`
|
--test-sync: test `osxphotos sync`
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import time
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import photoscript
|
import photoscript
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 3.2 MiB |
@ -15,7 +15,7 @@ import pytest
|
|||||||
import osxphotos
|
import osxphotos
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
from osxphotos._constants import _UNKNOWN_PERSON
|
||||||
from osxphotos.photoexporter import PhotoExporter
|
from osxphotos.photoexporter import PhotoExporter
|
||||||
from osxphotos.utils import get_macos_version, is_macos
|
from osxphotos.platform import get_macos_version, is_macos
|
||||||
|
|
||||||
OS_VERSION = get_macos_version() if is_macos else (None, None, None)
|
OS_VERSION = get_macos_version() if is_macos else (None, None, None)
|
||||||
SKIP_TEST = "OSXPHOTOS_TEST_EXPORT" not in os.environ or OS_VERSION[1] != "15"
|
SKIP_TEST = "OSXPHOTOS_TEST_EXPORT" not in os.environ or OS_VERSION[1] != "15"
|
||||||
|
|||||||
@ -36,7 +36,9 @@ from osxphotos.cli import (
|
|||||||
)
|
)
|
||||||
from osxphotos.exiftool import ExifTool, get_exiftool_path
|
from osxphotos.exiftool import ExifTool, get_exiftool_path
|
||||||
from osxphotos.fileutil import FileUtil
|
from osxphotos.fileutil import FileUtil
|
||||||
from osxphotos.utils import is_macos, noop, normalize_fs_path, normalize_unicode
|
from osxphotos.platform import is_macos
|
||||||
|
from osxphotos.unicode import normalize_fs_path, normalize_unicode
|
||||||
|
from osxphotos.utils import noop
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from osxmetadata import OSXMetaData, Tag
|
from osxmetadata import OSXMetaData, Tag
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import photoscript
|
import photoscript
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import os
|
|||||||
import pytest
|
import pytest
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import photoscript
|
import photoscript
|
||||||
|
|||||||
@ -44,7 +44,7 @@ from osxphotos.cli import (
|
|||||||
tutorial,
|
tutorial,
|
||||||
version,
|
version,
|
||||||
)
|
)
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from osxphotos.cli import uuid
|
from osxphotos.cli import uuid
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import pytest
|
|||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
|
||||||
import osxphotos
|
import osxphotos
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import photoscript
|
import photoscript
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import time
|
import time
|
||||||
|
import unicodedata
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
@ -21,7 +22,7 @@ from osxphotos import PhotosDB, QueryOptions
|
|||||||
from osxphotos._constants import UUID_PATTERN
|
from osxphotos._constants import UUID_PATTERN
|
||||||
from osxphotos.datetime_utils import datetime_remove_tz
|
from osxphotos.datetime_utils import datetime_remove_tz
|
||||||
from osxphotos.exiftool import get_exiftool_path
|
from osxphotos.exiftool import get_exiftool_path
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
from tests.conftest import get_os_version
|
from tests.conftest import get_os_version
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
@ -44,17 +45,17 @@ TEST_DATA = {
|
|||||||
TEST_IMAGE_1: {
|
TEST_IMAGE_1: {
|
||||||
"title": "Waves crashing on rocks",
|
"title": "Waves crashing on rocks",
|
||||||
"description": "Used for testing osxphotos",
|
"description": "Used for testing osxphotos",
|
||||||
"keywords": ["osxphotos"],
|
"keywords": ["osxphotos", "Sümmer"],
|
||||||
"lat": 33.7150638888889,
|
"lat": 33.7150638888889,
|
||||||
"lon": -118.319672222222,
|
"lon": -118.319672222222,
|
||||||
"check_templates": [
|
"check_templates": [
|
||||||
"exiftool title: Waves crashing on rocks",
|
"exiftool title: Waves crashing on rocks",
|
||||||
"exiftool description: Used for testing osxphotos",
|
"exiftool description: Used for testing osxphotos",
|
||||||
"exiftool keywords: ['osxphotos']",
|
"exiftool keywords: ['osxphotos', 'Sümmer']",
|
||||||
"exiftool location: (33.7150638888889, -118.319672222222)",
|
"exiftool location: (33.7150638888889, -118.319672222222)",
|
||||||
"title: {exiftool:XMP:Title}: Waves crashing on rocks",
|
"title: {exiftool:XMP:Title}: Waves crashing on rocks",
|
||||||
"description: {exiftool:IPTC:Caption-Abstract}: Used for testing osxphotos",
|
"description: {exiftool:IPTC:Caption-Abstract}: Used for testing osxphotos",
|
||||||
"keyword: {exiftool:IPTC:Keywords}: ['osxphotos']",
|
"keyword: {exiftool:IPTC:Keywords}: ['osxphotos', 'Sümmer']",
|
||||||
"album: {filepath.parent}: test-images",
|
"album: {filepath.parent}: test-images",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -536,7 +537,44 @@ def test_import_keyword_merge():
|
|||||||
photo_1 = Photo(uuid_1)
|
photo_1 = Photo(uuid_1)
|
||||||
|
|
||||||
assert photo_1.filename == file_1
|
assert photo_1.filename == file_1
|
||||||
assert sorted(photo_1.keywords) == ["Bar", "Foo", "osxphotos"]
|
assert sorted(photo_1.keywords) == sorted(list(set(["Bar", "Foo"] + TEST_DATA[TEST_IMAGE_1]["keywords"])))
|
||||||
|
|
||||||
|
@pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed")
|
||||||
|
@pytest.mark.test_import
|
||||||
|
def test_import_keyword_merge_unicode():
|
||||||
|
"""Test import with --keyword and --merge-keywords with unicode keywords (#1085)"""
|
||||||
|
cwd = os.getcwd()
|
||||||
|
test_image_1 = os.path.join(cwd, TEST_IMAGE_1)
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(
|
||||||
|
import_cli,
|
||||||
|
[
|
||||||
|
"--verbose",
|
||||||
|
"--clear-metadata",
|
||||||
|
"--exiftool",
|
||||||
|
"--keyword",
|
||||||
|
"Bar",
|
||||||
|
"--keyword",
|
||||||
|
"Foo",
|
||||||
|
"--keyword",
|
||||||
|
unicodedata.normalize("NFD", "Sümmer"),
|
||||||
|
"--keyword",
|
||||||
|
unicodedata.normalize("NFC", "Sümmer"),
|
||||||
|
"--merge-keywords",
|
||||||
|
test_image_1,
|
||||||
|
],
|
||||||
|
terminal_width=TERMINAL_WIDTH,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
import_data = parse_import_output(result.output)
|
||||||
|
file_1 = pathlib.Path(test_image_1).name
|
||||||
|
uuid_1 = import_data[file_1]
|
||||||
|
photo_1 = Photo(uuid_1)
|
||||||
|
|
||||||
|
assert photo_1.filename == file_1
|
||||||
|
assert sorted(photo_1.keywords) == sorted(list(set(["Bar", "Foo"] + TEST_DATA[TEST_IMAGE_1]["keywords"])))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.test_import
|
@pytest.mark.test_import
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import os
|
|||||||
import pytest
|
import pytest
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
import photoscript
|
import photoscript
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import os
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
from osxphotos._constants import _UNKNOWN_PERSON
|
||||||
from osxphotos.utils import get_macos_version, is_macos
|
from osxphotos.platform import get_macos_version, is_macos
|
||||||
|
|
||||||
OS_VERSION = get_macos_version() if is_macos else (None, None, None)
|
OS_VERSION = get_macos_version() if is_macos else (None, None, None)
|
||||||
SKIP_TEST = "OSXPHOTOS_TEST_EXPORT" not in os.environ or OS_VERSION[1] != "15"
|
SKIP_TEST = "OSXPHOTOS_TEST_EXPORT" not in os.environ or OS_VERSION[1] != "15"
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import pytest
|
|||||||
import osxphotos
|
import osxphotos
|
||||||
from osxphotos._constants import _UNKNOWN_PERSON
|
from osxphotos._constants import _UNKNOWN_PERSON
|
||||||
from osxphotos.photoexporter import PhotoExporter
|
from osxphotos.photoexporter import PhotoExporter
|
||||||
from osxphotos.utils import get_macos_version, is_macos
|
from osxphotos.platform import get_macos_version, is_macos
|
||||||
|
|
||||||
OS_VERSION = get_macos_version() if is_macos else (None, None, None)
|
OS_VERSION = get_macos_version() if is_macos else (None, None, None)
|
||||||
# SKIP_TEST = "OSXPHOTOS_TEST_EXPORT" not in os.environ or OS_VERSION[1] != "17"
|
# SKIP_TEST = "OSXPHOTOS_TEST_EXPORT" not in os.environ or OS_VERSION[1] != "17"
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import tempfile
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from osxphotos.photokit import (
|
from osxphotos.photokit import (
|
||||||
|
|||||||
90
tests/test_photosalbum_unicode.py
Normal file
90
tests/test_photosalbum_unicode.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
"""Test unicode names in PhotoAlbum PhotoAlbumPhotoScript (#1085)"""
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
from unicodedata import normalize
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
|
if not is_macos:
|
||||||
|
pytest.skip("requires macOS", allow_module_level=True)
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
from osxphotos.photosalbum import PhotosAlbum, PhotosAlbumPhotoScript
|
||||||
|
from osxphotos.unicode import *
|
||||||
|
|
||||||
|
UNICODE_FOLDER_NFC = normalize("NFC", "FolderUnicode/føldêr2")
|
||||||
|
UNICODE_FOLDER_NFD = normalize("NFD", UNICODE_FOLDER_NFC)
|
||||||
|
|
||||||
|
UNICODE_ALBUM_NFC = normalize("NFC", "âlbüm")
|
||||||
|
UNICODE_ALBUM_NFD = normalize("NFD", UNICODE_ALBUM_NFC)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not is_macos, reason="requires macOS")
|
||||||
|
@pytest.mark.addalbum
|
||||||
|
def test_unicode_album(addalbum_library):
|
||||||
|
"""Test that unicode album name is handled correctly and a duplicate album is not created"""
|
||||||
|
|
||||||
|
# get some photos to add
|
||||||
|
photosdb = osxphotos.PhotosDB()
|
||||||
|
photos = photosdb.query(osxphotos.QueryOptions(person=["Katie"]))
|
||||||
|
|
||||||
|
# get the album
|
||||||
|
album_name_nfc = UNICODE_ALBUM_NFC
|
||||||
|
album_nfc = PhotosAlbum(album_name_nfc, split_folder=None)
|
||||||
|
album_nfc.add_list(photos)
|
||||||
|
|
||||||
|
# again with NFD
|
||||||
|
album_name_nfd = UNICODE_ALBUM_NFD
|
||||||
|
album_nfd = PhotosAlbum(album_name_nfd, split_folder=None)
|
||||||
|
album_nfd.add_list(photos)
|
||||||
|
|
||||||
|
assert album_nfc.album.uuid == album_nfd.album.uuid
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not is_macos, reason="requires macOS")
|
||||||
|
@pytest.mark.addalbum
|
||||||
|
def test_unicode_folder_album_1(addalbum_library):
|
||||||
|
"""Test that unicode album name is handled correctly and a duplicate album is not created when album is in a folder"""
|
||||||
|
|
||||||
|
# get some photos to add
|
||||||
|
photosdb = osxphotos.PhotosDB()
|
||||||
|
photos = photosdb.query(osxphotos.QueryOptions(person=["Katie"]))
|
||||||
|
|
||||||
|
# get the album
|
||||||
|
album_name_nfc = f"{UNICODE_FOLDER_NFC}/{UNICODE_ALBUM_NFC}"
|
||||||
|
album_nfc = PhotosAlbum(album_name_nfc, split_folder="/")
|
||||||
|
album_nfc.add_list(photos)
|
||||||
|
|
||||||
|
# again with NFD
|
||||||
|
album_name_nfd = f"{UNICODE_FOLDER_NFD}/{UNICODE_ALBUM_NFD}"
|
||||||
|
album_nfd = PhotosAlbum(album_name_nfd, split_folder="/")
|
||||||
|
album_nfd.add_list(photos)
|
||||||
|
|
||||||
|
assert album_nfc.album.uuid == album_nfd.album.uuid
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not is_macos, reason="requires macOS")
|
||||||
|
@pytest.mark.addalbum
|
||||||
|
def test_unicode_folder_album_2(addalbum_library):
|
||||||
|
"""Test that unicode album name is handled correctly and a duplicate album is not created when album is in a folder
|
||||||
|
|
||||||
|
This is a variation of test_unicode_folder_album_1 where the album is created in the same unicode folder as the previous album
|
||||||
|
"""
|
||||||
|
|
||||||
|
# get some photos to add
|
||||||
|
photosdb = osxphotos.PhotosDB()
|
||||||
|
photos = photosdb.query(osxphotos.QueryOptions(person=["Katie"]))
|
||||||
|
|
||||||
|
# get the album
|
||||||
|
album_name_nfc = f"{UNICODE_FOLDER_NFC}/{UNICODE_ALBUM_NFC}"
|
||||||
|
album_nfc = PhotosAlbum(album_name_nfc, split_folder="/")
|
||||||
|
album_nfc.add_list(photos)
|
||||||
|
|
||||||
|
# again with NFD
|
||||||
|
album_name_nfd = f"{UNICODE_FOLDER_NFC}/{UNICODE_ALBUM_NFD}"
|
||||||
|
album_nfd = PhotosAlbum(album_name_nfd, split_folder="/")
|
||||||
|
album_nfd.add_list(photos)
|
||||||
|
|
||||||
|
assert album_nfc.album.uuid == album_nfd.album.uuid
|
||||||
@ -15,7 +15,7 @@ from osxphotos.phototemplate import (
|
|||||||
PhotoTemplate,
|
PhotoTemplate,
|
||||||
RenderOptions,
|
RenderOptions,
|
||||||
)
|
)
|
||||||
from osxphotos.utils import is_macos
|
from osxphotos.platform import is_macos
|
||||||
|
|
||||||
from .locale_util import setlocale
|
from .locale_util import setlocale
|
||||||
from .photoinfo_mock import PhotoInfoMock
|
from .photoinfo_mock import PhotoInfoMock
|
||||||
|
|||||||
110
tests/test_unicode.py
Normal file
110
tests/test_unicode.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
"""Test unicode utilities"""
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
from unicodedata import normalize
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from osxphotos.platform import is_macos
|
||||||
|
from osxphotos.unicode import *
|
||||||
|
|
||||||
|
UNICODE_PATH_NFC = normalize("NFC", "/path/to/ünicøde")
|
||||||
|
UNICODE_PATH_NFD = normalize("NFD", UNICODE_PATH_NFC)
|
||||||
|
|
||||||
|
UNICODE_STR_NFC = normalize("NFC", "âbc")
|
||||||
|
UNICODE_STR_NFD = normalize("NFD", UNICODE_STR_NFC)
|
||||||
|
|
||||||
|
UNICODE_LIST_NFC = [normalize("NFC", "âbc"), normalize("NFC", "dê")]
|
||||||
|
UNICODE_LIST_NFD = [normalize("NFD", "âbc"), normalize("NFD", "dê")]
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_unicode_format():
|
||||||
|
set_unicode_form("NFC")
|
||||||
|
assert get_unicode_form() == "NFC"
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_unicode_format():
|
||||||
|
set_unicode_form("NFD")
|
||||||
|
assert get_unicode_form() == "NFD"
|
||||||
|
|
||||||
|
set_unicode_form("NFC")
|
||||||
|
assert get_unicode_form() == "NFC"
|
||||||
|
|
||||||
|
# test invalid format
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
set_unicode_form("foo")
|
||||||
|
|
||||||
|
# Reset to correct format based
|
||||||
|
set_unicode_form(DEFAULT_UNICODE_FORM)
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_unicode_fs_format():
|
||||||
|
set_unicode_fs_form("NFC")
|
||||||
|
assert get_unicode_fs_form() == "NFC"
|
||||||
|
|
||||||
|
set_unicode_fs_form("NFD")
|
||||||
|
assert get_unicode_fs_form() == "NFD"
|
||||||
|
|
||||||
|
# test invalid format
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
set_unicode_fs_form("foo")
|
||||||
|
|
||||||
|
# Reset to correct format based on platform
|
||||||
|
set_unicode_fs_form("NFD" if is_macos else "NFC")
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_fs_path():
|
||||||
|
# Test with string path in NFC format
|
||||||
|
set_unicode_fs_form("NFC")
|
||||||
|
assert normalize_fs_path(UNICODE_PATH_NFD) == UNICODE_PATH_NFC
|
||||||
|
|
||||||
|
# Test with string path in NFD format
|
||||||
|
set_unicode_fs_form("NFD")
|
||||||
|
assert normalize_fs_path(UNICODE_PATH_NFC) == UNICODE_PATH_NFD
|
||||||
|
|
||||||
|
# Test with pathlib.Path object in NFC format
|
||||||
|
set_unicode_fs_form("NFC")
|
||||||
|
assert normalize_fs_path(pathlib.Path(UNICODE_PATH_NFD)) == pathlib.Path(
|
||||||
|
UNICODE_PATH_NFC
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with pathlib.Path object in NFD format
|
||||||
|
set_unicode_fs_form("NFD")
|
||||||
|
assert normalize_fs_path(pathlib.Path(UNICODE_PATH_NFC)) == pathlib.Path(
|
||||||
|
UNICODE_PATH_NFD
|
||||||
|
)
|
||||||
|
|
||||||
|
# Reset to correct format based on platform
|
||||||
|
set_unicode_fs_form("NFD" if is_macos else "NFC")
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_unicode():
|
||||||
|
# Test with str in NFC format
|
||||||
|
set_unicode_form("NFC")
|
||||||
|
assert normalize_unicode(UNICODE_STR_NFD) == UNICODE_STR_NFC
|
||||||
|
|
||||||
|
# Test with str in NFD format
|
||||||
|
set_unicode_form("NFD")
|
||||||
|
assert normalize_unicode(UNICODE_STR_NFC) == UNICODE_STR_NFD
|
||||||
|
|
||||||
|
# Test with list of str in NFC format
|
||||||
|
set_unicode_form("NFC")
|
||||||
|
assert normalize_unicode(UNICODE_LIST_NFD) == UNICODE_LIST_NFC
|
||||||
|
|
||||||
|
# Test with list of str in NFD format
|
||||||
|
set_unicode_form("NFD")
|
||||||
|
assert normalize_unicode(UNICODE_LIST_NFC) == UNICODE_LIST_NFD
|
||||||
|
|
||||||
|
# Test with tuple of str in NFC format
|
||||||
|
set_unicode_form("NFC")
|
||||||
|
assert normalize_unicode(tuple(UNICODE_LIST_NFD)) == tuple(UNICODE_LIST_NFC)
|
||||||
|
|
||||||
|
# Test with tuple of str in NFD format
|
||||||
|
set_unicode_form("NFD")
|
||||||
|
assert normalize_unicode(tuple(UNICODE_LIST_NFC)) == tuple(UNICODE_LIST_NFD)
|
||||||
|
|
||||||
|
# Test with None
|
||||||
|
assert normalize_unicode(None) is None
|
||||||
|
|
||||||
|
# Reset to correct format based
|
||||||
|
set_unicode_form(DEFAULT_UNICODE_FORM)
|
||||||
@ -3,12 +3,12 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import osxphotos.uti
|
import osxphotos.uti
|
||||||
|
from osxphotos.platform import is_macos
|
||||||
from osxphotos.uti import (
|
from osxphotos.uti import (
|
||||||
_get_uti_from_mdls,
|
_get_uti_from_mdls,
|
||||||
get_preferred_uti_extension,
|
get_preferred_uti_extension,
|
||||||
get_uti_for_extension,
|
get_uti_for_extension,
|
||||||
)
|
)
|
||||||
from osxphotos.utils import is_macos
|
|
||||||
|
|
||||||
EXT_DICT = {"heic": "public.heic", "jpg": "public.jpeg", ".jpg": "public.jpeg"}
|
EXT_DICT = {"heic": "public.heic", "jpg": "public.jpeg", ".jpg": "public.jpeg"}
|
||||||
UTI_DICT = {"public.heic": "heic", "public.jpeg": "jpeg"}
|
UTI_DICT = {"public.heic": "heic", "public.jpeg": "jpeg"}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user