Skip to content

Commit

Permalink
tests: properly escape % in directory names
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfikl committed May 20, 2024
1 parent f904507 commit b96d98d
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 20 deletions.
2 changes: 1 addition & 1 deletion papis/commands/addto.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def cli(query: str,
document = docs[0]

if file_name is not None: # Use args if set
papis.config.set("add-file-name", file_name)
papis.config.set("add-file-name", papis.config.escape_interp(file_name))

try:
run(document, files + urls, git=git, link=link)
Expand Down
2 changes: 1 addition & 1 deletion papis/commands/edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def cli(query: str,
return

if editor is not None:
papis.config.set("editor", editor)
papis.config.set("editor", papis.config.escape_interp(editor))

for document in documents:
if notes:
Expand Down
6 changes: 3 additions & 3 deletions papis/commands/open.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def run(document: papis.document.Document,
folder: bool = False,
mark: bool = False) -> None:
if opener is not None:
papis.config.set("opentool", opener)
papis.config.set("opentool", papis.config.escape_interp(opener))

_doc_folder = document.get_main_folder()
if _doc_folder is None:
Expand Down Expand Up @@ -136,7 +136,7 @@ def run(document: papis.document.Document,
papis.document.from_data(mark_dict[0]),
doc_key=_mark_name)
logger.info("Setting opener to '%s'.", opener)
papis.config.set("opentool", opener)
papis.config.set("opentool", papis.config.escape_interp(opener))
files = document.get_files()
if not files:
logger.error("The chosen document has no files attached: '%s'.",
Expand Down Expand Up @@ -169,7 +169,7 @@ def cli(query: str, doc_folder: Tuple[str, ...], tool: str, folder: bool,
mark: bool) -> None:
"""Open document from a given library"""
if tool:
papis.config.set("opentool", tool)
papis.config.set("opentool", papis.config.escape_interp(tool))

documents = papis.cli.handle_doc_folder_query_all_sort(query,
doc_folder,
Expand Down
20 changes: 18 additions & 2 deletions papis/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,8 @@ def set_lib(library: papis.library.Library) -> None:

if library.name not in config:
# NOTE: can't use set(...) here due to cyclic dependencies
config[library.name] = {"dirs": str(library.paths)}
paths = [escape_interp(path) for path in library.paths]
config[library.name] = {"dirs": str(paths)}

CURRENT_LIBRARY = library

Expand Down Expand Up @@ -577,7 +578,7 @@ def get_lib_from_name(libname: str) -> papis.library.Library:

lib = papis.library.from_paths([libname])
# NOTE: can't use set(...) here due to cyclic dependencies
config[lib.name] = {"dirs": str(lib.paths)}
config[lib.name] = {"dirs": str([escape_interp(libname)])}
else:
raise RuntimeError(
f"Library '{libname}' does not seem to exist. "
Expand Down Expand Up @@ -679,3 +680,18 @@ def reset_configuration() -> Configuration:

logger.debug("Resetting configuration.")
return get_configuration()


def escape_interp(path: str) -> str:
"""Escape paths added to the configuration file.
By default, the :class:`papis.config.Configuration` enables string interpolation
in the key values (e.g. using ``key = %(other_key)s-suffix)``). Any paths
added to the configuration should then be escaped so that they do not
interfere with the interpolation.
"""
import re

# FIXME: this should be smart enough to not double quote valid interpolation
# paths? Would need a regex to skip things like `%\(\w+\)`?
return re.sub(r"([^%])%([^%(])", r"\1%%\2", path)
11 changes: 8 additions & 3 deletions papis/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class TemporaryConfiguration:
libname: ClassVar[str] = "test"

def __init__(self,
prefix: str = "papis-test-",
settings: Optional[Dict[str, Any]] = None,
overwrite: bool = False) -> None:
#: A set of settings to be added to the configuration on creation
Expand All @@ -217,6 +218,9 @@ def __init__(self,
#: file used by papis.
self.configfile: str = ""

#: Prefix for the temporary directory created for the test.
self.prefix = prefix

self._tmpdir: Optional[tempfile.TemporaryDirectory[str]] = None
self._monkeypatch: Optional[pytest.MonkeyPatch] = None

Expand All @@ -241,7 +245,7 @@ def __enter__(self) -> "TemporaryConfiguration":

# create directories and files
self._monkeypatch = pytest.MonkeyPatch()
self._tmpdir = tempfile.TemporaryDirectory(prefix="papis-test-")
self._tmpdir = tempfile.TemporaryDirectory(prefix=self.prefix)

self.libdir = os.path.join(self.tmpdir, "lib")
self.configdir = os.path.join(self.tmpdir, "papis")
Expand All @@ -253,8 +257,10 @@ def __enter__(self) -> "TemporaryConfiguration":
os.makedirs(self.configscripts)

# load settings
import papis.config

Check notice

Code scanning / CodeQL

Module is imported with 'import' and 'import from' Note

Module 'papis.config' is imported with both 'import' and 'import from'.

settings = {
self.libname: {"dir": self.libdir},
self.libname: {"dir": papis.config.escape_interp(self.libdir)},
"settings": {"default-library": self.libname}
}

Expand Down Expand Up @@ -289,7 +295,6 @@ def __enter__(self) -> "TemporaryConfiguration":
# * reset papis.plugin managers

# reload configuration
import papis.config
papis.config.set_config_file(self.configfile)
papis.config.reset_configuration()

Expand Down
10 changes: 7 additions & 3 deletions tests/commands/test_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ def get_mock_script(name: str) -> str:

script = os.path.join(os.path.dirname(__file__), "scripts.py")

return "{} {} {}".format(sys.executable, script, name)
from papis.config import escape_interp

return escape_interp("{} {} {}".format(sys.executable, script, name))


@pytest.mark.library_setup(settings={
Expand Down Expand Up @@ -66,7 +68,8 @@ def test_edit_cli(tmp_library: TemporaryLibrary) -> None:
cli,
["--all", "--editor", ls, "krishnamurti"])
assert result.exit_code == 0
assert papis.config.get("editor") == get_mock_script("ls")
assert (papis.config.escape_interp(papis.config.get("editor"))
== get_mock_script("ls"))

# check --notes
notes_name = papis.config.get("notes-name")
Expand All @@ -76,7 +79,8 @@ def test_edit_cli(tmp_library: TemporaryLibrary) -> None:
cli,
["--all", "--editor", get_mock_script("echo"), "--notes", "krishnamurti"])
assert result.exit_code == 0
assert papis.config.get("editor") == get_mock_script("echo")
assert (papis.config.escape_interp(papis.config.get("editor"))
== get_mock_script("echo"))

db = papis.database.get()
doc, = db.query_dict({"author": "Krishnamurti"})
Expand Down
19 changes: 16 additions & 3 deletions tests/commands/test_open.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,24 @@

from papis.testing import TemporaryLibrary, PapisRunner

script = os.path.join(os.path.dirname(__file__), "scripts.py")

def get_mock_script(name: str) -> str:
"""Constructs a command call for a command mocked by ``scripts.py``.
:param name: the name of the command, e.g. ``ls`` or ``echo``.
:returns: a string of the command
"""
import sys

script = os.path.join(os.path.dirname(__file__), "scripts.py")

from papis.config import escape_interp

return escape_interp("{} {} {}".format(sys.executable, script, name))


@pytest.mark.library_setup(settings={
"file-browser": "{} {} echo".format(sys.executable, script)
"file-browser": get_mock_script("echo")
})
def test_open_cli(tmp_library: TemporaryLibrary) -> None:
from papis.commands.open import cli
Expand All @@ -35,7 +48,7 @@ def test_open_cli(tmp_library: TemporaryLibrary) -> None:
# Use a mock scriptlet
result = cli_runner.invoke(
cli,
["--tool", "{} {} echo".format(sys.executable, script),
["--tool", get_mock_script("echo"),
"--mark", "--all", "Krishnamurti"])
assert result.exit_code == 0

Expand Down
22 changes: 19 additions & 3 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,23 @@ def test_get_config_paths(tmp_config: TemporaryConfiguration) -> None:
assert papis.config.get_scripts_folder() == scriptsdir


def test_get_config_home(tmp_config: TemporaryConfiguration, monkeypatch) -> None:
def test_get_config_home(tmp_config: TemporaryConfiguration,
monkeypatch: pytest.MonkeyPatch) -> None:
import papis.config
assert re.match(r".+papis", papis.config.get_config_home()) is not None


def test_config_interpolation() -> None:
with TemporaryConfiguration(prefix="papis%test%") as tmp_config:
import papis.config

assert papis.config.get("dir", section=tmp_config.libname) == tmp_config.libdir

papis.config.set("some_value", "value1")
papis.config.set("more_value", "more_%(some_value)s")
assert papis.config.get("more_value") == "more_value1"


def test_set(tmp_config: TemporaryConfiguration) -> None:
import papis.config
papis.config.set("nonexistenkey", "rofi")
Expand Down Expand Up @@ -119,7 +131,8 @@ def test_get_types(tmp_config: TemporaryConfiguration) -> None:
value = papis.config.getboolean("boolean_config")


def test_get_configuration(tmp_config: TemporaryConfiguration, monkeypatch) -> None:
def test_get_configuration(tmp_config: TemporaryConfiguration,
monkeypatch: pytest.MonkeyPatch) -> None:
import papis.config

general_name = papis.config.get_general_settings_name()
Expand Down Expand Up @@ -178,6 +191,7 @@ def test_set_lib_from_path(tmp_config: TemporaryConfiguration) -> None:
import papis.config

assert tmp_config.libdir is not None

papis.config.set_lib_from_name(tmp_config.libdir)
assert papis.config.get_lib_name() == tmp_config.libdir

Expand All @@ -186,7 +200,9 @@ def test_set_lib_from_real_lib(tmp_config: TemporaryConfiguration) -> None:
import papis.config

libname = "test-set-lib"
papis.config.set("dir", tmp_config.libdir, section=libname)
papis.config.set("dir",
papis.config.escape_interp(tmp_config.libdir),
section=libname)

assert tmp_config.libdir is not None
assert os.path.exists(tmp_config.libdir)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def test_get_cache_home(tmp_config: TemporaryConfiguration, monkeypatch) -> None
assert get_cache_home() == os.path.join(tmp_config.tmpdir, "papis")
with tempfile.TemporaryDirectory(dir=tmp_config.tmpdir) as d:
tmp = os.path.join(d, "blah")
papis.config.set("cache-dir", tmp)
papis.config.set("cache-dir", papis.config.escape_interp(tmp))
assert get_cache_home() == tmp


Expand Down

0 comments on commit b96d98d

Please sign in to comment.