Reformat additional messages and finish centralizing exception handling (#1424)

* Update and modularize exception handling

cc #1024 #1141

- Stack traces are no longer shown to users unless the --debug flag is
  being used
- Errors, warnings, and other messages contain color as needed
- Converted error messages to Enum
- Adds print_msg function to centralize output (this should replace all
  other output in other modules)

Co-authored-by: Micah Jerome Ellison <micah.jerome.ellison@gmail.com>

* format with black

* add message to catch-all exception block

* Unskip some tests (#1399)

* remove skip_editor test and tag

* remove useless test

* unskip blank input test

* formatting

* rename test so it doesn't overwrite other test

* unskip some dayone tests that now work

* Bump ipython from 7.28.0 to 7.31.1 (#1401)

Bumps [ipython](https://github.com/ipython/ipython) from 7.28.0 to 7.31.1.
- [Release notes](https://github.com/ipython/ipython/releases)
- [Commits](https://github.com/ipython/ipython/compare/7.28.0...7.31.1)

---
updated-dependencies:
- dependency-name: ipython
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update changelog [ci skip]

* Bump asteval from 0.9.25 to 0.9.26 (#1400)

Bumps [asteval](https://github.com/newville/asteval) from 0.9.25 to 0.9.26.
- [Release notes](https://github.com/newville/asteval/releases)
- [Commits](https://github.com/newville/asteval/compare/0.9.25...0.9.26)

---
updated-dependencies:
- dependency-name: asteval
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update changelog [ci skip]

* Bump black from 21.12b0 to 22.1.0 (#1404)

* Bump black from 21.12b0 to 22.1.0

Bumps [black](https://github.com/psf/black) from 21.12b0 to 22.1.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/commits/22.1.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

* Run make format

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Micah Jerome Ellison <micah.jerome.ellison@gmail.com>

* Update changelog [ci skip]

* Add reference documentation to docs site and separate out "Tips and Tricks" and "External Editors" from "Recipes" (#1332)

* First draft of command line reference, mostly pulled from help screen

* Add first draft of config file reference, mostly pulled from advanced.md

* Clean up config file doc for readability

* Add --config-file and remove examples from CLI reference

* Add warning about time zone in timeformat

* More small changes, and adding template config keyword

* Cleaning up and re-ordering config file reference

* Clean up reference and anything else from advanced documentation that can live elsewhere and linking to config file reference wherever config file is mentioned

* Fix syntax highlighting in command line reference, clean up content a bit, include --diagnostic

* Mention version config key

* Apply minor changes suggested in PR review

* Rename "recipes" to "Tips and Tricks", pull "External Editors" out of it into its own page, and redirect old recipes link to tips-and-tricks

* Revert broken mkdocs-redirects usage from last commit

* Update changelog [ci skip]

* Add --co alias for --config-override (#1397)

* Add hash as a default tag symbol (#1398)

* Update changelog [ci skip]

* Increment version to v2.8.4-beta2

* Update changelog [ci skip]

* Increment version to v2.8.4

* Update changelog [ci skip]

* Bump pytest from 6.2.5 to 7.0.0 (#1407)

Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.5 to 7.0.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.2.5...7.0.0)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update changelog [ci skip]

* Drop support for Python 3.7 and 3.8 (#1412)

* Remove Python 3.7 and 3.8 from github actions workflows

* Update lockfile after running poetry update a couple times

* Update poetry lock

* Remove Python 3.7 and 3.8 from pyproject.toml and run poetry lock

* Update changelog [ci skip]

* Tidy up git ignore (#1414)

* cleaned gitignore and add comments

* removed colon for readbility

* alphabetize files in sections

Co-authored-by: nelnog <nel.nogales@gmail.com>

* fix behavior that was confusing pytest

* update test to match new message

* whitespace change

* clean up error for manually stopping the inline editor

* udpate error to use new exception handling

* move some exceptions and errors to the new exception handling

* add line breaks to keyboard interrupt so it looks more like other exceptions

* add handling for exceptions that happen earlier in the flow

* add new 'NothingToDelete' error to replace old behavior

* get rid of old exception

* add new exception handling to 'nothing saved to file' errors

* move exception for no editor configured into new handling

* move exception for no alt config to new handling

* get rid of old exception handling for encrypted journal

* Move error for too many wrong passwords into new handling

* fix merge errors

* replace sys.exit call with new exception handling

* replace sys.exit call with new exception handling

* replace sys.exit call with new exception handling

* reformat with black

* clean up old code

* clean up old code

* clean up linting issue

* update uncaught exception for new handling

* update test

* fix mangled lock file

Co-authored-by: Micah Jerome Ellison <micah.jerome.ellison@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jrnl Bot <jrnl.bot@gmail.com>
Co-authored-by: Nelson <35701520+nelnog@users.noreply.github.com>
Co-authored-by: nelnog <nel.nogales@gmail.com>
This commit is contained in:
Jonathan Wren 2022-03-19 12:30:23 -07:00 committed by GitHub
parent a1117918dd
commit bc42f74b2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 204 additions and 153 deletions

View file

@ -21,6 +21,11 @@ from .Journal import Journal
from .Journal import LegacyJournal from .Journal import LegacyJournal
from .prompt import create_password from .prompt import create_password
from jrnl.exception import JrnlException
from jrnl.messages import Message
from jrnl.messages import MsgText
from jrnl.messages import MsgType
def make_key(password): def make_key(password):
password = password.encode("utf-8") password = password.encode("utf-8")
@ -53,11 +58,11 @@ def decrypt_content(
password = getpass.getpass() password = getpass.getpass()
result = decrypt_func(password) result = decrypt_func(password)
attempt += 1 attempt += 1
if result is not None:
if result is None:
raise JrnlException(Message(MsgText.PasswordMaxTriesExceeded, MsgType.ERROR))
return result return result
else:
print("Extremely wrong password.", file=sys.stderr)
sys.exit(1)
class EncryptedJournal(Journal): class EncryptedJournal(Journal):
@ -121,15 +126,11 @@ class EncryptedJournal(Journal):
@classmethod @classmethod
def from_journal(cls, other: Journal): def from_journal(cls, other: Journal):
new_journal = super().from_journal(other) new_journal = super().from_journal(other)
try:
new_journal.password = ( new_journal.password = (
other.password other.password
if hasattr(other, "password") if hasattr(other, "password")
else create_password(other.name) else create_password(other.name)
) )
except KeyboardInterrupt:
print("[Interrupted while creating new journal]", file=sys.stderr)
sys.exit(1)
return new_journal return new_journal

View file

@ -431,13 +431,6 @@ def open_journal(journal_name, config, legacy=False):
from . import EncryptedJournal from . import EncryptedJournal
try:
if legacy: if legacy:
return EncryptedJournal.LegacyEncryptedJournal( return EncryptedJournal.LegacyEncryptedJournal(journal_name, **config).open()
journal_name, **config
).open()
return EncryptedJournal.EncryptedJournal(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)

View file

@ -5,9 +5,10 @@ import logging
import sys import sys
import traceback import traceback
from jrnl.jrnl import run from .jrnl import run
from jrnl.args import parse_args from .args import parse_args
from jrnl.output import print_msg from jrnl.output import print_msg
from jrnl.exception import JrnlException from jrnl.exception import JrnlException
from jrnl.messages import Message from jrnl.messages import Message
from jrnl.messages import MsgText from jrnl.messages import MsgText
@ -36,25 +37,40 @@ def cli(manual_args=None):
configure_logger(args.debug) configure_logger(args.debug)
logging.debug("Parsed args: %s", args) logging.debug("Parsed args: %s", args)
return run(args) status_code = run(args)
except JrnlException as e: except JrnlException as e:
status_code = 1
e.print() e.print()
return 1
except KeyboardInterrupt: except KeyboardInterrupt:
print_msg(Message(MsgText.KeyboardInterruptMsg, MsgType.WARNING)) status_code = 1
return 1 print_msg("\nKeyboardInterrupt", "\nAborted by user", msg=Message.ERROR)
except Exception as e: except Exception as e:
# uncaught exception
status_code = 1
debug = False
try: try:
is_debug = args.debug # type: ignore if args.debug: # type: ignore
debug = True
except NameError: except NameError:
# error happened before args were parsed # This should only happen when the exception
is_debug = "--debug" in sys.argv[1:] # happened before the args were parsed
if "--debug" in sys.argv:
debug = True
if is_debug: if debug:
print("\n")
traceback.print_tb(sys.exc_info()[2]) traceback.print_tb(sys.exc_info()[2])
print_msg(Message(MsgText.UncaughtException, MsgType.ERROR, {"exception": e})) print_msg(
return 1 Message(
MsgText.UncaughtException,
MsgType.ERROR,
{"name": type(e).__name__, "exception": e},
)
)
# This should be the only exit point
return status_code

View file

@ -197,9 +197,13 @@ def get_journal_name(args, config):
args.text = args.text[1:] args.text = args.text[1:]
if args.journal_name not in config["journals"]: if args.journal_name not in config["journals"]:
print("No default journal configured.", file=sys.stderr) raise JrnlException(
print(list_journals(config), file=sys.stderr) Message(
sys.exit(1) MsgText.NoDefaultJournal,
MsgType.ERROR,
{"journals": list_journals(config)},
),
)
logging.debug("Using journal name: %s", args.journal_name) logging.debug("Using journal name: %s", args.journal_name)
return args return args

View file

@ -3,11 +3,8 @@ import os
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import textwrap
from pathlib import Path from pathlib import Path
from jrnl.color import ERROR_COLOR
from jrnl.color import RESET_COLOR
from jrnl.os_compat import on_windows from jrnl.os_compat import on_windows
from jrnl.os_compat import split_args from jrnl.os_compat import split_args
from jrnl.output import print_msg from jrnl.output import print_msg
@ -32,22 +29,21 @@ def get_text_from_editor(config, template=""):
try: try:
subprocess.call(split_args(config["editor"]) + [tmpfile]) subprocess.call(split_args(config["editor"]) + [tmpfile])
except FileNotFoundError as e: except FileNotFoundError:
error_msg = f""" raise JrnlException(
{ERROR_COLOR}{str(e)}{RESET_COLOR} Message(
MsgText.EditorMisconfigured,
Please check the 'editor' key in your config file for errors: MsgType.ERROR,
{repr(config['editor'])} {"editor_key": config["editor"]},
""" )
print(textwrap.dedent(error_msg).strip(), file=sys.stderr) )
exit(1)
with open(tmpfile, "r", encoding="utf-8") as f: with open(tmpfile, "r", encoding="utf-8") as f:
raw = f.read() raw = f.read()
os.remove(tmpfile) os.remove(tmpfile)
if not raw: if not raw:
print("[Nothing saved to file]", file=sys.stderr) raise JrnlException(Message(MsgText.NoTextReceived, MsgType.ERROR))
return raw return raw

View file

@ -4,16 +4,6 @@ from jrnl.messages import Message
from jrnl.output import print_msg from jrnl.output import print_msg
class UserAbort(Exception):
pass
class UpgradeValidationException(Exception):
"""Raised when the contents of an upgraded journal do not match the old journal"""
pass
class JrnlException(Exception): class JrnlException(Exception):
"""Common exceptions raised by jrnl.""" """Common exceptions raised by jrnl."""

View file

@ -14,10 +14,14 @@ from .config import get_default_journal_path
from .config import load_config from .config import load_config
from .config import save_config from .config import save_config
from .config import verify_config_colors from .config import verify_config_colors
from .exception import UserAbort
from .prompt import yesno from .prompt import yesno
from .upgrade import is_old_version from .upgrade import is_old_version
from jrnl.exception import JrnlException
from jrnl.messages import Message
from jrnl.messages import MsgText
from jrnl.messages import MsgType
def upgrade_config(config_data, alt_config_path=None): def upgrade_config(config_data, alt_config_path=None):
"""Checks if there are keys missing in a given config dict, and if so, updates the config file accordingly. """Checks if there are keys missing in a given config dict, and if so, updates the config file accordingly.
@ -47,14 +51,14 @@ def find_default_config():
def find_alt_config(alt_config): def find_alt_config(alt_config):
if os.path.exists(alt_config): if not os.path.exists(alt_config):
return alt_config raise JrnlException(
else: Message(
print( MsgText.AltConfigNotFound, MsgType.ERROR, {"config_file": alt_config}
"Alternate configuration file not found at path specified.", file=sys.stderr
) )
print("Exiting.", file=sys.stderr) )
sys.exit(1)
return alt_config
def load_or_install_jrnl(alt_config_path): def load_or_install_jrnl(alt_config_path):
@ -72,32 +76,16 @@ def load_or_install_jrnl(alt_config_path):
config = load_config(config_path) config = load_config(config_path)
if is_old_version(config_path): if is_old_version(config_path):
from . import upgrade from jrnl import upgrade
try:
upgrade.upgrade_jrnl(config_path) upgrade.upgrade_jrnl(config_path)
except upgrade.UpgradeValidationException:
print("Aborting upgrade.", file=sys.stderr)
print(
"Please tell us about this problem at the following URL:",
file=sys.stderr,
)
print(
"https://github.com/jrnl-org/jrnl/issues/new?title=UpgradeValidationException",
file=sys.stderr,
)
print("Exiting.", file=sys.stderr)
sys.exit(1)
upgrade_config(config, alt_config_path) upgrade_config(config, alt_config_path)
verify_config_colors(config) verify_config_colors(config)
else: else:
logging.debug("Configuration file not found, installing jrnl...") logging.debug("Configuration file not found, installing jrnl...")
try:
config = install() config = install()
except KeyboardInterrupt:
raise UserAbort("Installation aborted")
logging.debug('Using configuration "%s"', config) logging.debug('Using configuration "%s"', config)
return config return config

View file

@ -7,17 +7,19 @@ import sys
from . import install from . import install
from . import plugins from . import plugins
from .Journal import open_journal from .Journal import open_journal
from .color import ERROR_COLOR
from .color import RESET_COLOR
from .config import get_journal_name from .config import get_journal_name
from .config import scope_config from .config import scope_config
from .config import get_config_path from .config import get_config_path
from .editor import get_text_from_editor from .editor import get_text_from_editor
from .editor import get_text_from_stdin from .editor import get_text_from_stdin
from .exception import UserAbort
from . import time from . import time
from .override import apply_overrides from .override import apply_overrides
from jrnl.exception import JrnlException
from jrnl.messages import Message
from jrnl.messages import MsgText
from jrnl.messages import MsgType
def run(args): def run(args):
""" """
@ -35,7 +37,6 @@ def run(args):
return args.preconfig_cmd(args) return args.preconfig_cmd(args)
# Load the config, and extract journal name # Load the config, and extract journal name
try:
config = install.load_or_install_jrnl(args.config_file_path) config = install.load_or_install_jrnl(args.config_file_path)
original_config = config.copy() original_config = config.copy()
@ -44,9 +45,6 @@ def run(args):
args = get_journal_name(args, config) args = get_journal_name(args, config)
config = scope_config(config, args.journal_name) config = scope_config(config, args.journal_name)
except UserAbort as err:
print(f"\n{err}", file=sys.stderr)
sys.exit(1)
# Run post-config command now that config is ready # Run post-config command now that config is ready
if callable(args.postconfig_cmd): if callable(args.postconfig_cmd):
@ -138,7 +136,9 @@ def write_mode(args, config, journal, **kwargs):
if not raw: if not raw:
logging.error("Write mode: couldn't get raw text") logging.error("Write mode: couldn't get raw text")
sys.exit() raise JrnlException(
Message(MsgText.JrnlExceptionMessage.NoTextReceived, MsgType.ERROR)
)
logging.debug( logging.debug(
'Write mode: appending raw text to journal "%s": %s', args.journal_name, raw 'Write mode: appending raw text to journal "%s": %s', args.journal_name, raw
@ -202,11 +202,13 @@ def _get_editor_template(config, **kwargs):
logging.debug("Write mode: template loaded: %s", template) logging.debug("Write mode: template loaded: %s", template)
except OSError: except OSError:
logging.error("Write mode: template not loaded") logging.error("Write mode: template not loaded")
print( raise JrnlException(
f"[Could not read template at '{config['template']}']", Message(
file=sys.stderr, MsgText.CantReadTemplate,
MsgType.ERROR,
{"template": config["template"]},
)
) )
sys.exit(1)
return template return template
@ -243,16 +245,13 @@ def _edit_search_results(config, journal, old_entries, **kwargs):
3. Write modifications to journal 3. Write modifications to journal
""" """
if not config["editor"]: if not config["editor"]:
print( raise JrnlException(
f""" Message(
[{ERROR_COLOR}ERROR{RESET_COLOR}: There is no editor configured.] MsgText.EditorNotConfigured,
MsgType.ERROR,
Please specify an editor in config file ({get_config_path()}) {"config_file": get_config_path()},
to use the --edit option. )
""",
file=sys.stderr,
) )
sys.exit(1)
# separate entries we are not editing # separate entries we are not editing
other_entries = [e for e in old_entries if e not in journal.entries] other_entries = [e for e in old_entries if e not in journal.entries]
@ -310,11 +309,7 @@ def _pluralize_entry(num):
def _delete_search_results(journal, old_entries, **kwargs): def _delete_search_results(journal, old_entries, **kwargs):
if not journal.entries: if not journal.entries:
print( raise JrnlException(Message(MsgText.NothingToDelete, MsgType.ERROR))
"[No entries deleted, because the search returned no results.]",
file=sys.stderr,
)
sys.exit(1)
entries_to_delete = journal.prompt_delete_entries() entries_to_delete = journal.prompt_delete_entries()

View file

@ -26,7 +26,7 @@ class MsgText(Enum):
# --- Exceptions ---# # --- Exceptions ---#
UncaughtException = """ UncaughtException = """
ERROR {name}
{exception} {exception}
This is probably a bug. Please file an issue at: This is probably a bug. Please file an issue at:
@ -61,6 +61,14 @@ class MsgText(Enum):
KeyboardInterruptMsg = "Aborted by user" KeyboardInterruptMsg = "Aborted by user"
CantReadTemplate = """
Unreadable template
Could not read template file at:
{template}
"""
NoDefaultJournal = "No default journal configured\n{journals}"
# --- Journal status ---# # --- Journal status ---#
JournalNotSaved = "Entry NOT saved to journal" JournalNotSaved = "Entry NOT saved to journal"
@ -72,6 +80,56 @@ class MsgText(Enum):
HowToQuitWindows = "Ctrl+z and then Enter" HowToQuitWindows = "Ctrl+z and then Enter"
HowToQuitLinux = "Ctrl+d" HowToQuitLinux = "Ctrl+d"
EditorMisconfigured = """
No such file or directory: '{editor_key}'
Please check the 'editor' key in your config file for errors:
editor: '{editor_key}'
"""
EditorNotConfigured = """
There is no editor configured
To use the --edit option, please specify an editor your config file:
{config_file}
For examples of how to configure an external editor, see:
https://jrnl.sh/en/stable/external-editors/
"""
NoTextReceived = """
Nothing saved to file
"""
# --- Upgrade --- #
JournalFailedUpgrade = """
The following journal{s} failed to upgrade:
{failed_journals}
Please tell us about this problem at the following URL:
https://github.com/jrnl-org/jrnl/issues/new?title=JournalFailedUpgrade
"""
UpgradeAborted = "jrnl was NOT upgraded"
ImportAborted = "Entries were NOT imported"
# -- Config --- #
AltConfigNotFound = """
Alternate configuration file not found at the given path:
{config_file}
"""
# --- Password --- #
PasswordMaxTriesExceeded = """
Too many attempts with wrong password
"""
# --- Search --- #
NothingToDelete = """
No entries to delete, because the search returned no results
"""
class Message(NamedTuple): class Message(NamedTuple):
text: MsgText text: MsgText

View file

@ -29,7 +29,7 @@ def list_journals(configuration):
from . import config from . import config
"""List the journals specified in the configuration file""" """List the journals specified in the configuration file"""
result = f"Journals defined in {config.get_config_path()}\n" result = f"Journals defined in config ({config.get_config_path()})\n"
ml = min(max(len(k) for k in configuration["journals"]), 20) ml = min(max(len(k) for k in configuration["journals"]), 20)
for journal, cfg in configuration["journals"].items(): for journal, cfg in configuration["journals"].items():
result += " * {:{}} -> {}\n".format( result += " * {:{}} -> {}\n".format(

View file

@ -4,6 +4,11 @@
import sys import sys
from jrnl.exception import JrnlException
from jrnl.messages import Message
from jrnl.messages import MsgText
from jrnl.messages import MsgType
class JRNLImporter: class JRNLImporter:
"""This plugin imports entries from other jrnl files.""" """This plugin imports entries from other jrnl files."""
@ -22,8 +27,11 @@ class JRNLImporter:
try: try:
other_journal_txt = sys.stdin.read() other_journal_txt = sys.stdin.read()
except KeyboardInterrupt: except KeyboardInterrupt:
print("[Entries NOT imported into journal.]", file=sys.stderr) raise JrnlException(
sys.exit(0) Message(MsgText.KeyboardInterruptMsg, MsgType.ERROR),
Message(MsgText.ImportAborted, MsgType.WARNING),
)
journal.import_(other_journal_txt) journal.import_(other_journal_txt)
new_cnt = len(journal.entries) new_cnt = len(journal.entries)
print( print(

View file

@ -10,10 +10,15 @@ from .EncryptedJournal import EncryptedJournal
from .config import is_config_json from .config import is_config_json
from .config import load_config from .config import load_config
from .config import scope_config from .config import scope_config
from .exception import UpgradeValidationException
from .exception import UserAbort
from .prompt import yesno from .prompt import yesno
from jrnl.output import print_msg
from jrnl.exception import JrnlException
from jrnl.messages import Message
from jrnl.messages import MsgText
from jrnl.messages import MsgType
def backup(filename, binary=False): def backup(filename, binary=False):
print(f" Created a backup at {filename}.backup", file=sys.stderr) print(f" Created a backup at {filename}.backup", file=sys.stderr)
@ -27,13 +32,9 @@ def backup(filename, binary=False):
backup.write(contents) backup.write(contents)
except FileNotFoundError: except FileNotFoundError:
print(f"\nError: {filename} does not exist.") print(f"\nError: {filename} does not exist.")
try:
cont = yesno(f"\nCreate {filename}?", default=False) cont = yesno(f"\nCreate {filename}?", default=False)
if not cont: if not cont:
raise KeyboardInterrupt raise JrnlException(Message(MsgText.UpgradeAborted), MsgType.WARNING)
except KeyboardInterrupt:
raise UserAbort("jrnl NOT upgraded, exiting.")
def check_exists(path): def check_exists(path):
@ -121,12 +122,9 @@ older versions of jrnl anymore.
file=sys.stderr, file=sys.stderr,
) )
try:
cont = yesno("\nContinue upgrading jrnl?", default=False) cont = yesno("\nContinue upgrading jrnl?", default=False)
if not cont: if not cont:
raise KeyboardInterrupt raise JrnlException(Message(MsgText.UpgradeAborted), MsgType.WARNING)
except KeyboardInterrupt:
raise UserAbort("jrnl NOT upgraded, exiting.")
for journal_name, path in encrypted_journals.items(): for journal_name, path in encrypted_journals.items():
print( print(
@ -154,15 +152,18 @@ older versions of jrnl anymore.
failed_journals = [j for j in all_journals if not j.validate_parsing()] failed_journals = [j for j in all_journals if not j.validate_parsing()]
if len(failed_journals) > 0: if len(failed_journals) > 0:
print( print_msg("Aborting upgrade.", msg=Message.NORMAL)
"\nThe following journal{} failed to upgrade:\n{}".format(
"s" if len(failed_journals) > 1 else "",
"\n".join(j.name for j in failed_journals),
),
file=sys.stderr,
)
raise UpgradeValidationException raise JrnlException(
Message(
MsgText.JournalFailedUpgrade,
MsgType.ERROR,
{
"s": "s" if len(failed_journals) > 1 else "",
"failed_journals": "\n".join(j.name for j in failed_journals),
},
)
)
# write all journals - or - don't # write all journals - or - don't
for j in all_journals: for j in all_journals:

View file

@ -41,6 +41,7 @@ Feature: Delete entries from journal
Scenario Outline: Delete flag with nonsense input deletes nothing (issue #932) Scenario Outline: Delete flag with nonsense input deletes nothing (issue #932)
Given we use the config "<config_file>" Given we use the config "<config_file>"
When we run "jrnl --delete asdfasdf" When we run "jrnl --delete asdfasdf"
Then the output should contain "No entries to delete"
When we run "jrnl -99 --short" When we run "jrnl -99 --short"
Then the output should be Then the output should be
2020-08-29 11:11 Entry the first. 2020-08-29 11:11 Entry the first.

View file

@ -78,7 +78,7 @@ Feature: Writing new entries.
And we write nothing to the editor if opened And we write nothing to the editor if opened
And we use the password "test" if prompted And we use the password "test" if prompted
When we run "jrnl --edit" When we run "jrnl --edit"
Then the error output should contain "[Nothing saved to file]" Then the error output should contain "Nothing saved to file"
And the editor should have been called And the editor should have been called
Examples: configs Examples: configs

View file

@ -2,6 +2,7 @@ import pytest
import os import os
from jrnl.install import find_alt_config from jrnl.install import find_alt_config
from jrnl.exception import JrnlException
def test_find_alt_config(request): def test_find_alt_config(request):
@ -14,9 +15,9 @@ def test_find_alt_config(request):
def test_find_alt_config_not_exist(request): def test_find_alt_config_not_exist(request):
bad_config_path = os.path.join( bad_config_path = os.path.join(
request.fspath.dirname, "..", "data", "configs", "not-existing-config.yaml" request.fspath.dirname, "..", "data", "configs", "does-not-exist.yaml"
) )
with pytest.raises(SystemExit) as ex: with pytest.raises(JrnlException) as ex:
found_alt_config = find_alt_config(bad_config_path) found_alt_config = find_alt_config(bad_config_path)
assert found_alt_config is not None assert found_alt_config is not None
assert isinstance(ex.value, SystemExit) assert isinstance(ex.value, JrnlException)

View file

@ -1,7 +1,6 @@
import pytest import pytest
from jrnl.exception import JrnlException from jrnl.exception import JrnlException
from jrnl.plugins.fancy_exporter import check_provided_linewrap_viability from jrnl.plugins.fancy_exporter import check_provided_linewrap_viability