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 import sys
from . import install, plugins, util from . import install, plugins, util
from .commands import list_journals from .util import list_journals
from .parsing import parse_args_before_config from .parsing import parse_args_before_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
@ -148,7 +148,7 @@ Python 3.7 (or higher) soon.
configure_logger(args.debug) configure_logger(args.debug)
# Run command if possible before config is available # Run command if possible before config is available
if args.preconfig_cmd is not None: if callable(args.preconfig_cmd):
args.preconfig_cmd(args) args.preconfig_cmd(args)
sys.exit(0) sys.exit(0)
@ -160,7 +160,7 @@ Python 3.7 (or higher) soon.
sys.exit(1) sys.exit(1)
# Run command now that config is available # 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) args.postconfig_cmd(config=config, args=args)
sys.exit(0) 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(_): def preconfig_diagnostic(_):
import platform import platform
import sys import sys
@ -31,22 +17,7 @@ def preconfig_version(_):
print(version_str) print(version_str)
def preconfig_command(args):
print("this is a pre-config command")
def postconfig_list(config, **kwargs): def postconfig_list(config, **kwargs):
from .util import list_journals
print(list_journals(config)) 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 argparse
import re 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_version
from .commands import preconfig_diagnostic from .commands import preconfig_diagnostic
from .commands import postconfig_list 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): 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. 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.
""" """
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser(
parser.add_argument( formatter_class=WrappingFormatter,
"-v", 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", "--version",
action="store_const", action="store_const",
const=preconfig_version, const=preconfig_version,
dest="preconfig_cmd", dest="preconfig_cmd",
help="prints version information and exits", help="prints version information",
) )
standalone.add_argument(
parser.add_argument( "-v",
"--cmd1",
action="store_const", action="store_const",
const=lambda: print("cmd1"), const=preconfig_version,
dest="preconfig_cmd", dest="preconfig_cmd",
help=argparse.SUPPRESS,
) )
parser.add_argument( standalone.add_argument(
"--diagnostic", "--diagnostic",
action="store_const", action="store_const",
const=preconfig_diagnostic, const=preconfig_diagnostic,
dest="preconfig_cmd", dest="preconfig_cmd",
help="outputs diagnostic information and exits", help=argparse.SUPPRESS,
) )
standalone.add_argument(
parser.add_argument(
"--ls",
"--list", "--list",
action="store_const", action="store_const",
const=postconfig_list, const=postconfig_list,
dest="postconfig_cmd", dest="postconfig_cmd",
help="lists all configured journals", help="list all configured journals",
) )
standalone.add_argument(
parser.add_argument( "--ls",
action="store_const",
const=postconfig_list,
dest="postconfig_cmd",
help=argparse.SUPPRESS,
)
standalone.add_argument(
"-ls", "-ls",
action="store_const", action="store_const",
const=lambda **kwargs: deprecated_cmd( const=lambda **kwargs: deprecated_cmd(
"-ls", "--ls or --list", callback=postconfig_list, **kwargs "-ls", "--list or --ls", callback=postconfig_list, **kwargs
), ),
dest="postconfig_cmd", dest="postconfig_cmd",
help="displays accessible journals", help=argparse.SUPPRESS,
) )
standalone.add_argument(
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(
"--encrypt", "--encrypt",
metavar="FILENAME", metavar="FILENAME",
dest="encrypt", dest="encrypt",
@ -180,7 +96,7 @@ def parse_args_before_config(args=None):
default=False, default=False,
const=None, const=None,
) )
exporting.add_argument( standalone.add_argument(
"--decrypt", "--decrypt",
metavar="FILENAME", metavar="FILENAME",
dest="decrypt", dest="decrypt",
@ -189,22 +105,163 @@ def parse_args_before_config(args=None):
default=False, default=False,
const=None, 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( exporting.add_argument(
"--edit", "--edit",
dest="edit", dest="edit",
help="Opens your editor to edit the selected entries.", help="Opens the selected entries in your configured editor",
action="store_true", action="store_true",
) )
exporting.add_argument( exporting.add_argument(
"--delete", "--delete",
dest="delete", dest="delete",
action="store_true", 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: if not args:
args = [] args = []

View file

@ -289,3 +289,35 @@ def split_title(text):
if not sep: if not sep:
return text, "" return text, ""
return text[: sep.end()].strip(), text[sep.end() :].strip() 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 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(cli) args = parse_args_before_config(cli)
return vars(args) return vars(args)
@ -48,7 +48,6 @@ def test_contains_alone():
def test_debug_alone(): def test_debug_alone():
assert cli_as_dict("--debug") == expected_args(debug=True) assert cli_as_dict("--debug") == expected_args(debug=True)
assert cli_as_dict("-d") == expected_args(debug=True)
def test_delete_alone(): def test_delete_alone():