Update more arg parsing options and handling

This commit is contained in:
Jonathan Wren 2020-07-02 20:35:17 -07:00
parent c98d01bb8b
commit 22db905e3d
5 changed files with 240 additions and 181 deletions

View file

@ -25,7 +25,7 @@ import platform
import sys
from . import install, plugins, util
from .commands import list_journals
from .util import list_journals
from .parsing import parse_args_before_config
from .Journal import PlainJournal, open_journal
from .util import WARNING_COLOR, ERROR_COLOR, RESET_COLOR, UserAbort
@ -148,7 +148,7 @@ Python 3.7 (or higher) soon.
configure_logger(args.debug)
# Run command if possible before config is available
if args.preconfig_cmd is not None:
if callable(args.preconfig_cmd):
args.preconfig_cmd(args)
sys.exit(0)
@ -160,7 +160,7 @@ Python 3.7 (or higher) soon.
sys.exit(1)
# Run command now that config is available
if args.postconfig_cmd is not None:
if callable(args.postconfig_cmd):
args.postconfig_cmd(config=config, args=args)
sys.exit(0)

View file

@ -1,17 +1,3 @@
def deprecated_cmd(old_cmd, new_cmd, callback, **kwargs):
import sys
from .util import RESET_COLOR, WARNING_COLOR
print(
WARNING_COLOR,
f"\nThe command {old_cmd} is deprecated and will be removed from jrnl soon.\n"
f"Please use {new_cmd} instead.\n",
RESET_COLOR,
file=sys.stderr,
)
callback(**kwargs)
def preconfig_diagnostic(_):
import platform
import sys
@ -31,22 +17,7 @@ def preconfig_version(_):
print(version_str)
def preconfig_command(args):
print("this is a pre-config command")
def postconfig_list(config, **kwargs):
from .util import list_journals
print(list_journals(config))
def list_journals(config):
from . import install
"""List the journals specified in the configuration file"""
result = f"Journals defined in {install.CONFIG_FILE_PATH}\n"
ml = min(max(len(k) for k in config["journals"]), 20)
for journal, cfg in config["journals"].items():
result += " * {:{}} -> {}\n".format(
journal, ml, cfg["journal"] if isinstance(cfg, dict) else cfg
)
return result

View file

