Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix OS compatibility issues for editors with spaces, slashes, and quotes #1153

Merged
merged 5 commits into from
Jan 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions features/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def before_feature(context, feature):
feature.skip()
return

if "skip_win" in feature.tags and on_windows:
if "skip_win" in feature.tags and on_windows():
feature.skip("Skipping on Windows")
return

Expand All @@ -69,7 +69,7 @@ def before_scenario(context, scenario):
scenario.skip()
return

if "skip_win" in scenario.effective_tags and on_windows:
if "skip_win" in scenario.effective_tags and on_windows():
scenario.skip("Skipping on Windows")
return

Expand Down
11 changes: 3 additions & 8 deletions features/steps/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import os
from pathlib import Path
import re
import shlex
import time
from unittest.mock import patch

Expand All @@ -22,7 +21,7 @@
from jrnl import plugins
from jrnl.cli import cli
from jrnl.config import load_config
from jrnl.os_compat import on_windows
from jrnl.os_compat import split_args

try:
import parsedatetime.parsedatetime_consts as pdt
Expand Down Expand Up @@ -87,10 +86,6 @@ def delete_password(self, servicename, username):
keyring.set_keyring(TestKeyring())


def ushlex(command):
return shlex.split(command, posix=not on_windows)


def read_journal(context, journal_name="default"):
configuration = load_config(context.config_path)
with open(configuration["journals"][journal_name]) as journal_file:
Expand Down Expand Up @@ -300,7 +295,7 @@ def run_with_input(context, command, inputs=""):
else:
text = iter([inputs])

args = ushlex(command)[1:]
args = split_args(command)[1:]

def _mock_editor(command):
context.editor_command = command
Expand Down Expand Up @@ -383,7 +378,7 @@ def run(context, command, text=""):
cache_dir = os.path.join("features", "cache", context.cache_dir)
command = command.format(cache_dir=cache_dir)

args = ushlex(command)
args = split_args(command)

def _mock_editor(command):
context.editor_command = command
Expand Down
17 changes: 12 additions & 5 deletions features/steps/export_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,24 @@ def assert_exported_yaml_file_content(context, file_path, cache_dir=None):

for actual_line, expected_line in zip(actual_content, expected_content):
if actual_line.startswith("tags: ") and expected_line.startswith("tags: "):
assert_equal_tags_ignoring_order(actual_line, expected_line)
assert_equal_tags_ignoring_order(
actual_line, expected_line, actual_content, expected_content
)
else:
assert actual_line.strip() == expected_line.strip(), [
actual_line.strip(),
expected_line.strip(),
[actual_line.strip(), expected_line.strip()],
[actual_content, expected_content],
]


def assert_equal_tags_ignoring_order(actual_line, expected_line):
def assert_equal_tags_ignoring_order(
actual_line, expected_line, actual_content, expected_content
):
actual_tags = set(tag.strip() for tag in actual_line[len("tags: ") :].split(","))
expected_tags = set(
tag.strip() for tag in expected_line[len("tags: ") :].split(",")
)
assert actual_tags == expected_tags, [actual_tags, expected_tags]
assert actual_tags == expected_tags, [
[actual_tags, expected_tags],
[expected_content, actual_content],
]
2 changes: 1 addition & 1 deletion jrnl/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from .os_compat import on_windows

if on_windows:
if on_windows():
colorama.init()

WARNING_COLOR = colorama.Fore.YELLOW
Expand Down
6 changes: 3 additions & 3 deletions jrnl/editor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import logging
import os
import shlex
import subprocess
import sys
import tempfile
Expand All @@ -10,6 +9,7 @@
from .color import ERROR_COLOR
from .color import RESET_COLOR
from .os_compat import on_windows
from .os_compat import split_args


def get_text_from_editor(config, template=""):
Expand All @@ -25,7 +25,7 @@ def get_text_from_editor(config, template=""):
f.write(template)

try:
subprocess.call(shlex.split(config["editor"], posix=on_windows) + [tmpfile])
subprocess.call(split_args(config["editor"]) + [tmpfile])
except Exception as e:
error_msg = f"""
{ERROR_COLOR}{str(e)}{RESET_COLOR}
Expand All @@ -47,7 +47,7 @@ def get_text_from_editor(config, template=""):


def get_text_from_stdin():
_how_to_quit = "Ctrl+z and then Enter" if on_windows else "Ctrl+d"
_how_to_quit = "Ctrl+z and then Enter" if on_windows() else "Ctrl+d"
print(
f"[Writing Entry; on a blank line, press {_how_to_quit} to finish writing]\n",
file=sys.stderr,
Expand Down
14 changes: 13 additions & 1 deletion jrnl/os_compat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

import shlex
from sys import platform

on_windows = "win32" in platform

def on_windows():
return "win32" in platform


def on_posix():
return not on_windows()


def split_args(args):
"""Split arguments and add escape characters as appropriate for the OS"""
return shlex.split(args, posix=on_posix())
87 changes: 87 additions & 0 deletions tests/test_os_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from unittest import mock
import pytest

from jrnl.os_compat import on_windows
from jrnl.os_compat import on_posix
from jrnl.os_compat import split_args


@pytest.mark.parametrize(
"systems",
[
["linux", False],
["win32", True],
["cygwin", False],
["msys", False],
["darwin", False],
["os2", False],
["os2emx", False],
["riscos", False],
["atheos", False],
["freebsd7", False],
["freebsd8", False],
["freebsdN", False],
["openbsd6", False],
],
)
def test_on_windows(systems):
osname, expected_on_windows = systems[0], systems[1]
with mock.patch("jrnl.os_compat.platform", osname):
assert on_windows() == expected_on_windows


@pytest.mark.parametrize(
"systems",
[
["linux", True],
["win32", False],
["cygwin", True],
["msys", True],
["darwin", True],
["os2", True],
["os2emx", True],
["riscos", True],
["atheos", True],
["freebsd7", True],
["freebsd8", True],
["freebsdN", True],
["openbsd6", True],
],
)
def test_on_posix(systems):
osname, expected_on_posix = systems[0], systems[1]
with mock.patch("jrnl.os_compat.platform", osname):
assert on_posix() == expected_on_posix


@pytest.mark.parametrize(
"args",
[
["notepad", ["notepad"]],
["subl -w", ["subl", "-w"]],
[
'"C:\\Program Files\\Sublime Text 3\\subl.exe" -w',
['"C:\\Program Files\\Sublime Text 3\\subl.exe"', "-w"],
],
],
)
def test_split_args_on_windows(args):
input_arguments, expected_split_args = args[0], args[1]
with mock.patch("jrnl.os_compat.on_windows", lambda: True):
assert split_args(input_arguments) == expected_split_args


@pytest.mark.parametrize(
"args",
[
["vim", ["vim"]],
[
'vim -f +Goyo +Limelight "+set spell linebreak"',
["vim", "-f", "+Goyo", "+Limelight", '"+set spell linebreak"'],
],
],
)
def test_split_args_on_not_windows(args):
input_arguments, expected_split_args = args[0], args[1]
with mock.patch("jrnl.os_compat.on_windows", lambda: True):
assert split_args(input_arguments) == expected_split_args