merge in develop

This commit is contained in:
Jonathan Wren 2022-06-04 19:28:11 -07:00
commit 15b86cae97
30 changed files with 827 additions and 222 deletions

View file

@ -6,15 +6,27 @@
**Implemented enhancements:** **Implemented enhancements:**
- Add --change-time command to change the timestamp of an entry [\#1429](https://github.com/jrnl-org/jrnl/issues/1429)
- Show name of journal when creating a password/encrypting [\#1478](https://github.com/jrnl-org/jrnl/pull/1478) ([jonakeys](https://github.com/jonakeys))
- Implement --change-time flag [\#1452](https://github.com/jrnl-org/jrnl/pull/1452) ([richardjs](https://github.com/richardjs))
- Reformat additional messages and finish centralizing exception handling [\#1424](https://github.com/jrnl-org/jrnl/pull/1424) ([wren](https://github.com/wren)) - Reformat additional messages and finish centralizing exception handling [\#1424](https://github.com/jrnl-org/jrnl/pull/1424) ([wren](https://github.com/wren))
- Reformat messages and add new centralized exception handling [\#1417](https://github.com/jrnl-org/jrnl/pull/1417) ([wren](https://github.com/wren)) - Reformat messages and add new centralized exception handling [\#1417](https://github.com/jrnl-org/jrnl/pull/1417) ([wren](https://github.com/wren))
**Fixed bugs:** **Fixed bugs:**
- Using -not argument by itself leads to new entry creation [\#1472](https://github.com/jrnl-org/jrnl/issues/1472)
- Empty config file leads to confusing error message [\#1420](https://github.com/jrnl-org/jrnl/issues/1420) - Empty config file leads to confusing error message [\#1420](https://github.com/jrnl-org/jrnl/issues/1420)
- "Entry not saved" text doesn't appear in default stdin editor [\#1419](https://github.com/jrnl-org/jrnl/issues/1419) - "Entry not saved" text doesn't appear in default stdin editor [\#1419](https://github.com/jrnl-org/jrnl/issues/1419)
- jrnl --encrypt doesn't prompt me for password change [\#1358](https://github.com/jrnl-org/jrnl/issues/1358)
- '-not -contains x' raises an AttributeError exception [\#1350](https://github.com/jrnl-org/jrnl/issues/1350) - '-not -contains x' raises an AttributeError exception [\#1350](https://github.com/jrnl-org/jrnl/issues/1350)
- Directory YAML export shows stack trace when it doesn't need to [\#1227](https://github.com/jrnl-org/jrnl/issues/1227) - Exception when providing folder name of folder that does not exist as journal path [\#1293](https://github.com/jrnl-org/jrnl/issues/1293)
- display\_format: pretty and display\_format: short lead to crash [\#1263](https://github.com/jrnl-org/jrnl/issues/1263)
- Fixed error related to display\_format in config file for some values [\#1495](https://github.com/jrnl-org/jrnl/pull/1495) ([apainintheneck](https://github.com/apainintheneck))
- Create folder if config ends with \(back\)slash [\#1492](https://github.com/jrnl-org/jrnl/pull/1492) ([jonakeys](https://github.com/jonakeys))
- `-not` search parameter no longer opens editor [\#1490](https://github.com/jrnl-org/jrnl/pull/1490) ([apainintheneck](https://github.com/apainintheneck))
- Fix TypeError when using debug flag [\#1484](https://github.com/jrnl-org/jrnl/pull/1484) ([jonakeys](https://github.com/jonakeys))
- Prompt for password change when using 'jrnl --encrypt' on already encrypted journal [\#1477](https://github.com/jrnl-org/jrnl/pull/1477) ([jonakeys](https://github.com/jonakeys))
- Always expand all paths \(journals, templates, etc\) [\#1468](https://github.com/jrnl-org/jrnl/pull/1468) ([apainintheneck](https://github.com/apainintheneck))
- The `-not` option with no arguments now outputs error instead of stack trace [\#1466](https://github.com/jrnl-org/jrnl/pull/1466) ([apainintheneck](https://github.com/apainintheneck)) - The `-not` option with no arguments now outputs error instead of stack trace [\#1466](https://github.com/jrnl-org/jrnl/pull/1466) ([apainintheneck](https://github.com/apainintheneck))
- Give a proper message when trying to use an empty config file [\#1461](https://github.com/jrnl-org/jrnl/pull/1461) ([jonakeys](https://github.com/jonakeys)) - Give a proper message when trying to use an empty config file [\#1461](https://github.com/jrnl-org/jrnl/pull/1461) ([jonakeys](https://github.com/jonakeys))
- Display "No entry to save, because no text was received" after empty entry on cmdline [\#1459](https://github.com/jrnl-org/jrnl/pull/1459) ([apainintheneck](https://github.com/apainintheneck)) - Display "No entry to save, because no text was received" after empty entry on cmdline [\#1459](https://github.com/jrnl-org/jrnl/pull/1459) ([apainintheneck](https://github.com/apainintheneck))
@ -23,17 +35,22 @@
**Deprecated:** **Deprecated:**
- Remove "sample" format and its asteval dependency [\#1435](https://github.com/jrnl-org/jrnl/issues/1435) - Remove "sample" format and its asteval dependency [\#1435](https://github.com/jrnl-org/jrnl/issues/1435)
- Drop support for Python 3.7 and 3.8 [\#1412](https://github.com/jrnl-org/jrnl/pull/1412) ([micahellison](https://github.com/micahellison))
**Build:** **Build:**
- Clean up .gitignore [\#1286](https://github.com/jrnl-org/jrnl/issues/1286) - Clean up .gitignore [\#1286](https://github.com/jrnl-org/jrnl/issues/1286)
- Pin jinja2 in docs requirements to keep readthedocs builds from failing [\#1439](https://github.com/jrnl-org/jrnl/pull/1439) ([micahellison](https://github.com/micahellison)) - Pin jinja2 in docs requirements to keep readthedocs builds from failing [\#1439](https://github.com/jrnl-org/jrnl/pull/1439) ([micahellison](https://github.com/micahellison))
- Tidy up git ignore [\#1414](https://github.com/jrnl-org/jrnl/pull/1414) ([nelnog](https://github.com/nelnog)) - Tidy up git ignore [\#1414](https://github.com/jrnl-org/jrnl/pull/1414) ([nelnog](https://github.com/nelnog))
- Drop support for Python 3.7 and 3.8 [\#1412](https://github.com/jrnl-org/jrnl/pull/1412) ([micahellison](https://github.com/micahellison))
**Documentation:**
- Document --change-time in web-based docs' command line reference [\#1471](https://github.com/jrnl-org/jrnl/pull/1471) ([micahellison](https://github.com/micahellison))
**Packaging:** **Packaging:**
- Sync jrnl's Python version support more closely to Python release cycle [\#1406](https://github.com/jrnl-org/jrnl/issues/1406) - Sync jrnl's Python version support more closely to Python release cycle [\#1406](https://github.com/jrnl-org/jrnl/issues/1406)
- Bump keyring from 23.5.0 to 23.5.1 [\#1487](https://github.com/jrnl-org/jrnl/pull/1487) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump cryptography from 37.0.1 to 37.0.2 [\#1467](https://github.com/jrnl-org/jrnl/pull/1467) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump cryptography from 37.0.1 to 37.0.2 [\#1467](https://github.com/jrnl-org/jrnl/pull/1467) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump cryptography from 36.0.2 to 37.0.1 [\#1462](https://github.com/jrnl-org/jrnl/pull/1462) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump cryptography from 36.0.2 to 37.0.1 [\#1462](https://github.com/jrnl-org/jrnl/pull/1462) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump pytest from 7.1.1 to 7.1.2 [\#1458](https://github.com/jrnl-org/jrnl/pull/1458) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pytest from 7.1.1 to 7.1.2 [\#1458](https://github.com/jrnl-org/jrnl/pull/1458) ([dependabot[bot]](https://github.com/apps/dependabot))

View file

@ -28,9 +28,10 @@ you have an entry on May 5th, 2021 in a folder journal at `~/folderjournal`, it
be located in: `~/folderjournal/2021/05/05.txt` be located in: `~/folderjournal/2021/05/05.txt`
!!! note !!! note
When creating a new folder journal, you will need to create the folder before running Creating a new folder journal can be done in two ways:
`jrnl`. Otherwise, when you run `jrnl` for the first time, it will assume that you
are creating a single file journal instead, and it will create a file at that path. * Create a folder with the name of the journal before running `jrnl`. Otherwise, when you run `jrnl` for the first time, it will assume that you are creating a single file journal instead, and it will create a file at that path.
* Create a new journal in your [config_file](advanced.md) and end the path with a ``/`` (on a POSIX system like Linux or MacOSX) or a ``\`` (on a Windows system). The folder will be created automatically if it doesn't exist.
!!! note !!! note
Folder journals can't be encrypted. Folder journals can't be encrypted.

View file

@ -90,6 +90,11 @@ the temporary file you were editing and makes the changes to your journal.
Interactively deletes selected entries. You'll be asked to confirm deletion of Interactively deletes selected entries. You'll be asked to confirm deletion of
each entry. each entry.
### --change-time DATE
Interactively changes the time of the selected entries to the date specified,
or to right now if no date is specified. You'll be asked to confirm each entry,
unless you are using this with `--edit` on a single entry.
### --format TYPE ### --format TYPE
Display selected entries in an alternate format. See [formats](formats.md). Display selected entries in an alternate format. See [formats](formats.md).

View file

@ -1,2 +1,2 @@
mkdocs==1.2.3 mkdocs>=1.0,<1.3
jinja2==3.0.3 # https://github.com/readthedocs/readthedocs.org/issues/9037 jinja2==3.0.3 # https://github.com/readthedocs/readthedocs.org/issues/9037

View file

@ -8,6 +8,7 @@ import fnmatch
import os import os
from . import Journal from . import Journal
from . import time
def get_files(journal_config): def get_files(journal_config):
@ -88,6 +89,17 @@ class Folder(Journal.Journal):
self.entries.remove(entry) self.entries.remove(entry)
self._diff_entry_dates.append(entry.date) self._diff_entry_dates.append(entry.date)
def change_date_entries(self, date):
"""Changes entry dates to given date."""
date = time.parse(date)
self._diff_entry_dates.append(date)
for entry in self.entries:
self._diff_entry_dates.append(entry.date)
entry.date = date
def parse_editable_str(self, edited): def parse_editable_str(self, edited):
"""Parses the output of self.editable_str and updates its entries.""" """Parses the output of self.editable_str and updates its entries."""
mod_entries = self._parse(edited) mod_entries = self._parse(edited)

View file

@ -10,6 +10,7 @@ import re
from . import Entry from . import Entry
from . import time from . import time
from .prompt import yesno from .prompt import yesno
from .path import expand_path
from jrnl.output import print_msg from jrnl.output import print_msg
from jrnl.messages import Message from jrnl.messages import Message
@ -280,13 +281,19 @@ class Journal:
for entry in entries_to_delete: for entry in entries_to_delete:
self.entries.remove(entry) self.entries.remove(entry)
def prompt_delete_entries(self): def change_date_entries(self, date):
"""Prompts for deletion of each of the entries in a journal. """Changes entry dates to given date."""
Returns the entries the user wishes to delete.""" date = time.parse(date)
to_delete = [] for entry in self.entries:
entry.date = date
def ask_delete(entry): def prompt_action_entries(self, message):
"""Prompts for action for each entry in a journal, using given message.
Returns the entries the user wishes to apply the action on."""
to_act = []
def ask_action(entry):
return yesno( return yesno(
Message( Message(
MsgText.DeleteEntryQuestion, MsgText.DeleteEntryQuestion,
@ -296,10 +303,10 @@ class Journal:
) )
for entry in self.entries: for entry in self.entries:
if ask_delete(entry): if ask_action(entry):
to_delete.append(entry) to_act.append(entry)
return to_delete return to_act
def new_entry(self, raw, date=None, sort=True): def new_entry(self, raw, date=None, sort=True):
"""Constructs a new entry from some raw text input. """Constructs a new entry from some raw text input.
@ -426,7 +433,7 @@ def open_journal(journal_name, config, legacy=False):
backwards compatibility with jrnl 1.x backwards compatibility with jrnl 1.x
""" """
config = config.copy() config = config.copy()
config["journal"] = os.path.expanduser(os.path.expandvars(config["journal"])) config["journal"] = expand_path(config["journal"])
if os.path.isdir(config["journal"]): if os.path.isdir(config["journal"]):
if config["encrypt"]: if config["encrypt"]:
@ -454,6 +461,10 @@ def open_journal(journal_name, config, legacy=False):
if not config["encrypt"]: if not config["encrypt"]:
if legacy: if legacy:
return LegacyJournal(journal_name, **config).open() return LegacyJournal(journal_name, **config).open()
if config["journal"].endswith(os.sep):
from . import FolderJournal
return FolderJournal.Folder(journal_name, **config).open()
return PlainJournal(journal_name, **config).open() return PlainJournal(journal_name, **config).open()
from . import EncryptedJournal from . import EncryptedJournal

View file

@ -267,6 +267,14 @@ def parse_args(args=[]):
action="store_true", action="store_true",
help="Interactively deletes selected entries", help="Interactively deletes selected entries",
) )
exporting.add_argument(
"--change-time",
dest="change_time",
nargs="?",
metavar="DATE",
const="now",
help="Change timestamp for seleted entries (default: now)",
)
exporting.add_argument( exporting.add_argument(
"--format", "--format",
metavar="TYPE", metavar="TYPE",

View file

@ -19,6 +19,7 @@ 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
from jrnl.messages import MsgStyle from jrnl.messages import MsgStyle
from jrnl.prompt import create_password
def preconfig_diagnostic(_): def preconfig_diagnostic(_):
@ -86,9 +87,14 @@ def postconfig_encrypt(args, config, original_config, **kwargs):
) )
) )
journal.config["encrypt"] = True
new_journal = EncryptedJournal.from_journal(journal) new_journal = EncryptedJournal.from_journal(journal)
# If journal is encrypted, create new password
if journal.config["encrypt"] is True:
print(f"Journal {journal.name} is already encrypted. Create a new password.")
new_journal.password = create_password(new_journal.name)
journal.config["encrypt"] = True
new_journal.write(args.filename) new_journal.write(args.filename)
print_msg( print_msg(

View file

@ -13,6 +13,7 @@ from jrnl.messages import Message
from jrnl.messages import MsgText from jrnl.messages import MsgText
from jrnl.messages import MsgStyle from jrnl.messages import MsgStyle
from .path import home_dir
# Constants # Constants
DEFAULT_CONFIG_NAME = "jrnl.yaml" DEFAULT_CONFIG_NAME = "jrnl.yaml"
@ -81,9 +82,7 @@ def get_config_path():
), ),
) )
return os.path.join( return os.path.join(config_directory_path or home_dir(), DEFAULT_CONFIG_NAME)
config_directory_path or os.path.expanduser("~"), DEFAULT_CONFIG_NAME
)
def get_default_config(): def get_default_config():
@ -110,9 +109,7 @@ def get_default_config():
def get_default_journal_path(): def get_default_journal_path():
journal_data_path = xdg.BaseDirectory.save_data_path( journal_data_path = xdg.BaseDirectory.save_data_path(XDG_RESOURCE) or home_dir()
XDG_RESOURCE
) or os.path.expanduser("~")
return os.path.join(journal_data_path, DEFAULT_JOURNAL_NAME) return os.path.join(journal_data_path, DEFAULT_JOURNAL_NAME)

View file

@ -7,6 +7,9 @@ import logging
import os import os
import sys import sys
from .path import home_dir
from .path import absolute_path
from .path import expand_path
from .config import DEFAULT_JOURNAL_KEY from .config import DEFAULT_JOURNAL_KEY
from .config import get_config_path from .config import get_config_path
from .config import get_default_config from .config import get_default_config
@ -47,7 +50,7 @@ def find_default_config():
config_path = ( config_path = (
get_config_path() get_config_path()
if os.path.exists(get_config_path()) if os.path.exists(get_config_path())
else os.path.join(os.path.expanduser("~"), ".jrnl_config") else os.path.join(home_dir(), ".jrnl_config")
) )
return config_path return config_path
@ -119,12 +122,9 @@ def install():
), ),
get_input=True, get_input=True,
) )
journal_path = os.path.abspath(user_given_path or default_journal_path) journal_path = absolute_path(user_given_path or default_journal_path)
default_config = get_default_config() default_config = get_default_config()
default_config["journals"][DEFAULT_JOURNAL_KEY] = os.path.expanduser( default_config["journals"][DEFAULT_JOURNAL_KEY] = journal_path
os.path.expandvars(journal_path)
)
# If the folder doesn't exist, create it # If the folder doesn't exist, create it
path = os.path.split(default_config["journals"][DEFAULT_JOURNAL_KEY])[0] path = os.path.split(default_config["journals"][DEFAULT_JOURNAL_KEY])[0]
@ -154,7 +154,7 @@ def _initialize_autocomplete():
def _autocomplete_path(text, state): def _autocomplete_path(text, state):
expansions = glob.glob(os.path.expanduser(os.path.expandvars(text)) + "*") expansions = glob.glob(expand_path(text) + "*")
expansions = [e + "/" if os.path.isdir(e) else e for e in expansions] expansions = [e + "/" if os.path.isdir(e) else e for e in expansions]
expansions.append(None) expansions.append(None)
return expansions[state] return expansions[state]

View file

@ -16,6 +16,7 @@ from . import time
from .override import apply_overrides from .override import apply_overrides
from jrnl.output import print_msg from jrnl.output import print_msg
from jrnl.output import print_msgs from jrnl.output import print_msgs
from .path import expand_path
from jrnl.exception import JrnlException from jrnl.exception import JrnlException
from jrnl.messages import Message from jrnl.messages import Message
@ -81,6 +82,8 @@ def _is_write_mode(args, config, **kwargs):
args.contains, args.contains,
args.delete, args.delete,
args.edit, args.edit,
args.change_time,
args.excluded,
args.export, args.export,
args.end_date, args.end_date,
args.today_in_history, args.today_in_history,
@ -152,13 +155,15 @@ def write_mode(args, config, journal, **kwargs):
) )
) )
journal.write() journal.write()
logging.debug("Write mode: completed journal.write()", args.journal_name, raw) logging.debug("Write mode: completed journal.write()")
def search_mode(args, journal, **kwargs): def search_mode(args, journal, **kwargs):
""" """
Search for entries in a journal, then either: Search for entries in a journal, then either:
1. Send them to configured editor for user manipulation 1. Send them to configured editor for user manipulation (and also
change their timestamps if requested)
2. Change their timestamps
2. Delete them (with confirmation for each entry) 2. Delete them (with confirmation for each entry)
3. Display them (with formatting options) 3. Display them (with formatting options)
""" """
@ -174,8 +179,27 @@ def search_mode(args, journal, **kwargs):
# Where do the search results go? # Where do the search results go?
if args.edit: if args.edit:
# If we want to both edit and change time in one action
if args.change_time:
# Generate a new list instead of assigning so it won't be
# modified by _change_time_search_results
selected_entries = [e for e in journal.entries]
no_change_time_prompt = len(journal.entries) == 1
_change_time_search_results(no_prompt=no_change_time_prompt, **kwargs)
# Re-filter the journal enties (_change_time_search_results
# puts the filtered entries back); use selected_entries
# instead of running _search_journal again, because times
# have changed since the original search
kwargs["old_entries"] = journal.entries
journal.entries = selected_entries
_edit_search_results(**kwargs) _edit_search_results(**kwargs)
elif args.change_time:
_change_time_search_results(**kwargs)
elif args.delete: elif args.delete:
_delete_search_results(**kwargs) _delete_search_results(**kwargs)
@ -203,8 +227,10 @@ def _get_editor_template(config, **kwargs):
logging.debug("Write mode: no template configured") logging.debug("Write mode: no template configured")
return "" return ""
template_path = expand_path(config["template"])
try: try:
template = open(config["template"]).read() template = open(template_path).read()
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")
@ -212,7 +238,7 @@ def _get_editor_template(config, **kwargs):
Message( Message(
MsgText.CantReadTemplate, MsgText.CantReadTemplate,
MsgStyle.ERROR, MsgStyle.ERROR,
{"template": config["template"]}, {"template": template_path},
) )
) )
@ -244,6 +270,11 @@ def _search_journal(args, journal, **kwargs):
journal.limit(args.limit) journal.limit(args.limit)
def _other_entries(journal, entries):
"""Find entries that are not in journal"""
return [e for e in entries if e not in journal.entries]
def _edit_search_results(config, journal, old_entries, **kwargs): def _edit_search_results(config, journal, old_entries, **kwargs):
""" """
1. Send the given journal entries to the user-configured editor 1. Send the given journal entries to the user-configured editor
@ -260,7 +291,7 @@ def _edit_search_results(config, journal, old_entries, **kwargs):
) )
# 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 = _other_entries(journal, old_entries)
# Get stats now for summary later # Get stats now for summary later
old_stats = _get_predit_stats(journal) old_stats = _get_predit_stats(journal)
@ -322,7 +353,7 @@ def _delete_search_results(journal, old_entries, **kwargs):
if not journal.entries: if not journal.entries:
raise JrnlException(Message(MsgText.NothingToDelete, MsgStyle.ERROR)) raise JrnlException(Message(MsgText.NothingToDelete, MsgStyle.ERROR))
entries_to_delete = journal.prompt_delete_entries() entries_to_delete = journal.prompt_action_entries("Delete entry")
if entries_to_delete: if entries_to_delete:
journal.entries = old_entries journal.entries = old_entries
@ -331,23 +362,45 @@ def _delete_search_results(journal, old_entries, **kwargs):
journal.write() journal.write()
def _change_time_search_results(args, journal, old_entries, no_prompt=False, **kwargs):
if not journal.entries:
raise JrnlException(Message(MsgText.NothingToModify, MsgType.WARNING))
# separate entries we are not editing
other_entries = _other_entries(journal, old_entries)
if no_prompt:
entries_to_change = journal.entries
else:
entries_to_change = journal.prompt_action_entries("Change time")
if entries_to_change:
other_entries += [e for e in journal.entries if e not in entries_to_change]
journal.entries = entries_to_change
date = time.parse(args.change_time)
journal.change_date_entries(date)
journal.entries += other_entries
journal.sort()
journal.write()
def _display_search_results(args, journal, **kwargs): def _display_search_results(args, journal, **kwargs):
if args.short or args.export == "short": # Get export format from config file if not provided at the command line
args.export = args.export or kwargs["config"].get("display_format")
if args.tags:
print(plugins.get_exporter("tags").export(journal))
elif args.short or args.export == "short":
print(journal.pprint(short=True)) print(journal.pprint(short=True))
elif args.export == "pretty": elif args.export == "pretty":
print(journal.pprint()) print(journal.pprint())
elif args.tags:
print(plugins.get_exporter("tags").export(journal))
elif args.export: elif args.export:
exporter = plugins.get_exporter(args.export) exporter = plugins.get_exporter(args.export)
print(exporter.export(journal, args.filename)) print(exporter.export(journal, args.filename))
elif kwargs["config"].get("display_format"):
exporter = plugins.get_exporter(kwargs["config"]["display_format"])
print(exporter.export(journal, args.filename))
else: else:
print(journal.pprint()) print(journal.pprint())

View file

@ -237,7 +237,7 @@ class MsgText(Enum):
# --- Password --- # # --- Password --- #
Password = "Password:" Password = "Password:"
PasswordFirstEntry = "Enter new password: " PasswordFirstEntry = "Enter password for journal '{journal_name}': "
PasswordConfirmEntry = "Enter password again: " PasswordConfirmEntry = "Enter password again: "
PasswordMaxTriesExceeded = "Too many attempts with wrong password" PasswordMaxTriesExceeded = "Too many attempts with wrong password"
PasswordCanNotBeEmpty = "Password can't be empty!" PasswordCanNotBeEmpty = "Password can't be empty!"
@ -250,6 +250,10 @@ class MsgText(Enum):
No entries to delete, because the search returned no results No entries to delete, because the search returned no results
""" """
NothingToModify = """
No entries to modify, because the search returned no results
"""
# --- Formats --- # # --- Formats --- #
HeadingsPastH6 = """ HeadingsPastH6 = """
Headings increased past H6 on export - {date} {title} Headings increased past H6 on export - {date} {title}

13
jrnl/path.py Normal file
View file

@ -0,0 +1,13 @@
import os.path
def home_dir():
return os.path.expanduser("~")
def expand_path(path):
return os.path.expanduser(os.path.expandvars(path))
def absolute_path(path):
return os.path.abspath(expand_path(path))

View file

@ -13,7 +13,14 @@ def create_password(journal_name: str) -> str:
"hide_input": True, "hide_input": True,
} }
while True: while True:
pw = print_msg(Message(MsgText.PasswordFirstEntry, MsgStyle.PROMPT), **kwargs) pw = print_msg(
Message(
MsgText.PasswordFirstEntry,
MsgStyle.PROMPT,
params={"journal_name": journal_name},
),
**kwargs
)
if not pw: if not pw:
print_msg(Message(MsgText.PasswordCanNotBeEmpty, MsgStyle.WARNING)) print_msg(Message(MsgText.PasswordCanNotBeEmpty, MsgStyle.WARNING))

View file

@ -10,6 +10,7 @@ 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 .prompt import yesno from .prompt import yesno
from .path import expand_path
from jrnl.output import print_msg from jrnl.output import print_msg
from jrnl.output import print_msgs from jrnl.output import print_msgs
@ -20,7 +21,7 @@ from jrnl.messages import MsgStyle
def backup(filename, binary=False): def backup(filename, binary=False):
filename = os.path.expanduser(os.path.expandvars(filename)) filename = expand_path(filename)
try: try:
with open(filename, "rb" if binary else "r") as original: with open(filename, "rb" if binary else "r") as original:
@ -61,13 +62,13 @@ def upgrade_jrnl(config_path):
for journal_name, journal_conf in config["journals"].items(): for journal_name, journal_conf in config["journals"].items():
if isinstance(journal_conf, dict): if isinstance(journal_conf, dict):
path = journal_conf.get("journal") path = expand_path(journal_conf.get("journal"))
encrypt = journal_conf.get("encrypt") encrypt = journal_conf.get("encrypt")
else: else:
encrypt = config.get("encrypt") encrypt = config.get("encrypt")
path = journal_conf path = expand_path(journal_conf)
if os.path.exists(os.path.expanduser(path)): if os.path.exists(path):
path = os.path.expanduser(path) path = os.path.expanduser(path)
else: else:
print_msg(Message(MsgText.DoesNotExist, MsgStyle.ERROR, {"name": path})) print_msg(Message(MsgText.DoesNotExist, MsgStyle.ERROR, {"name": path}))

285
poetry.lock generated
View file

@ -11,7 +11,7 @@ textwrap3 = ">=0.9.2"
[[package]] [[package]]
name = "appnope" name = "appnope"
version = "0.1.2" version = "0.1.3"
description = "Disable App Nap on macOS >= 10.9" description = "Disable App Nap on macOS >= 10.9"
category = "dev" category = "dev"
optional = false optional = false
@ -107,11 +107,11 @@ pycparser = "*"
[[package]] [[package]]
name = "click" name = "click"
version = "8.0.4" version = "8.1.3"
description = "Composable command line interface toolkit" description = "Composable command line interface toolkit"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""} colorama = {version = "*", markers = "platform_system == \"Windows\""}
@ -185,7 +185,7 @@ pyflakes = ">=2.4.0,<2.5.0"
[[package]] [[package]]
name = "ghp-import" name = "ghp-import"
version = "2.0.2" version = "2.1.0"
description = "Copy your docs directly to the gh-pages branch." description = "Copy your docs directly to the gh-pages branch."
category = "dev" category = "dev"
optional = false optional = false
@ -207,7 +207,7 @@ python-versions = "*"
[[package]] [[package]]
name = "importlib-metadata" name = "importlib-metadata"
version = "4.11.2" version = "4.11.3"
description = "Read metadata from Python packages" description = "Read metadata from Python packages"
category = "main" category = "main"
optional = false optional = false
@ -244,7 +244,7 @@ toml = {version = ">=0.10.2", markers = "python_version > \"3.6\""}
[[package]] [[package]]
name = "ipython" name = "ipython"
version = "8.1.1" version = "8.3.0"
description = "IPython: Productive Interactive Computing" description = "IPython: Productive Interactive Computing"
category = "dev" category = "dev"
optional = false optional = false
@ -265,7 +265,7 @@ stack-data = "*"
traitlets = ">=5" traitlets = ">=5"
[package.extras] [package.extras]
all = ["black", "Sphinx (>=1.3)", "ipykernel", "nbconvert", "nbformat", "ipywidgets", "notebook", "ipyparallel", "qtconsole", "curio", "matplotlib (!=3.2.0)", "numpy (>=1.19)", "pandas", "pytest", "testpath", "trio", "pytest-asyncio"] all = ["black", "Sphinx (>=1.3)", "ipykernel", "nbconvert", "nbformat", "ipywidgets", "notebook", "ipyparallel", "qtconsole", "pytest (<7.1)", "pytest-asyncio", "testpath", "curio", "matplotlib (!=3.2.0)", "numpy (>=1.19)", "pandas", "trio"]
black = ["black"] black = ["black"]
doc = ["Sphinx (>=1.3)"] doc = ["Sphinx (>=1.3)"]
kernel = ["ipykernel"] kernel = ["ipykernel"]
@ -274,8 +274,8 @@ nbformat = ["nbformat"]
notebook = ["ipywidgets", "notebook"] notebook = ["ipywidgets", "notebook"]
parallel = ["ipyparallel"] parallel = ["ipyparallel"]
qtconsole = ["qtconsole"] qtconsole = ["qtconsole"]
test = ["pytest", "pytest-asyncio", "testpath"] test = ["pytest (<7.1)", "pytest-asyncio", "testpath"]
test_extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.19)", "pandas", "pytest", "testpath", "trio"] test_extra = ["pytest (<7.1)", "pytest-asyncio", "testpath", "curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.19)", "pandas", "trio"]
[[package]] [[package]]
name = "jedi" name = "jedi"
@ -294,23 +294,23 @@ testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"]
[[package]] [[package]]
name = "jeepney" name = "jeepney"
version = "0.7.1" version = "0.8.0"
description = "Low-level, pure Python DBus protocol wrapper." description = "Low-level, pure Python DBus protocol wrapper."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
[package.extras] [package.extras]
test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio", "async-timeout"] test = ["pytest", "pytest-trio", "pytest-asyncio (>=0.17)", "testpath", "trio", "async-timeout"]
trio = ["trio", "async-generator"] trio = ["trio", "async-generator"]
[[package]] [[package]]
name = "jinja2" name = "jinja2"
version = "3.0.3" version = "3.1.2"
description = "A very fast and expressive template engine." description = "A very fast and expressive template engine."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
MarkupSafe = ">=2.0" MarkupSafe = ">=2.0"
@ -320,7 +320,7 @@ i18n = ["Babel (>=2.7)"]
[[package]] [[package]]
name = "keyring" name = "keyring"
version = "23.5.0" version = "23.5.1"
description = "Store and access your passwords safely." description = "Store and access your passwords safely."
category = "main" category = "main"
optional = false optional = false
@ -333,8 +333,8 @@ pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_
SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""}
[package.extras] [package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"]
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"]
[[package]] [[package]]
name = "mako" name = "mako"
@ -354,7 +354,7 @@ testing = ["pytest"]
[[package]] [[package]]
name = "markdown" name = "markdown"
version = "3.3.6" version = "3.3.7"
description = "Python implementation of Markdown." description = "Python implementation of Markdown."
category = "dev" category = "dev"
optional = false optional = false
@ -368,7 +368,7 @@ testing = ["coverage", "pyyaml"]
[[package]] [[package]]
name = "markupsafe" name = "markupsafe"
version = "2.1.0" version = "2.1.1"
description = "Safely add untrusted strings to HTML/XML markup." description = "Safely add untrusted strings to HTML/XML markup."
category = "main" category = "main"
optional = false optional = false
@ -403,7 +403,7 @@ python-versions = ">=3.6"
[[package]] [[package]]
name = "mkdocs" name = "mkdocs"
version = "1.3.0" version = "1.2.4"
description = "Project documentation with Markdown." description = "Project documentation with Markdown."
category = "dev" category = "dev"
optional = false optional = false
@ -412,8 +412,8 @@ python-versions = ">=3.6"
[package.dependencies] [package.dependencies]
click = ">=3.3" click = ">=3.3"
ghp-import = ">=1.0" ghp-import = ">=1.0"
importlib-metadata = ">=4.3" importlib-metadata = ">=3.10"
Jinja2 = ">=2.10.2" Jinja2 = ">=2.10.1"
Markdown = ">=3.2.1" Markdown = ">=3.2.1"
mergedeep = ">=1.3.4" mergedeep = ">=1.3.4"
packaging = ">=20.5" packaging = ">=20.5"
@ -516,15 +516,15 @@ python-versions = "*"
[[package]] [[package]]
name = "platformdirs" name = "platformdirs"
version = "2.5.1" version = "2.5.2"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
[package.extras] [package.extras]
docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
[[package]] [[package]]
name = "pluggy" name = "pluggy"
@ -548,7 +548,7 @@ python-versions = "*"
[[package]] [[package]]
name = "prompt-toolkit" name = "prompt-toolkit"
version = "3.0.28" version = "3.0.29"
description = "Library for building powerful interactive command lines in Python" description = "Library for building powerful interactive command lines in Python"
category = "dev" category = "dev"
optional = false optional = false
@ -610,22 +610,22 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]] [[package]]
name = "pygments" name = "pygments"
version = "2.11.2" version = "2.12.0"
description = "Pygments is a syntax highlighting package written in Python." description = "Pygments is a syntax highlighting package written in Python."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.6"
[[package]] [[package]]
name = "pyparsing" name = "pyparsing"
version = "3.0.7" version = "3.0.8"
description = "Python parsing module" description = "pyparsing module - Classes and methods to define and execute parsing grammars"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6.8"
[package.extras] [package.extras]
diagrams = ["jinja2", "railroad-diagrams"] diagrams = ["railroad-diagrams", "jinja2"]
[[package]] [[package]]
name = "pyproject-flake8" name = "pyproject-flake8"
@ -745,11 +745,11 @@ pyyaml = "*"
[[package]] [[package]]
name = "rich" name = "rich"
version = "12.0.0" version = "12.4.0"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6.2,<4.0.0" python-versions = ">=3.6.3,<4.0.0"
[package.dependencies] [package.dependencies]
commonmark = ">=0.9.0,<0.10.0" commonmark = ">=0.9.0,<0.10.0"
@ -783,7 +783,7 @@ python-versions = ">=3.5"
[[package]] [[package]]
name = "secretstorage" name = "secretstorage"
version = "3.3.1" version = "3.3.2"
description = "Python bindings to FreeDesktop.org Secret Service API" description = "Python bindings to FreeDesktop.org Secret Service API"
category = "main" category = "main"
optional = false optional = false
@ -854,11 +854,11 @@ test = ["pytest"]
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.1.1" version = "4.2.0"
description = "Backported and Experimental Type Hints for Python 3.6+" description = "Backported and Experimental Type Hints for Python 3.7+"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
[[package]] [[package]]
name = "tzlocal" name = "tzlocal"
@ -873,7 +873,7 @@ pytz = "*"
[[package]] [[package]]
name = "watchdog" name = "watchdog"
version = "2.1.6" version = "2.1.7"
description = "Filesystem events monitoring" description = "Filesystem events monitoring"
category = "dev" category = "dev"
optional = false optional = false
@ -917,15 +917,15 @@ tests = ["coverage", "flake8", "wheel"]
[[package]] [[package]]
name = "zipp" name = "zipp"
version = "3.7.0" version = "3.8.0"
description = "Backport of pathlib-compatible object wrapper for zip files" description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
[package.extras] [package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"]
[extras] [extras]
testing = ["pytest", "pytest-bdd", "toml"] testing = ["pytest", "pytest-bdd", "toml"]
@ -933,7 +933,7 @@ testing = ["pytest", "pytest-bdd", "toml"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = ">=3.9.0, <3.12" python-versions = ">=3.9.0, <3.12"
content-hash = "8a4b09a72f705e5265d6d1bebc61926df2ff1e0237ac1369bef6dbebb44f4d9c" content-hash = "8cd80d198bf8208d9e4b2054b89a22f1ce9faccc91aaef3440fb22e1782b0239"
[metadata.files] [metadata.files]
ansiwrap = [ ansiwrap = [
@ -941,8 +941,8 @@ ansiwrap = [
{file = "ansiwrap-0.8.4.zip", hash = "sha256:ca0c740734cde59bf919f8ff2c386f74f9a369818cdc60efe94893d01ea8d9b7"}, {file = "ansiwrap-0.8.4.zip", hash = "sha256:ca0c740734cde59bf919f8ff2c386f74f9a369818cdc60efe94893d01ea8d9b7"},
] ]
appnope = [ appnope = [
{file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
{file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
] ]
argcomplete = [ argcomplete = [
{file = "argcomplete-2.0.0-py2.py3-none-any.whl", hash = "sha256:cffa11ea77999bb0dd27bb25ff6dc142a6796142f68d45b1a26b11f58724561e"}, {file = "argcomplete-2.0.0-py2.py3-none-any.whl", hash = "sha256:cffa11ea77999bb0dd27bb25ff6dc142a6796142f68d45b1a26b11f58724561e"},
@ -1042,8 +1042,8 @@ cffi = [
{file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
] ]
click = [ click = [
{file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
] ]
colorama = [ colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
@ -1090,15 +1090,15 @@ flake8 = [
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
] ]
ghp-import = [ ghp-import = [
{file = "ghp-import-2.0.2.tar.gz", hash = "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071"}, {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
{file = "ghp_import-2.0.2-py3-none-any.whl", hash = "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
] ]
glob2 = [ glob2 = [
{file = "glob2-0.7.tar.gz", hash = "sha256:85c3dbd07c8aa26d63d7aacee34fa86e9a91a3873bc30bf62ec46e531f92ab8c"}, {file = "glob2-0.7.tar.gz", hash = "sha256:85c3dbd07c8aa26d63d7aacee34fa86e9a91a3873bc30bf62ec46e531f92ab8c"},
] ]
importlib-metadata = [ importlib-metadata = [
{file = "importlib_metadata-4.11.2-py3-none-any.whl", hash = "sha256:d16e8c1deb60de41b8e8ed21c1a7b947b0bc62fab7e1d470bcdf331cea2e6735"}, {file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"},
{file = "importlib_metadata-4.11.2.tar.gz", hash = "sha256:b36ffa925fe3139b2f6ff11d6925ffd4fa7bc47870165e3ac260ac7b4f91e6ac"}, {file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"},
] ]
iniconfig = [ iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
@ -1108,74 +1108,74 @@ ipdb = [
{file = "ipdb-0.13.9.tar.gz", hash = "sha256:951bd9a64731c444fd907a5ce268543020086a697f6be08f7cc2c9a752a278c5"}, {file = "ipdb-0.13.9.tar.gz", hash = "sha256:951bd9a64731c444fd907a5ce268543020086a697f6be08f7cc2c9a752a278c5"},
] ]
ipython = [ ipython = [
{file = "ipython-8.1.1-py3-none-any.whl", hash = "sha256:6f56bfaeaa3247aa3b9cd3b8cbab3a9c0abf7428392f97b21902d12b2f42a381"}, {file = "ipython-8.3.0-py3-none-any.whl", hash = "sha256:341456643a764c28f670409bbd5d2518f9b82c013441084ff2c2fc999698f83b"},
{file = "ipython-8.1.1.tar.gz", hash = "sha256:8138762243c9b3a3ffcf70b37151a2a35c23d3a29f9743878c33624f4207be3d"}, {file = "ipython-8.3.0.tar.gz", hash = "sha256:807ae3cf43b84693c9272f70368440a9a7eaa2e7e6882dad943c32fbf7e51402"},
] ]
jedi = [ jedi = [
{file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"},
{file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"},
] ]
jeepney = [ jeepney = [
{file = "jeepney-0.7.1-py3-none-any.whl", hash = "sha256:1b5a0ea5c0e7b166b2f5895b91a08c14de8915afda4407fb5022a195224958ac"}, {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"},
{file = "jeepney-0.7.1.tar.gz", hash = "sha256:fa9e232dfa0c498bd0b8a3a73b8d8a31978304dcef0515adc859d4e096f96f4f"}, {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"},
] ]
jinja2 = [ jinja2 = [
{file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
{file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
] ]
keyring = [ keyring = [
{file = "keyring-23.5.0-py3-none-any.whl", hash = "sha256:b0d28928ac3ec8e42ef4cc227822647a19f1d544f21f96457965dc01cf555261"}, {file = "keyring-23.5.1-py3-none-any.whl", hash = "sha256:9ef58314bcc823f426b49ec787539a2d73571b37de4cd498f839803b01acff1e"},
{file = "keyring-23.5.0.tar.gz", hash = "sha256:9012508e141a80bd1c0b6778d5c610dd9f8c464d75ac6774248500503f972fb9"}, {file = "keyring-23.5.1.tar.gz", hash = "sha256:dee502cdf18a98211bef428eea11456a33c00718b2f08524fd5727c7f424bffd"},
] ]
mako = [ mako = [
{file = "Mako-1.2.0-py3-none-any.whl", hash = "sha256:23aab11fdbbb0f1051b93793a58323ff937e98e34aece1c4219675122e57e4ba"}, {file = "Mako-1.2.0-py3-none-any.whl", hash = "sha256:23aab11fdbbb0f1051b93793a58323ff937e98e34aece1c4219675122e57e4ba"},
{file = "Mako-1.2.0.tar.gz", hash = "sha256:9a7c7e922b87db3686210cf49d5d767033a41d4010b284e747682c92bddd8b39"}, {file = "Mako-1.2.0.tar.gz", hash = "sha256:9a7c7e922b87db3686210cf49d5d767033a41d4010b284e747682c92bddd8b39"},
] ]
markdown = [ markdown = [
{file = "Markdown-3.3.6-py3-none-any.whl", hash = "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3"}, {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"},
{file = "Markdown-3.3.6.tar.gz", hash = "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006"}, {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"},
] ]
markupsafe = [ markupsafe = [
{file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
{file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
{file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce"}, {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
{file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3"}, {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
{file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989"}, {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
{file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26"}, {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
{file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076"}, {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
{file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f"}, {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
{file = "MarkupSafe-2.1.0-cp310-cp310-win32.whl", hash = "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454"}, {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
{file = "MarkupSafe-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c"}, {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
{file = "MarkupSafe-2.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357"}, {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
{file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61"}, {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
{file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8"}, {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
{file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb"}, {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
{file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e"}, {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
{file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49"}, {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
{file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7"}, {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
{file = "MarkupSafe-2.1.0-cp37-cp37m-win32.whl", hash = "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a"}, {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
{file = "MarkupSafe-2.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad"}, {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
{file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759"}, {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
{file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7"}, {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
{file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed"}, {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
{file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea"}, {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
{file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730"}, {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
{file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1"}, {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
{file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8"}, {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
{file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f"}, {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
{file = "MarkupSafe-2.1.0-cp38-cp38-win32.whl", hash = "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8"}, {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
{file = "MarkupSafe-2.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea"}, {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
{file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3"}, {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
{file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448"}, {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
{file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c"}, {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
{file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956"}, {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
{file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c"}, {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
{file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7"}, {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
{file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d"}, {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
{file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635"}, {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
{file = "MarkupSafe-2.1.0-cp39-cp39-win32.whl", hash = "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05"}, {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
{file = "MarkupSafe-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7"}, {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
{file = "MarkupSafe-2.1.0.tar.gz", hash = "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f"}, {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
] ]
matplotlib-inline = [ matplotlib-inline = [
{file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"},
@ -1190,8 +1190,8 @@ mergedeep = [
{file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"},
] ]
mkdocs = [ mkdocs = [
{file = "mkdocs-1.3.0-py3-none-any.whl", hash = "sha256:26bd2b03d739ac57a3e6eed0b7bcc86168703b719c27b99ad6ca91dc439aacde"}, {file = "mkdocs-1.2.4-py3-none-any.whl", hash = "sha256:f108e7ab5a7ed3e30826dbf82f37638f0d90d11161644616cc4f01a1e2ab3940"},
{file = "mkdocs-1.3.0.tar.gz", hash = "sha256:b504405b04da38795fec9b2e5e28f6aa3a73bb0960cb6d5d27ead28952bd35ea"}, {file = "mkdocs-1.2.4.tar.gz", hash = "sha256:8e7970a26183487fe2a1041940c6fd03aa0dbe5549e50c3e7194f565cb3c678a"},
] ]
mypy-extensions = [ mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
@ -1229,8 +1229,8 @@ pickleshare = [
{file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
] ]
platformdirs = [ platformdirs = [
{file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"}, {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
{file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"}, {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
] ]
pluggy = [ pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
@ -1241,8 +1241,8 @@ pprintpp = [
{file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"},
] ]
prompt-toolkit = [ prompt-toolkit = [
{file = "prompt_toolkit-3.0.28-py3-none-any.whl", hash = "sha256:30129d870dcb0b3b6a53efdc9d0a83ea96162ffd28ffe077e94215b233dc670c"}, {file = "prompt_toolkit-3.0.29-py3-none-any.whl", hash = "sha256:62291dad495e665fca0bda814e342c69952086afb0f4094d0893d357e5c78752"},
{file = "prompt_toolkit-3.0.28.tar.gz", hash = "sha256:9f1cd16b1e86c2968f2519d7fb31dd9d669916f515612c269d14e9ed52b51650"}, {file = "prompt_toolkit-3.0.29.tar.gz", hash = "sha256:bd640f60e8cecd74f0dc249713d433ace2ddc62b65ee07f96d358e0b152b6ea7"},
] ]
ptyprocess = [ ptyprocess = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
@ -1269,12 +1269,12 @@ pyflakes = [
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
] ]
pygments = [ pygments = [
{file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"},
{file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"},
] ]
pyparsing = [ pyparsing = [
{file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"},
{file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"},
] ]
pyproject-flake8 = [ pyproject-flake8 = [
{file = "pyproject-flake8-0.0.1a4.tar.gz", hash = "sha256:8ed9453f1d984cfe94c998f9840275359e29e7f435b8ddd188ae084e2dc1270c"}, {file = "pyproject-flake8-0.0.1a4.tar.gz", hash = "sha256:8ed9453f1d984cfe94c998f9840275359e29e7f435b8ddd188ae084e2dc1270c"},
@ -1347,8 +1347,8 @@ pyyaml-env-tag = [
{file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
] ]
rich = [ rich = [
{file = "rich-12.0.0-py3-none-any.whl", hash = "sha256:fdcd2f8d416e152bcf35c659987038d1ae5a7bd336e821ca7551858a4c7e38a9"}, {file = "rich-12.4.0-py3-none-any.whl", hash = "sha256:fdf6f447f231289dd446d31e5ee856fb0cc7dccd852a80f3e9d5aeecccabaedc"},
{file = "rich-12.0.0.tar.gz", hash = "sha256:14bfd0507edc633e021b02c45cbf7ca22e33b513817627b8de3412f047a3e798"}, {file = "rich-12.4.0.tar.gz", hash = "sha256:7aebf67874dc5cc2c89e2cb6ac322c95bd92510df2af0296c8bf494335ef704f"},
] ]
"ruamel.yaml" = [ "ruamel.yaml" = [
{file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"},
@ -1382,8 +1382,8 @@ rich = [
{file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"},
] ]
secretstorage = [ secretstorage = [
{file = "SecretStorage-3.3.1-py3-none-any.whl", hash = "sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f"}, {file = "SecretStorage-3.3.2-py3-none-any.whl", hash = "sha256:755dc845b6ad76dcbcbc07ea3da75ae54bb1ea529eb72d15f83d26499a5df319"},
{file = "SecretStorage-3.3.1.tar.gz", hash = "sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"}, {file = "SecretStorage-3.3.2.tar.gz", hash = "sha256:0a8eb9645b320881c222e827c26f4cfcf55363e8b374a021981ef886657a912f"},
] ]
six = [ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
@ -1410,37 +1410,38 @@ traitlets = [
{file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"}, {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"},
] ]
typing-extensions = [ typing-extensions = [
{file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"},
{file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"},
] ]
tzlocal = [ tzlocal = [
{file = "tzlocal-2.1-py2.py3-none-any.whl", hash = "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"}, {file = "tzlocal-2.1-py2.py3-none-any.whl", hash = "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"},
{file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"}, {file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"},
] ]
watchdog = [ watchdog = [
{file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"}, {file = "watchdog-2.1.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:177bae28ca723bc00846466016d34f8c1d6a621383b6caca86745918d55c7383"},
{file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b"}, {file = "watchdog-2.1.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d1cf7dfd747dec519486a98ef16097e6c480934ef115b16f18adb341df747a4"},
{file = "watchdog-2.1.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542"}, {file = "watchdog-2.1.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f14ce6adea2af1bba495acdde0e510aecaeb13b33f7bd2f6324e551b26688ca"},
{file = "watchdog-2.1.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669"}, {file = "watchdog-2.1.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4d0e98ac2e8dd803a56f4e10438b33a2d40390a72750cff4939b4b274e7906fa"},
{file = "watchdog-2.1.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660"}, {file = "watchdog-2.1.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:81982c7884aac75017a6ecc72f1a4fedbae04181a8665a34afce9539fc1b3fab"},
{file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3"}, {file = "watchdog-2.1.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0b4a1fe6201c6e5a1926f5767b8664b45f0fcb429b62564a41f490ff1ce1dc7a"},
{file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04"}, {file = "watchdog-2.1.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6e6ae29b72977f2e1ee3d0b760d7ee47896cb53e831cbeede3e64485e5633cc8"},
{file = "watchdog-2.1.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b"}, {file = "watchdog-2.1.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b9777664848160449e5b4260e0b7bc1ae0f6f4992a8b285db4ec1ef119ffa0e2"},
{file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604"}, {file = "watchdog-2.1.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:19b36d436578eb437e029c6b838e732ed08054956366f6dd11875434a62d2b99"},
{file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6"}, {file = "watchdog-2.1.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b61acffaf5cd5d664af555c0850f9747cc5f2baf71e54bbac164c58398d6ca7b"},
{file = "watchdog-2.1.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9"}, {file = "watchdog-2.1.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e877c70245424b06c41ac258023ea4bd0c8e4ff15d7c1368f17cd0ae6e351dd"},
{file = "watchdog-2.1.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8"}, {file = "watchdog-2.1.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d802d65262a560278cf1a65ef7cae4e2bc7ecfe19e5451349e4c67e23c9dc420"},
{file = "watchdog-2.1.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6"}, {file = "watchdog-2.1.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b3750ee5399e6e9c69eae8b125092b871ee9e2fcbd657a92747aea28f9056a5c"},
{file = "watchdog-2.1.6-py3-none-manylinux2014_armv7l.whl", hash = "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685"}, {file = "watchdog-2.1.7-py3-none-manylinux2014_aarch64.whl", hash = "sha256:ed6d9aad09a2a948572224663ab00f8975fae242aa540509737bb4507133fa2d"},
{file = "watchdog-2.1.6-py3-none-manylinux2014_i686.whl", hash = "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0"}, {file = "watchdog-2.1.7-py3-none-manylinux2014_armv7l.whl", hash = "sha256:b26e13e8008dcaea6a909e91d39b629a39635d1a8a7239dd35327c74f4388601"},
{file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65"}, {file = "watchdog-2.1.7-py3-none-manylinux2014_i686.whl", hash = "sha256:0908bb50f6f7de54d5d31ec3da1654cb7287c6b87bce371954561e6de379d690"},
{file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb"}, {file = "watchdog-2.1.7-py3-none-manylinux2014_ppc64.whl", hash = "sha256:bdcbf75580bf4b960fb659bbccd00123d83119619195f42d721e002c1621602f"},
{file = "watchdog-2.1.6-py3-none-manylinux2014_s390x.whl", hash = "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2"}, {file = "watchdog-2.1.7-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:81a5861d0158a7e55fe149335fb2bbfa6f48cbcbd149b52dbe2cd9a544034bbd"},
{file = "watchdog-2.1.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15"}, {file = "watchdog-2.1.7-py3-none-manylinux2014_s390x.whl", hash = "sha256:03b43d583df0f18782a0431b6e9e9965c5b3f7cf8ec36a00b930def67942c385"},
{file = "watchdog-2.1.6-py3-none-win32.whl", hash = "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d"}, {file = "watchdog-2.1.7-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ae934e34c11aa8296c18f70bf66ed60e9870fcdb4cc19129a04ca83ab23e7055"},
{file = "watchdog-2.1.6-py3-none-win_amd64.whl", hash = "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5"}, {file = "watchdog-2.1.7-py3-none-win32.whl", hash = "sha256:49639865e3db4be032a96695c98ac09eed39bbb43fe876bb217da8f8101689a6"},
{file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"}, {file = "watchdog-2.1.7-py3-none-win_amd64.whl", hash = "sha256:340b875aecf4b0e6672076a6f05cfce6686935559bb6d34cebedee04126a9566"},
{file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"}, {file = "watchdog-2.1.7-py3-none-win_ia64.whl", hash = "sha256:351e09b6d9374d5bcb947e6ac47a608ec25b9d70583e9db00b2fcdb97b00b572"},
{file = "watchdog-2.1.7.tar.gz", hash = "sha256:3fd47815353be9c44eebc94cc28fe26b2b0c5bd889dafc4a5a7cbdf924143480"},
] ]
wcwidth = [ wcwidth = [
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
@ -1455,6 +1456,6 @@ yq = [
{file = "yq-2.14.0.tar.gz", hash = "sha256:f4bf2b299d1e5c7ebd74cfb25d1f5d9b6401063bac07a2d09a156144c1d644e1"}, {file = "yq-2.14.0.tar.gz", hash = "sha256:f4bf2b299d1e5c7ebd74cfb25d1f5d9b6401063bac07a2d09a156144c1d644e1"},
] ]
zipp = [ zipp = [
{file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},
{file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"}, {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"},
] ]

View file

@ -52,7 +52,7 @@ toml = { version = ">=0.10", optional = true }
rich = "^12.2.0" rich = "^12.2.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
mkdocs = ">=1.0" mkdocs = ">=1.0,<1.3"
black = { version = ">=21.5b2", allow-prereleases = true } black = { version = ">=21.5b2", allow-prereleases = true }
toml = ">=0.10" toml = ">=0.10"
pytest = ">=6.2" pytest = ">=6.2"
@ -82,6 +82,10 @@ required_plugins = [
] ]
markers = [ markers = [
"todo", "todo",
"skip_win",
"skip_posix",
"on_win",
"on_posix",
] ]
addopts = [ addopts = [
"--pdbcls=IPython.terminal.debugger:Pdb" "--pdbcls=IPython.terminal.debugger:Pdb"

View file

@ -0,0 +1,240 @@
Feature: Change entry times in journal
Scenario Outline: Change time flag changes single entry timestamp
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl -1"
Then the output should contain "2020-09-24 09:14 The third entry finally"
When we run "jrnl -1 --change-time '2022-04-23 10:30'" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2022-04-23 10:30 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change flag changes prompted entries
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl -1"
Then the output should contain "2020-09-24 09:14 The third entry finally"
When we run "jrnl --change-time '2022-04-23 10:30'" and enter
Y
N
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-31 14:32 A second entry in what I hope to be a long series.
2022-04-23 10:30 Entry the first.
2022-04-23 10:30 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change time flag with nonsense input changes nothing
Given we use the config "<config_file>"
When we run "jrnl --change-time now asdfasdf"
Then the output should contain "No entries to modify"
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Change time flag with tag only changes tagged entries
Given we use the config "<config_file>"
When we run "jrnl --change-time '2022-04-23 10:30' @ipsum" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
2022-04-23 10:30 Entry the first.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change time flag with multiple tags changes all entries matching any of the tags
Given we use the config "<config_file>"
When we run "jrnl --change-time '2022-04-23 10:30' @ipsum @tagthree" and enter
Y
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-31 14:32 A second entry in what I hope to be a long series.
2022-04-23 10:30 Entry the first.
2022-04-23 10:30 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change time flag with -and changes boolean AND of tagged entries
Given we use the config "<config_file>"
When we run "jrnl --change-time '2022-04-23 10:30' -and @tagone @tagtwo" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
2022-04-23 10:30 Entry the first.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change time flag with -not does not change entries from given tag
Given we use the config "<config_file>"
When we run "jrnl --change-time '2022-04-23 10:30' @tagone -not @ipsum" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2022-04-23 10:30 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change time flag with -from search operator only changes entries since that date
Given we use the config "<config_file>"
When we run "jrnl --change-time '2022-04-23 10:30' -from 2020-09-01" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2022-04-23 10:30 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change time flag with -to only changes entries up to specified date
Given we use the config "<config_file>"
When we run "jrnl --change-time '2022-04-23 10:30' -to 2020-08-31" and enter
Y
Y
When we run "jrnl -99 --short"
Then the output should be
2020-09-24 09:14 The third entry finally after weeks without writing.
2022-04-23 10:30 Entry the first.
2022-04-23 10:30 A second entry in what I hope to be a long series.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change time flag with -starred only changes starred entries
Given we use the config "<config_file>"
When we run "jrnl --change-time '2022-04-23 10:30' -starred" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-09-24 09:14 The third entry finally after weeks without writing.
2022-04-23 10:30 A second entry in what I hope to be a long series.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change time flag with -contains only changes entries containing expression
Given we use the config "<config_file>"
When we run "jrnl --change-time '2022-04-23 10:30' -contains dignissim" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
2022-04-23 10:30 Entry the first.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Change time flag with no enties specified changes nothing
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl --change-time" and enter
N
N
N
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: --change-time with --edit modifies selected entries
Given we use the config "<config_file>"
And we write nothing to the editor if opened
And we use the password "test" if prompted
When we run "jrnl --change-time '2022-04-23 10:30' --edit" and enter
Y
N
Y
Then the error output should contain "No entry to save"
And the editor should have been called
When we run "jrnl -99 --short"
Then the output should be
2020-08-31 14:32 A second entry in what I hope to be a long series.
2022-04-23 10:30 Entry the first.
2022-04-23 10:30 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo

View file

@ -24,10 +24,11 @@ Feature: Encrypting and decrypting journals
2013-06-10 15:40 Life is good. 2013-06-10 15:40 Life is good.
@todo
Scenario: Trying to encrypt an already encrypted journal Scenario: Trying to encrypt an already encrypted journal
# This should warn the user that the journal is already encrypted Given we use the config "encrypted.yaml"
When we run "jrnl --encrypt" and enter "bad doggie no biscuit"
Then the output should contain "already encrypted. Create a new password."
Then we should be prompted for a password
Scenario Outline: Encrypting a journal Scenario Outline: Encrypting a journal
Given we use the config "simple.yaml" Given we use the config "simple.yaml"

View file

@ -30,6 +30,24 @@ Feature: Journals iteracting with the file system in a way that users can see
Then the journal should exist Then the journal should exist
When we run "jrnl -99 --short" When we run "jrnl -99 --short"
Then the output should contain "This is a new entry in my journal" Then the output should contain "This is a new entry in my journal"
@on_posix
Scenario: If the directory for a Folder journal ending in a slash ('/') doesn't exist, then it should be created
Given we use the config "missing_directory.yaml"
Then the journal "endslash" directory should not exist
When we run "jrnl endslash This is a new entry in my journal"
Then the journal "endslash" directory should exist
When we run "jrnl endslash -1"
Then the output should contain "This is a new entry in my journal"
@on_win
Scenario: If the directory for a Folder journal ending in a backslash ('\') doesn't exist, then it should be created
Given we use the config "missing_directory.yaml"
Then the journal "endbackslash" directory should not exist
When we run "jrnl endbackslash This is a new entry in my journal"
Then the journal "endbackslash" directory should exist
When we run "jrnl endbackslash -1"
Then the output should contain "This is a new entry in my journal"
Scenario: Creating journal with relative path should update to absolute path Scenario: Creating journal with relative path should update to absolute path
Given we use no config Given we use no config

View file

@ -560,3 +560,19 @@ Feature: Custom formats
| basic_encrypted.yaml | | basic_encrypted.yaml |
| basic_folder.yaml | | basic_folder.yaml |
| basic_dayone.yaml | | basic_dayone.yaml |
Scenario Outline: display_format short and pretty do not crash if specified as config values
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl --config-override display_format short -1"
Then we should get no error
When we run "jrnl --config-override display_format pretty -1"
Then we should get no error
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |

View file

@ -183,6 +183,19 @@ Feature: Searching in a journal
| basic_folder.yaml | | basic_folder.yaml |
| basic_dayone.yaml | | basic_dayone.yaml |
Scenario Outline: Using -not should exclude all entries with that tag
# https://github.com/jrnl-org/jrnl/issues/1472
Given we use the config "<config_file>"
When we run "jrnl -not @tagtwo"
Then the output should not contain "@tagtwo"
And the editor should not have been called
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario: DayOne tag searching should work with tags containing a mixture of upper and lower case. Scenario: DayOne tag searching should work with tags containing a mixture of upper and lower case.
# https://github.com/jrnl-org/jrnl/issues/354 # https://github.com/jrnl-org/jrnl/issues/354
Given we use the config "dayone.yaml" Given we use the config "dayone.yaml"

View file

@ -5,6 +5,7 @@ scenarios("features/config_file.feature")
scenarios("features/core.feature") scenarios("features/core.feature")
scenarios("features/datetime.feature") scenarios("features/datetime.feature")
scenarios("features/delete.feature") scenarios("features/delete.feature")
scenarios("features/change_time.feature")
scenarios("features/encrypt.feature") scenarios("features/encrypt.feature")
scenarios("features/file_storage.feature") scenarios("features/file_storage.feature")
scenarios("features/format.feature") scenarios("features/format.feature")

View file

@ -2,8 +2,10 @@
# License: https://www.gnu.org/licenses/gpl-3.0.html # License: https://www.gnu.org/licenses/gpl-3.0.html
from pytest import mark from pytest import mark
from pytest import skip
from jrnl.os_compat import on_windows from jrnl.os_compat import on_windows
from jrnl.os_compat import on_posix
pytest_plugins = [ pytest_plugins = [
@ -15,11 +17,39 @@ pytest_plugins = [
def pytest_bdd_apply_tag(tag, function): def pytest_bdd_apply_tag(tag, function):
# skip markers
if tag == "skip_win": if tag == "skip_win":
marker = mark.skipif(on_windows(), reason="Skip test on Windows") marker = mark.skipif(on_windows(), reason="Skip test on Windows")
elif tag == "skip_posix":
marker = mark.skipif(on_posix(), reason="Skip test on Mac/Linux")
# only on OS markers
elif tag == "on_win":
marker = mark.skipif(not on_windows(), reason="Skip test not on Windows")
elif tag == "on_posix":
marker = mark.skipif(not on_posix(), reason="Skip test not on Mac/Linux")
else: else:
# Fall back to pytest-bdd's default behavior # Fall back to pytest-bdd's default behavior
return None return None
marker(function) marker(function)
return True return True
def pytest_runtest_setup(item):
markers = [mark.name for mark in item.iter_markers()]
on_win = on_windows()
on_nix = on_posix()
if "skip_win" in markers and on_win:
skip("Skip test on Windows")
if "skip_posix" in markers and on_nix:
skip("Skip test on Mac/Linux")
if "on_win" in markers and not on_win:
skip("Skip test not on Windows")
if "on_posix" in markers and not on_nix:
skip("Skip test not on Mac/Linux")

View file

@ -5,6 +5,8 @@ encrypt: false
highlight: true highlight: true
journals: journals:
default: features/journals/missing_directory/simple.journal default: features/journals/missing_directory/simple.journal
endslash: features/journals/missing_folder/
endbackslash: features\journals\missing_folder\
linewrap: 80 linewrap: 80
tagsymbols: "@" tagsymbols: "@"
template: false template: false

View file

@ -213,6 +213,16 @@ def journal_should_not_exist(config_on_disk, should_or_should_not):
) )
@then(parse('the journal "{journal_name}" directory {should_or_should_not} exist'))
def directory_should_not_exist(config_on_disk, should_or_should_not, journal_name):
scoped_config = scope_config(config_on_disk, journal_name)
expected_path = scoped_config["journal"]
we_should = parse_should_or_should_not(should_or_should_not)
dir_exists = os.path.isdir(expected_path)
assert dir_exists == we_should
@then(parse('the content of file "{file_path}" in the cache should be\n{file_content}')) @then(parse('the content of file "{file_path}" in the cache should be\n{file_content}'))
def content_of_file_should_be(file_path, file_content, cache_dir): def content_of_file_should_be(file_path, file_content, cache_dir):
assert cache_dir["exists"] assert cache_dir["exists"]

View file

@ -1,25 +0,0 @@
import argparse
from unittest import mock
import pytest
import jrnl
from jrnl.jrnl import _display_search_results
# fmt: off
# see: https://github.com/psf/black/issues/664
@pytest.mark.parametrize("export_format", [ "pretty", "short","markdown"])
#fmt: on
@mock.patch.object(argparse, "Namespace", return_value={"export": "markdown", "filename": "irrele.vant"})
def test_export_format(mock_args, export_format):
test_journal = jrnl.Journal.Journal
mock_args.export = export_format
#fmt: off
# see: https://github.com/psf/black/issues/664
with mock.patch("builtins.print") as mock_spy_print, \
mock.patch('jrnl.Journal.Journal.pprint') as mock_pprint:
_display_search_results(mock_args, test_journal)
mock_spy_print.assert_called_once_with(mock_pprint())
#fmt: on

48
tests/unit/test_jrnl.py Normal file
View file

@ -0,0 +1,48 @@
from unittest import mock
import pytest
import random
import string
import jrnl
from jrnl.jrnl import _display_search_results
from jrnl.args import parse_args
@pytest.fixture
def random_string():
return "".join(random.choices(string.ascii_uppercase + string.digits, k=25))
@pytest.mark.parametrize("export_format", ["pretty", "short"])
@mock.patch("builtins.print")
@mock.patch("jrnl.Journal.Journal.pprint")
def test_display_search_results_pretty_short(mock_pprint, mock_print, export_format):
mock_args = parse_args(["--format", export_format])
test_journal = mock.Mock(wraps=jrnl.Journal.Journal)
_display_search_results(mock_args, test_journal)
mock_print.assert_called_once_with(mock_pprint.return_value)
@pytest.mark.parametrize(
"export_format", ["markdown", "json", "xml", "yaml", "fancy", "dates"]
)
@mock.patch("jrnl.plugins.get_exporter")
@mock.patch("builtins.print")
def test_display_search_results_builtin_plugins(
mock_print, mock_exporter, export_format, random_string
):
test_filename = random_string
mock_args = parse_args(["--format", export_format, "--file", test_filename])
test_journal = mock.Mock(wraps=jrnl.Journal.Journal)
mock_export = mock.Mock()
mock_exporter.return_value.export = mock_export
_display_search_results(mock_args, test_journal)
mock_exporter.assert_called_once_with(export_format)
mock_export.assert_called_once_with(test_journal, test_filename)
mock_print.assert_called_once_with(mock_export.return_value)

View file

@ -17,6 +17,7 @@ def expected_args(**kwargs):
"contains": None, "contains": None,
"debug": False, "debug": False,
"delete": False, "delete": False,
"change_time": None,
"edit": False, "edit": False,
"end_date": None, "end_date": None,
"today_in_history": False, "today_in_history": False,
@ -58,6 +59,13 @@ def test_delete_alone():
assert cli_as_dict("--delete") == expected_args(delete=True) assert cli_as_dict("--delete") == expected_args(delete=True)
def test_change_time_alone():
assert cli_as_dict("--change-time") == expected_args(change_time="now")
assert cli_as_dict("--change-time yesterday") == expected_args(
change_time="yesterday"
)
def test_diagnostic_alone(): def test_diagnostic_alone():
from jrnl.commands import preconfig_diagnostic from jrnl.commands import preconfig_diagnostic

103
tests/unit/test_path.py Normal file
View file

@ -0,0 +1,103 @@
import pytest
import random
import string
from os import getenv
from unittest.mock import patch
from jrnl.path import home_dir
from jrnl.path import expand_path
from jrnl.path import absolute_path
@pytest.fixture
def home_dir_str(monkeypatch):
username = "username"
monkeypatch.setenv("USERPROFILE", username) # for windows
monkeypatch.setenv("HOME", username) # for *nix
return username
@pytest.fixture
def random_test_var(monkeypatch):
name = f"JRNL_TEST_{''.join(random.sample(string.ascii_uppercase, 10))}"
val = "".join(random.sample(string.ascii_lowercase, 25))
monkeypatch.setenv(name, val)
return (name, val)
def test_home_dir(home_dir_str):
assert home_dir() == home_dir_str
@pytest.mark.on_posix
@pytest.mark.parametrize(
"path",
["~"],
)
def test_expand_path_actually_expands_mac_linux(path):
# makes sure that path isn't being returns as-is
assert expand_path(path) != path
@pytest.mark.on_win
@pytest.mark.parametrize(
"path",
["~", "%USERPROFILE%"],
)
def test_expand_path_actually_expands_windows(path):
# makes sure that path isn't being returns as-is
assert expand_path(path) != path
@pytest.mark.on_posix
@pytest.mark.parametrize(
"paths",
[
["~", "HOME"],
],
)
def test_expand_path_expands_into_correct_value_mac_linux(paths):
input_path, expected_path = paths[0], paths[1]
assert expand_path(input_path) == getenv(expected_path)
@pytest.mark.on_win
@pytest.mark.parametrize(
"paths",
[
["~", "USERPROFILE"],
["%USERPROFILE%", "USERPROFILE"],
],
)
def test_expand_path_expands_into_correct_value_windows(paths):
input_path, expected_path = paths[0], paths[1]
assert expand_path(input_path) == getenv(expected_path)
@pytest.mark.on_posix
@pytest.mark.parametrize("_", range(25))
def test_expand_path_expands_into_random_env_value_mac_linux(_, random_test_var):
var_name, var_value = random_test_var[0], random_test_var[1]
assert expand_path(var_name) == var_name
assert expand_path(f"${var_name}") == var_value # mac & linux
assert expand_path(f"${var_name}") == getenv(var_name)
@pytest.mark.on_win
@pytest.mark.parametrize("_", range(25))
def test_expand_path_expands_into_random_env_value_windows(_, random_test_var):
var_name, var_value = random_test_var[0], random_test_var[1]
assert expand_path(var_name) == var_name
assert expand_path(f"%{var_name}%") == var_value # windows
assert expand_path(f"%{var_name}%") == getenv(var_name)
@patch("jrnl.path.expand_path")
@patch("os.path.abspath")
def test_absolute_path(mock_abspath, mock_expand_path):
test_val = "test_value"
assert absolute_path(test_val) == mock_abspath.return_value
mock_expand_path.assert_called_with(test_val)
mock_abspath.assert_called_with(mock_expand_path.return_value)