@ -1,11 +1,20 @@
import argparse
import re
import textwrap
from . import plugins
from .plugins import util
from .plugins import IMPORT_FORMATS
from .plugins import EXPORT_FORMATS
from .commands import preconfig_version
from .commands import preconfig_diagnostic
from .commands import postconfig_list
from .commands import deprecated_cmd
from .util import deprecated_cmd
class WrappingFormatter(argparse.RawDescriptionHelpFormatter):
def _split_lines(self, text, width):
text = self._whitespace_matcher.sub(" ", text).strip()
return textwrap.wrap(text, width=56)
def parse_args_before_config(args=None):
@ -13,165 +22,72 @@ def parse_args_before_config(args=None):
Argument parsing that is doable before the config is available.
Everything else goes into "text" for later parsing.
"""
parser = argparse.ArgumentParser()
parser.add_argument(
"-v",
parser = argparse.ArgumentParser(
formatter_class=WrappingFormatter,
add_help=False,
description="The command-line note-taking and journaling app.",
epilog="",
)
optional = parser.add_argument_group("Optional Arguments")
optional.add_argument(
"--debug",
dest="debug",
action="store_true",
help="Print information useful for troubleshooting",
)
standalone = parser.add_argument_group(
"Standalone Commands",
"These commands will exit after they complete. You may only run one at a time.",
)
standalone.add_argument("--help", action="help", help="Show this help message")
standalone.add_argument("-h", action="help", help=argparse.SUPPRESS)
standalone.add_argument(
"--version",
action="store_const",
const=preconfig_version,
dest="preconfig_cmd",
help="prints version information and exits",
help="prints version information",
)
parser.add_argument(
"--cmd1",
standalone.add_argument(
"-v",
action="store_const",
const=lambda: print("cmd1"),
const=preconfig_version,
dest="preconfig_cmd",
help=argparse.SUPPRESS,
)
parser.add_argument(
standalone.add_argument(
"--diagnostic",
action="store_const",
const=preconfig_diagnostic,
dest="preconfig_cmd",
help="outputs diagnostic information and exits",
help=argparse.SUPPRESS,
)
parser.add_argument(
"--ls",
standalone.add_argument(
"--list",
action="store_const",
const=postconfig_list,
dest="postconfig_cmd",
help="lists all configured journals",
help="list all configured journals",
)
parser.add_argument(
standalone.add_argument(
"--ls",
action="store_const",
const=postconfig_list,
dest="postconfig_cmd",
help=argparse.SUPPRESS,
)
standalone.add_argument(
"-ls",
action="store_const",
const=lambda **kwargs: deprecated_cmd(
"-ls", "--ls or --list", callback=postconfig_list, **kwargs
"-ls", "--list or --ls", callback=postconfig_list, **kwargs
),
dest="postconfig_cmd",
help="displays accessible journals",
help=argparse.SUPPRESS,
)
parser.add_argument(
"-d", "--debug", dest="debug", action="store_true", help="execute in debug mode"
)
composing = parser.add_argument_group(
"Composing",
'To write an entry simply write it on the command line, e.g. "jrnl yesterday at 1pm: Went to the gym."',
)
reading = parser.add_argument_group(
"Reading",
"Specifying either of these parameters will display posts of your journal",
)
reading.add_argument(
"-from", dest="start_date", metavar="DATE", help="View entries after this date"
)
reading.add_argument(
"-until",
"-to",
dest="end_date",
metavar="DATE",
help="View entries before this date",
)
reading.add_argument(
"-contains", dest="contains", help="View entries containing a specific string"
)
reading.add_argument(
"-on", dest="on_date", metavar="DATE", help="View entries on this date"
)
reading.add_argument(
"-and",
dest="strict",
action="store_true",
help="Filter by tags using AND (default: OR)",
)
reading.add_argument(
"-starred",
dest="starred",
action="store_true",
help="Show only starred entries",
)
reading.add_argument(
"-n",
dest="limit",
default=None,
metavar="N",
help="Shows the last n entries matching the filter. '-n 3' and '-3' have the same effect.",
nargs="?",
type=int,
)
reading.add_argument(
"-not",
dest="excluded",
nargs="?",
default=[],
metavar="E",
action="append",
help="Exclude entries with these tags",
)
exporting = parser.add_argument_group(
"Export / Import", "Options for transmogrifying your journal"
)
exporting.add_argument(
"-s",
"--short",
dest="short",
action="store_true",
help="Show only titles or line containing the search tags",
)
exporting.add_argument(
"--tags",
dest="tags",
action="store_true",
help="Returns a list of all tags and number of occurences",
)
exporting.add_argument(
"--export",
metavar="TYPE",
dest="export",
choices=plugins.EXPORT_FORMATS,
help="Export your journal. TYPE can be {}.".format(
plugins.util.oxford_list(plugins.EXPORT_FORMATS)
),
default=False,
const=None,
)
exporting.add_argument(
"-o",
metavar="OUTPUT",
dest="output",
help="Optionally specifies output file when using --export. If OUTPUT is a directory, exports each entry into an individual file instead.",
default=False,
const=None,
)
exporting.add_argument(
"--import",
metavar="TYPE",
dest="import_",
choices=plugins.IMPORT_FORMATS,
help="Import entries into your journal. TYPE can be {}, and it defaults to jrnl if nothing else is specified.".format(
plugins.util.oxford_list(plugins.IMPORT_FORMATS)
),
default=False,
const="jrnl",
nargs="?",
)
exporting.add_argument(
"-i",
metavar="INPUT",
dest="input",
help="Optionally specifies input file when using --import.",
default=False,
const=None,
)
exporting.add_argument(
standalone.add_argument(
"--encrypt",
metavar="FILENAME",
dest="encrypt",
@ -180,7 +96,7 @@ def parse_args_before_config(args=None):
default=False,
const=None,
)
exporting.add_argument(
standalone.add_argument(
"--decrypt",
metavar="FILENAME",
dest="decrypt",
@ -189,22 +105,163 @@ def parse_args_before_config(args=None):
default=False,
const=None,
)
standalone.add_argument(
"--import",
metavar="TYPE",
dest="import_",
choices=IMPORT_FORMATS,
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(
"-i",
metavar="FILENAME",
dest="input",
help="Optionally specifies input file when using --import.",
default=False,
const=None,
)
compose_msg = """ To add a new entry into your journal, simply write it on the command line:
jrnl yesterday: I was walking and I found this big log.
The date and the following colon ("yesterday:") are optional. If you leave
them out, "now" will be used:
jrnl Then I rolled the log over, and underneath was a tiny little stick.
Also, you can mark extra special entries ("star" them) with an asterisk:
jrnl *That log had a child!
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
around your entire entry:
jrnl 'saturday at 8pm: *Always pass on what you have learned. -Yoda'"""
composing = parser.add_argument_group(
"Writing", textwrap.dedent(compose_msg).strip()
)
composing.add_argument("text", metavar="", nargs="*")
read_msg = (
"To find entries from your journal, use any combination of the below filters."
)
reading = parser.add_argument_group("Searching", textwrap.dedent(read_msg))
reading.add_argument(
"-on", dest="on_date", metavar="DATE", help="Show entries on this date"
)
reading.add_argument(
"-from",
dest="start_date",
metavar="DATE",
help="Show entries after, or on, this date",
)
reading.add_argument(
"-to",
dest="end_date",
metavar="DATE",
help="Show entries before, or on, this date (alias: -until)",
)
reading.add_argument(
"-until", dest="end_date", help=argparse.SUPPRESS,
)
reading.add_argument(
"-contains",
dest="contains",
metavar="TEXT",
help="Show entries containing specific text (put quotes around text with spaces)",
)
reading.add_argument(
"-and",
dest="strict",
action="store_true",
help='Show only entries that match all conditions, like saying "x AND y" (default: OR)',
)
reading.add_argument(
"-starred",
dest="starred",
action="store_true",
help="Show only starred entries (marked with *)",
)
reading.add_argument(
"-n",
dest="limit",
default=None,
metavar="NUMBER",
help="Show a maximum of NUMBER entries (note: '-n 3' and '-3' have the same effect)",
nargs="?",
type=int,
)
reading.add_argument(
"-not",
dest="excluded",
nargs="?",
default=[],
metavar="TAG",
action="append",
help="Exclude entries with this tag",
)
search_options_msg = """ These help you do various tasks with the selected entries from your search.
If used on their own (with no search), they will act on your entire journal"""
exporting = parser.add_argument_group(
"Options for Searching", textwrap.dedent(search_options_msg)
)
exporting.add_argument(
"--edit",
dest="edit",
help="Opens your editor to edit the selected entries.",
help="Opens the selected entries in your configured editor",
action="store_true",
)
exporting.add_argument(
"--delete",
dest="delete",
action="store_true",
help="Opens an interactive interface for deleting entries.",
help="Interactively deletes selected entries",
)
exporting.add_argument(
"--format",
metavar="TYPE",
dest="export",
choices=EXPORT_FORMATS,
help=f"Display selected entries in an alternate format (other than jrnl). TYPE can be: {util.oxford_list(EXPORT_FORMATS)}.",
default=False,
)
exporting.add_argument(
"--export",
metavar="TYPE",
dest="export",
choices=EXPORT_FORMATS,
help=argparse.SUPPRESS,
)
exporting.add_argument(
"--tags",
dest="tags",
action="store_true",
help="Alias for '--format tags'. Returns a list of all tags and number of occurences",
)
exporting.add_argument(
"--short",
dest="short",
action="store_true",
help="Show only titles or line containing the search tags",
)
exporting.add_argument(
"-s", dest="short", action="store_true", help=argparse.SUPPRESS,
)
exporting.add_argument(
"-o",
metavar="FILENAME",
dest="output",
help="Optionally specifies output file (or directory) when using --format.",
default=False,
const=None,
)
# Everything else
composing.add_argument("text", metavar="", nargs="*")
if not args:
args = []

View file

@ -289,3 +289,35 @@ def split_title(text):
if not sep:
return text, ""
return text[: sep.end()].strip(), text[sep.end() :].strip()
def deprecated_cmd(old_cmd, new_cmd, callback=None, **kwargs):
import sys
import textwrap
from .util import RESET_COLOR, WARNING_COLOR
log = logging.getLogger(__name__)
warning_msg = f"""
The command {old_cmd} is deprecated and will be removed from jrnl soon.
Please us {new_cmd} instead.
"""
warning_msg = textwrap.dedent(warning_msg)
log.warning(warning_msg)
print(f"{WARNING_COLOR}{warning_msg}{RESET_COLOR}", file=sys.stderr)
if callback is not None:
callback(**kwargs)
def list_journals(config):
from . import install
"""List the journals specified in the configuration file"""
result = f"Journals defined in {install.CONFIG_FILE_PATH}\n"
ml = min(max(len(k) for k in config["journals"]), 20)
for journal, cfg in config["journals"].items():
result += " * {:{}} -> {}\n".format(
journal, ml, cfg["journal"] if isinstance(cfg, dict) else cfg
)
return result

View file

@ -1,4 +1,4 @@
from jrnl.cli import parse_args_before_config as parse_args
from jrnl.cli import parse_args_before_config
import pytest
import shlex
@ -6,7 +6,7 @@ import shlex
def cli_as_dict(str):
cli = shlex.split(str)
args = parse_args(cli)
args = parse_args_before_config(cli)
return vars(args)
@ -48,7 +48,6 @@ def test_contains_alone():
def test_debug_alone():
assert cli_as_dict("--debug") == expected_args(debug=True)
assert cli_as_dict("-d") == expected_args(debug=True)
def test_delete_alone():