Bug fix for template functions #477
This commit is contained in:
parent
5ea01df69b
commit
49317582c4
@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.42.45"
|
||||
__version__ = "0.42.46"
|
||||
|
||||
@ -57,7 +57,7 @@ from .photokit import check_photokit_authorization, request_photokit_authorizati
|
||||
from .photosalbum import PhotosAlbum
|
||||
from .phototemplate import PhotoTemplate, RenderOptions
|
||||
from .queryoptions import QueryOptions
|
||||
from .utils import get_preferred_uti_extension, load_function
|
||||
from .utils import get_preferred_uti_extension, load_function, expand_and_validate_filepath
|
||||
|
||||
# global variable to control verbose output
|
||||
# set via --verbose/-V
|
||||
@ -172,13 +172,14 @@ class FunctionCall(click.ParamType):
|
||||
|
||||
filename, funcname = value.split("::")
|
||||
|
||||
if not pathlib.Path(filename).is_file():
|
||||
filename_validated = expand_and_validate_filepath(filename)
|
||||
if not filename_validated:
|
||||
self.fail(f"'{filename}' does not appear to be a file")
|
||||
|
||||
try:
|
||||
function = load_function(filename, funcname)
|
||||
function = load_function(filename_validated, funcname)
|
||||
except Exception as e:
|
||||
self.fail(f"Could not load function {funcname} from {filename}")
|
||||
self.fail(f"Could not load function {funcname} from {filename_validated}")
|
||||
|
||||
return (function, value)
|
||||
|
||||
|
||||
@ -4,8 +4,10 @@ import datetime
|
||||
import locale
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import shlex
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from textx import TextXSyntaxError, metamodel_from_file
|
||||
|
||||
@ -14,9 +16,7 @@ from ._version import __version__
|
||||
from .datetime_formatter import DateTimeFormatter
|
||||
from .exiftool import ExifToolCaching
|
||||
from .path_utils import sanitize_dirname, sanitize_filename, sanitize_pathpart
|
||||
from .utils import load_function
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
from .utils import expand_and_validate_filepath, load_function
|
||||
|
||||
# TODO: a lot of values are passed from function to function like path_sep--make these all class properties
|
||||
|
||||
@ -1177,10 +1177,11 @@ class PhotoTemplate:
|
||||
|
||||
filename, funcname = subfield.split("::")
|
||||
|
||||
if not pathlib.Path(filename).is_file():
|
||||
filename_validated = expand_and_validate_filepath(filename)
|
||||
if not filename_validated:
|
||||
raise ValueError(f"'{filename}' does not appear to be a file")
|
||||
|
||||
template_func = load_function(filename, funcname)
|
||||
template_func = load_function(filename_validated, funcname)
|
||||
values = template_func(self.photo)
|
||||
|
||||
if not isinstance(values, (str, list)):
|
||||
@ -1211,10 +1212,11 @@ class PhotoTemplate:
|
||||
|
||||
filename, funcname = filter_.split("::")
|
||||
|
||||
if not pathlib.Path(filename).is_file():
|
||||
filename_validated = expand_and_validate_filepath(filename)
|
||||
if not filename_validated:
|
||||
raise ValueError(f"'{filename}' does not appear to be a file")
|
||||
|
||||
template_func = load_function(filename, funcname)
|
||||
template_func = load_function(filename_validated, funcname)
|
||||
|
||||
if not isinstance(values, (list, tuple)):
|
||||
values = [values]
|
||||
|
||||
@ -63,7 +63,8 @@ SubField:
|
||||
;
|
||||
|
||||
SUBFIELD_WORD:
|
||||
/[\.\w:\/]+/
|
||||
/[\.\w:\/\-\~\'\"\%\@\#\^\’]+/
|
||||
/\\\s/?
|
||||
;
|
||||
|
||||
Filter:
|
||||
|
||||
@ -419,6 +419,20 @@ def increment_filename(filepath):
|
||||
return str(dest)
|
||||
|
||||
|
||||
def expand_and_validate_filepath(path: str) -> str:
|
||||
"""validate and expand ~ in filepath, also un-escapes "\ "
|
||||
|
||||
Returns:
|
||||
expanded path if path is valid file, else None
|
||||
"""
|
||||
|
||||
path = re.sub(r"\\ ", " ", path)
|
||||
path = pathlib.Path(path).expanduser()
|
||||
if path.is_file():
|
||||
return str(path)
|
||||
return None
|
||||
|
||||
|
||||
def load_function(pyfile: str, function_name: str) -> Callable:
|
||||
"""Load function_name from python file pyfile"""
|
||||
module_file = pathlib.Path(pyfile)
|
||||
|
||||
3
tests/hyphen-dir/README.md
Normal file
3
tests/hyphen-dir/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Contents
|
||||
|
||||
This directory used by test_template.py for testing {function} templates with hyphenated directory names
|
||||
20
tests/hyphen-dir/template_function.py
Normal file
20
tests/hyphen-dir/template_function.py
Normal file
@ -0,0 +1,20 @@
|
||||
""" Example showing how to use a custom function for osxphotos {function} template """
|
||||
|
||||
import pathlib
|
||||
from typing import List, Union
|
||||
|
||||
import osxphotos
|
||||
|
||||
|
||||
def foo(photo: osxphotos.PhotoInfo, **kwargs) -> Union[List, str]:
|
||||
""" example function for {function} template
|
||||
|
||||
Args:
|
||||
photo: osxphotos.PhotoInfo object
|
||||
**kwargs: not currently used, placeholder to keep functions compatible with possible changes to {function}
|
||||
|
||||
Returns:
|
||||
str or list of str of values that should be substituted for the {function} template
|
||||
"""
|
||||
|
||||
return photo.original_filename + "-FOO"
|
||||
@ -82,7 +82,9 @@ TEMPLATE_VALUES_TITLE = {
|
||||
"{title|titlecase}": ["Tulips Tied Together At A Flower Shop"],
|
||||
"{title|upper}": ["TULIPS TIED TOGETHER AT A FLOWER SHOP"],
|
||||
"{title|titlecase|lower|upper}": ["TULIPS TIED TOGETHER AT A FLOWER SHOP"],
|
||||
"{title|titlecase|lower|upper|shell_quote}": ["'TULIPS TIED TOGETHER AT A FLOWER SHOP'"],
|
||||
"{title|titlecase|lower|upper|shell_quote}": [
|
||||
"'TULIPS TIED TOGETHER AT A FLOWER SHOP'"
|
||||
],
|
||||
"{title|upper|titlecase}": ["Tulips Tied Together At A Flower Shop"],
|
||||
"{title|capitalize}": ["Tulips tied together at a flower shop"],
|
||||
"{title[ ,_]}": ["Tulips_tied_together_at_a_flower_shop"],
|
||||
@ -388,7 +390,9 @@ def test_lookup_multi(photosdb_places):
|
||||
lookup_str = re.match(r"\{([^\\,}]+)\}", subst).group(1)
|
||||
if subst in ["{exiftool}", "{photo}", "{function}"]:
|
||||
continue
|
||||
lookup = template.get_template_value_multi(lookup_str, path_sep=os.path.sep, default=[])
|
||||
lookup = template.get_template_value_multi(
|
||||
lookup_str, path_sep=os.path.sep, default=[]
|
||||
)
|
||||
assert isinstance(lookup, list)
|
||||
|
||||
|
||||
@ -975,6 +979,15 @@ def test_conditional(photosdb):
|
||||
assert sorted(rendered) == sorted(UUID_CONDITIONAL[uuid][template])
|
||||
|
||||
|
||||
def test_function_hyphen_dir(photosdb):
|
||||
"""Test {function} with a hyphenated directory (#477)"""
|
||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||
rendered, _ = photo.render_template(
|
||||
"{function:tests/hyphen-dir/template_function.py::foo}"
|
||||
)
|
||||
assert rendered == [f"{photo.original_filename}-FOO"]
|
||||
|
||||
|
||||
def test_function(photosdb):
|
||||
"""Test {function}"""
|
||||
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user