mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 08:38:32 +02:00
Move import to be standalone command to reduce clutter in cli.py
This commit is contained in:
parent
ce07fedc06
commit
a54ed90259
9 changed files with 113 additions and 84 deletions
|
@ -136,6 +136,40 @@ Feature: Basic reading and writing to a journal
|
||||||
Then the output should contain "jrnl"
|
Then the output should contain "jrnl"
|
||||||
And the output should contain "Python"
|
And the output should contain "Python"
|
||||||
|
|
||||||
Scenario: Version warning appears for versions below 3.7
|
Scenario: --import allows new entry to journal
|
||||||
When we run "jrnl --diagnostic"
|
Given we use the config "basic.yaml"
|
||||||
Then the Python version warning should appear if our version is below 3.7
|
When we run "jrnl --import" and pipe "[2020-07-05 15:00] Observe and import."
|
||||||
|
And we run "jrnl -1"
|
||||||
|
Then the journal should contain "[2020-07-05 15:00] Observe and import."
|
||||||
|
And the output should contain "Observe and import"
|
||||||
|
|
||||||
|
Scenario: --import allows new large entry to journal
|
||||||
|
Given we use the config "basic.yaml"
|
||||||
|
When we run "jrnl --import" and pipe
|
||||||
|
"""
|
||||||
|
[2020-07-05 15:00] Observe and import.
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada quis
|
||||||
|
est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque augue
|
||||||
|
et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu consequat.
|
||||||
|
Aenean ante ex, elementum ut interdum et, mattis eget lacus. In commodo nulla nec
|
||||||
|
tellus placerat, sed ultricies metus bibendum. Duis eget venenatis erat. In at
|
||||||
|
dolor dui end of entry.
|
||||||
|
"""
|
||||||
|
And we run "jrnl -1"
|
||||||
|
Then the journal should contain "[2020-07-05 15:00] Observe and import."
|
||||||
|
And the output should contain "Observe and import"
|
||||||
|
And the output should contain "Lorem ipsum"
|
||||||
|
And the output should contain "end of entry."
|
||||||
|
|
||||||
|
Scenario: --import allows import of multiple entries to journal
|
||||||
|
Given we use the config "basic.yaml"
|
||||||
|
When we run "jrnl --import" and pipe
|
||||||
|
"""
|
||||||
|
[2020-07-05 15:00] Observe and import.
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
|
||||||
|
[2020-07-05 15:01] Twice as nice.
|
||||||
|
Sed dignissim sed nisl eu consequat.
|
||||||
|
"""
|
||||||
|
Then the journal should contain "[2020-07-05 15:00] Observe and import."
|
||||||
|
Then the journal should contain "[2020-07-05 15:01] Twice as nice."
|
||||||
|
|
|
@ -210,8 +210,12 @@ def run_with_input(context, command, inputs=""):
|
||||||
|
|
||||||
|
|
||||||
@when('we run "{command}"')
|
@when('we run "{command}"')
|
||||||
|
@when('we run "{command}" and pipe')
|
||||||
|
@when('we run "{command}" and pipe "{text}"')
|
||||||
@when('we run "{command}" with cache directory "{cache_dir}"')
|
@when('we run "{command}" with cache directory "{cache_dir}"')
|
||||||
def run(context, command, cache_dir=None):
|
def run(context, command, text="", cache_dir=None):
|
||||||
|
text = text or context.text or ""
|
||||||
|
|
||||||
if cache_dir is not None:
|
if cache_dir is not None:
|
||||||
cache_dir = os.path.join("features", "cache", cache_dir)
|
cache_dir = os.path.join("features", "cache", cache_dir)
|
||||||
command = command.format(cache_dir=cache_dir)
|
command = command.format(cache_dir=cache_dir)
|
||||||
|
@ -224,7 +228,7 @@ def run(context, command, cache_dir=None):
|
||||||
try:
|
try:
|
||||||
with patch("sys.argv", args), patch(
|
with patch("sys.argv", args), patch(
|
||||||
"subprocess.call", side_effect=_mock_editor
|
"subprocess.call", side_effect=_mock_editor
|
||||||
):
|
), patch("sys.stdin.read", side_effect=lambda: text):
|
||||||
cli.run(args[1:])
|
cli.run(args[1:])
|
||||||
context.exit_status = 0
|
context.exit_status = 0
|
||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
|
|
|
@ -371,7 +371,7 @@ class LegacyJournal(Journal):
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
|
|
||||||
def open_journal(name, config, legacy=False):
|
def open_journal(journal_name, config, legacy=False):
|
||||||
"""
|
"""
|
||||||
Creates a normal, encrypted or DayOne journal based on the passed config.
|
Creates a normal, encrypted or DayOne journal based on the passed config.
|
||||||
If legacy is True, it will open Journals with legacy classes build for
|
If legacy is True, it will open Journals with legacy classes build for
|
||||||
|
@ -394,11 +394,18 @@ def open_journal(name, config, legacy=False):
|
||||||
|
|
||||||
if not config["encrypt"]:
|
if not config["encrypt"]:
|
||||||
if legacy:
|
if legacy:
|
||||||
return LegacyJournal(name, **config).open()
|
return LegacyJournal(journal_name, **config).open()
|
||||||
return PlainJournal(name, **config).open()
|
return PlainJournal(journal_name, **config).open()
|
||||||
else:
|
|
||||||
from . import EncryptedJournal
|
|
||||||
|
|
||||||
|
from . import EncryptedJournal
|
||||||
|
|
||||||
|
try:
|
||||||
if legacy:
|
if legacy:
|
||||||
return EncryptedJournal.LegacyEncryptedJournal(name, **config).open()
|
return EncryptedJournal.LegacyEncryptedJournal(
|
||||||
return EncryptedJournal.EncryptedJournal(name, **config).open()
|
journal_name, **config
|
||||||
|
).open()
|
||||||
|
return EncryptedJournal.EncryptedJournal(journal_name, **config).open()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
# Since encrypted journals prompt for a password, it's easy for a user to ctrl+c out
|
||||||
|
print("[Interrupted while opening journal]", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
62
jrnl/cli.py
62
jrnl/cli.py
|
@ -25,10 +25,10 @@ import platform
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from . import install, plugins, util
|
from . import install, plugins, util
|
||||||
from .parsing import parse_args_before_config
|
from .parsing import parse_args
|
||||||
from .parsing import parse_args_after_config
|
|
||||||
from .Journal import PlainJournal, open_journal
|
from .Journal import PlainJournal, open_journal
|
||||||
from .util import WARNING_COLOR, ERROR_COLOR, RESET_COLOR, UserAbort
|
from .util import WARNING_COLOR, ERROR_COLOR, RESET_COLOR, UserAbort
|
||||||
|
from .util import get_journal_name
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
logging.getLogger("keyring.backend").setLevel(logging.ERROR)
|
logging.getLogger("keyring.backend").setLevel(logging.ERROR)
|
||||||
|
@ -38,12 +38,7 @@ def guess_mode(args, config):
|
||||||
"""Guesses the mode (compose, read or export) from the given arguments"""
|
"""Guesses the mode (compose, read or export) from the given arguments"""
|
||||||
compose = True
|
compose = True
|
||||||
export = False
|
export = False
|
||||||
import_ = False
|
if (
|
||||||
if args.import_ is not False:
|
|
||||||
compose = False
|
|
||||||
export = False
|
|
||||||
import_ = True
|
|
||||||
elif (
|
|
||||||
args.decrypt is not False
|
args.decrypt is not False
|
||||||
or args.encrypt is not False
|
or args.encrypt is not False
|
||||||
or args.export is not False
|
or args.export is not False
|
||||||
|
@ -70,7 +65,7 @@ def guess_mode(args, config):
|
||||||
# No date and only tags?
|
# No date and only tags?
|
||||||
compose = False
|
compose = False
|
||||||
|
|
||||||
return compose, export, import_
|
return compose, export
|
||||||
|
|
||||||
|
|
||||||
def encrypt(journal, filename=None):
|
def encrypt(journal, filename=None):
|
||||||
|
@ -139,7 +134,7 @@ Python 3.7 (or higher) soon.
|
||||||
if manual_args is None:
|
if manual_args is None:
|
||||||
manual_args = sys.argv[1:]
|
manual_args = sys.argv[1:]
|
||||||
|
|
||||||
args = parse_args_before_config(manual_args)
|
args = parse_args(manual_args)
|
||||||
configure_logger(args.debug)
|
configure_logger(args.debug)
|
||||||
|
|
||||||
# Run command if possible before config is available
|
# Run command if possible before config is available
|
||||||
|
@ -150,38 +145,24 @@ Python 3.7 (or higher) soon.
|
||||||
# Load the config
|
# Load the config
|
||||||
try:
|
try:
|
||||||
config = install.load_or_install_jrnl()
|
config = install.load_or_install_jrnl()
|
||||||
|
original_config = config.copy()
|
||||||
|
args = get_journal_name(args, config)
|
||||||
|
config = util.scope_config(config, args.journal_name)
|
||||||
except UserAbort as err:
|
except UserAbort as err:
|
||||||
print(f"\n{err}", file=sys.stderr)
|
print(f"\n{err}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Run command now that config is available
|
# Run post-config command now that config is ready
|
||||||
if callable(args.postconfig_cmd):
|
if callable(args.postconfig_cmd):
|
||||||
args.postconfig_cmd(config=config, args=args)
|
args.postconfig_cmd(args=args, config=config)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
args = parse_args_after_config(args, config)
|
# --- All the standalone commands are now done --- #
|
||||||
|
|
||||||
log.debug('Using configuration "%s"', config)
|
# Get the journal we're going to be working with
|
||||||
original_config = config.copy()
|
journal = open_journal(args.journal_name, config)
|
||||||
|
|
||||||
config = util.scope_config(config, args.journal_name)
|
mode_compose, mode_export = guess_mode(args, config)
|
||||||
|
|
||||||
log.debug('Using journal "%s"', args.journal_name)
|
|
||||||
|
|
||||||
mode_compose, mode_export, mode_import = guess_mode(args, config)
|
|
||||||
|
|
||||||
# How to quit writing?
|
|
||||||
if "win32" in sys.platform:
|
|
||||||
_exit_multiline_code = "on a blank line, press Ctrl+Z and then Enter"
|
|
||||||
else:
|
|
||||||
_exit_multiline_code = "press Ctrl+D"
|
|
||||||
|
|
||||||
# This is where we finally open the journal!
|
|
||||||
try:
|
|
||||||
journal = open_journal(args.journal_name, config)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("[Interrupted while opening journal]", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if mode_compose and not args.text:
|
if mode_compose and not args.text:
|
||||||
if not sys.stdin.isatty():
|
if not sys.stdin.isatty():
|
||||||
|
@ -201,25 +182,24 @@ Python 3.7 (or higher) soon.
|
||||||
raw = util.get_text_from_editor(config, template)
|
raw = util.get_text_from_editor(config, template)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
_how_to_quit = (
|
||||||
|
"Ctrl+z and then Enter" if "win32" in sys.platform else "Ctrl+d"
|
||||||
|
)
|
||||||
print(
|
print(
|
||||||
"[Compose Entry; " + _exit_multiline_code + " to finish writing]\n",
|
f"[Writing Entry; on a blank line, press {_how_to_quit} to finish writing]\n",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
raw = sys.stdin.read()
|
raw = sys.stdin.read()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("[Entry NOT saved to journal.]", file=sys.stderr)
|
print("[Entry NOT saved to journal]", file=sys.stderr)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
if raw:
|
if raw:
|
||||||
args.text = [raw]
|
args.text = [raw]
|
||||||
else:
|
else:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
# Import mode
|
|
||||||
if mode_import:
|
|
||||||
plugins.get_importer(args.import_).import_(journal, args.input)
|
|
||||||
|
|
||||||
# Writing mode
|
# Writing mode
|
||||||
elif mode_compose:
|
if mode_compose:
|
||||||
raw = " ".join(args.text).strip()
|
raw = " ".join(args.text).strip()
|
||||||
log.debug('Appending raw line "%s" to journal "%s"', raw, args.journal_name)
|
log.debug('Appending raw line "%s" to journal "%s"', raw, args.journal_name)
|
||||||
journal.new_entry(raw)
|
journal.new_entry(raw)
|
||||||
|
@ -242,7 +222,7 @@ Python 3.7 (or higher) soon.
|
||||||
journal.limit(args.limit)
|
journal.limit(args.limit)
|
||||||
|
|
||||||
# Reading mode
|
# Reading mode
|
||||||
if not mode_compose and not mode_export and not mode_import:
|
if not mode_compose and not mode_export:
|
||||||
print(journal.pprint())
|
print(journal.pprint())
|
||||||
|
|
||||||
# Various export modes
|
# Various export modes
|
||||||
|
|
|
@ -21,3 +21,14 @@ def postconfig_list(config, **kwargs):
|
||||||
from .util import list_journals
|
from .util import list_journals
|
||||||
|
|
||||||
print(list_journals(config))
|
print(list_journals(config))
|
||||||
|
|
||||||
|
|
||||||
|
def postconfig_import(args, config, **kwargs):
|
||||||
|
from .plugins import get_importer
|
||||||
|
from .Journal import open_journal
|
||||||
|
|
||||||
|
# Requires opening the journal
|
||||||
|
journal = open_journal(args.journal_name, config)
|
||||||
|
|
||||||
|
format = args.export if args.export else "jrnl"
|
||||||
|
get_importer(format).import_(journal, args.input)
|
||||||
|
|
|
@ -117,14 +117,15 @@ def load_or_install_jrnl():
|
||||||
upgrade_config(config)
|
upgrade_config(config)
|
||||||
verify_config(config)
|
verify_config(config)
|
||||||
|
|
||||||
return config
|
|
||||||
else:
|
else:
|
||||||
log.debug("Configuration file not found, installing jrnl...")
|
log.debug("Configuration file not found, installing jrnl...")
|
||||||
try:
|
try:
|
||||||
config = install()
|
config = install()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise UserAbort("Installation aborted")
|
raise UserAbort("Installation aborted")
|
||||||
return config
|
|
||||||
|
log.debug('Using configuration "%s"', config)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
def install():
|
def install():
|
||||||
|
|
|
@ -8,8 +8,8 @@ from .plugins import EXPORT_FORMATS
|
||||||
from .commands import preconfig_version
|
from .commands import preconfig_version
|
||||||
from .commands import preconfig_diagnostic
|
from .commands import preconfig_diagnostic
|
||||||
from .commands import postconfig_list
|
from .commands import postconfig_list
|
||||||
|
from .commands import postconfig_import
|
||||||
from .util import deprecated_cmd
|
from .util import deprecated_cmd
|
||||||
from .util import get_journal_name
|
|
||||||
|
|
||||||
|
|
||||||
class WrappingFormatter(argparse.RawDescriptionHelpFormatter):
|
class WrappingFormatter(argparse.RawDescriptionHelpFormatter):
|
||||||
|
@ -18,7 +18,7 @@ class WrappingFormatter(argparse.RawDescriptionHelpFormatter):
|
||||||
return textwrap.wrap(text, width=56)
|
return textwrap.wrap(text, width=56)
|
||||||
|
|
||||||
|
|
||||||
def parse_args_before_config(args=[]):
|
def parse_args(args=[]):
|
||||||
"""
|
"""
|
||||||
Argument parsing that is doable before the config is available.
|
Argument parsing that is doable before the config is available.
|
||||||
Everything else goes into "text" for later parsing.
|
Everything else goes into "text" for later parsing.
|
||||||
|
@ -27,7 +27,12 @@ def parse_args_before_config(args=[]):
|
||||||
formatter_class=WrappingFormatter,
|
formatter_class=WrappingFormatter,
|
||||||
add_help=False,
|
add_help=False,
|
||||||
description="The command-line note-taking and journaling app.",
|
description="The command-line note-taking and journaling app.",
|
||||||
epilog="",
|
epilog=textwrap.dedent(
|
||||||
|
"""
|
||||||
|
Thank you to all of our contributors! Come see the whole list of code and
|
||||||
|
financial contributors at https://github.com/jrnl-org/jrnl. And special
|
||||||
|
thanks to Bad Lip Reading for the Yoda joke in the Writing section above."""
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
optional = parser.add_argument_group("Optional Arguments")
|
optional = parser.add_argument_group("Optional Arguments")
|
||||||
|
@ -108,13 +113,11 @@ def parse_args_before_config(args=[]):
|
||||||
)
|
)
|
||||||
standalone.add_argument(
|
standalone.add_argument(
|
||||||
"--import",
|
"--import",
|
||||||
|
action="store_const",
|
||||||
metavar="TYPE",
|
metavar="TYPE",
|
||||||
dest="import_",
|
dest="postconfig_cmd",
|
||||||
choices=IMPORT_FORMATS,
|
const=postconfig_import,
|
||||||
help=f"Import entries into your journal. TYPE can be: {util.oxford_list(IMPORT_FORMATS)} (default: jrnl)",
|
help=f"Import entries into your journal. TYPE can be: {util.oxford_list(IMPORT_FORMATS)} (default: jrnl)",
|
||||||
default=False,
|
|
||||||
const="jrnl",
|
|
||||||
nargs="?",
|
|
||||||
)
|
)
|
||||||
standalone.add_argument(
|
standalone.add_argument(
|
||||||
"-i",
|
"-i",
|
||||||
|
@ -132,17 +135,17 @@ def parse_args_before_config(args=[]):
|
||||||
The date and the following colon ("yesterday:") are optional. If you leave
|
The date and the following colon ("yesterday:") are optional. If you leave
|
||||||
them out, "now" will be used:
|
them out, "now" will be used:
|
||||||
|
|
||||||
jrnl Then I rolled the log over, and underneath was a tiny little stick.
|
jrnl Then I rolled the log over.
|
||||||
|
|
||||||
Also, you can mark extra special entries ("star" them) with an asterisk:
|
Also, you can mark extra special entries ("star" them) with an asterisk:
|
||||||
|
|
||||||
jrnl *That log had a child!
|
jrnl *And underneath was a tiny little stick.
|
||||||
|
|
||||||
Please note that asterisks might be a special character in your shell, so you
|
Please note that asterisks might be a special character in your shell, so you
|
||||||
might have to escape them. When in doubt about escaping, put single quotes
|
might have to escape them. When in doubt about escaping, put quotes around
|
||||||
around your entire entry:
|
your entire entry:
|
||||||
|
|
||||||
jrnl 'saturday at 8pm: *Always pass on what you have learned. -Yoda'"""
|
jrnl "saturday at 2am: *Then I was like 'That log had a child!'" """
|
||||||
|
|
||||||
composing = parser.add_argument_group(
|
composing = parser.add_argument_group(
|
||||||
"Writing", textwrap.dedent(compose_msg).strip()
|
"Writing", textwrap.dedent(compose_msg).strip()
|
||||||
|
@ -270,11 +273,3 @@ def parse_args_before_config(args=[]):
|
||||||
|
|
||||||
# return parser.parse_args(args)
|
# return parser.parse_args(args)
|
||||||
return parser.parse_intermixed_args(args)
|
return parser.parse_intermixed_args(args)
|
||||||
|
|
||||||
|
|
||||||
def parse_args_after_config(args, config):
|
|
||||||
# print(str(args)) # @todo take this out
|
|
||||||
|
|
||||||
args = get_journal_name(args, config)
|
|
||||||
|
|
||||||
return args
|
|
||||||
|
|
|
@ -150,7 +150,6 @@ def scope_config(config, journal_name):
|
||||||
else:
|
else:
|
||||||
# But also just give them a string to point to the journal file
|
# But also just give them a string to point to the journal file
|
||||||
config["journal"] = journal_conf
|
config["journal"] = journal_conf
|
||||||
config.pop("journals")
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
@ -335,4 +334,5 @@ def get_journal_name(args, config):
|
||||||
print(list_journals(config), file=sys.stderr)
|
print(list_journals(config), file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
log.debug("Using journal name: %s", args.journal_name)
|
||||||
return args
|
return args
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from jrnl.cli import parse_args_before_config
|
from jrnl.cli import parse_args
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import shlex
|
import shlex
|
||||||
|
@ -6,7 +6,7 @@ import shlex
|
||||||
|
|
||||||
def cli_as_dict(str):
|
def cli_as_dict(str):
|
||||||
cli = shlex.split(str)
|
cli = shlex.split(str)
|
||||||
args = parse_args_before_config(cli)
|
args = parse_args(cli)
|
||||||
return vars(args)
|
return vars(args)
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ def expected_args(**kwargs):
|
||||||
"end_date": None,
|
"end_date": None,
|
||||||
"excluded": [],
|
"excluded": [],
|
||||||
"export": False,
|
"export": False,
|
||||||
"import_": False,
|
|
||||||
"input": False,
|
"input": False,
|
||||||
"limit": None,
|
"limit": None,
|
||||||
"on_date": None,
|
"on_date": None,
|
||||||
|
@ -106,11 +105,9 @@ def test_export_alone():
|
||||||
|
|
||||||
|
|
||||||
def test_import_alone():
|
def test_import_alone():
|
||||||
assert cli_as_dict("--import jrnl") == expected_args(import_="jrnl")
|
from jrnl.commands import postconfig_import
|
||||||
|
|
||||||
|
assert cli_as_dict("--import") == expected_args(postconfig_cmd=postconfig_import)
|
||||||
def test_import_defaults_to_jrnl():
|
|
||||||
assert cli_as_dict("--import") == expected_args(import_="jrnl")
|
|
||||||
|
|
||||||
|
|
||||||
def test_input_flag_alone():
|
def test_input_flag_alone():
|
||||||
|
|
Loading…
Add table
Reference in a new issue