From 1fa851ed0c74e14645b747eb7b17acf847371bef Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Sat, 26 Oct 2019 21:43:48 +0200 Subject: [PATCH 01/93] Fix handling of little-endian date format Ever since version 2.0, when parsing a journal file, jrnl would not use the custom date format string anymore. Instead, it relied on the dateutil library to get the parsing right. This change was made to allow people to change their date format without having to manually change their file. However, this broke some existing date formats like %d.%m.%Y, as it would falsely interpret the month as day and vice versa. This commit adds some tests to catch this error and fixes it by trying to parse the dates using the custom format first, only falling back to dateutil when needed. --- features/custom_dates.feature | 35 +++++++++++++++++++ .../data/configs/little_endian_dates.yaml | 12 +++++++ .../upgrade_from_195_little_endian_dates.json | 11 ++++++ .../data/journals/little_endian_dates.journal | 5 +++ ...ple_jrnl-1-9-5_little_endian_dates.journal | 13 +++++++ features/upgrade.feature | 13 ++++++- jrnl/Journal.py | 6 +++- 7 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 features/custom_dates.feature create mode 100644 features/data/configs/little_endian_dates.yaml create mode 100644 features/data/configs/upgrade_from_195_little_endian_dates.json create mode 100644 features/data/journals/little_endian_dates.journal create mode 100644 features/data/journals/simple_jrnl-1-9-5_little_endian_dates.journal diff --git a/features/custom_dates.feature b/features/custom_dates.feature new file mode 100644 index 00000000..d3489648 --- /dev/null +++ b/features/custom_dates.feature @@ -0,0 +1,35 @@ +Feature: Reading and writing to journal with custom date formats + + Scenario: Loading a sample journal + Given we use the config "little_endian_dates.yaml" + When we run "jrnl -n 2" + Then we should get no error + And the output should be + """ + 09.06.2013 15:39 My first entry. + | Everything is alright + + 10.06.2013 15:40 Life is good. + | But I'm better. + """ + + Scenario: Writing an entry from command line + Given we use the config "little_endian_dates.yaml" + When we run "jrnl 2013-07-12: A cold and stormy day. I ate crisps on the sofa." + Then we should see the message "Entry added" + When we run "jrnl -n 1" + Then the output should contain "12.07.2013 09:00 A cold and stormy day." + + Scenario: Filtering for dates + Given we use the config "little_endian_dates.yaml" + When we run "jrnl -on 2013-06-10 --short" + Then the output should be "10.06.2013 15:40 Life is good." + When we run "jrnl -on 'june 6 2013' --short" + Then the output should be "10.06.2013 15:40 Life is good." + + Scenario: Writing an entry at the prompt + Given we use the config "little_endian_dates.yaml" + When we run "jrnl" and enter "2013-05-10: I saw Elvis. He's alive." + Then we should get no error + And the journal should contain "[10.05.2013 09:00] I saw Elvis." + And the journal should contain "He's alive." \ No newline at end of file diff --git a/features/data/configs/little_endian_dates.yaml b/features/data/configs/little_endian_dates.yaml new file mode 100644 index 00000000..223c820d --- /dev/null +++ b/features/data/configs/little_endian_dates.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/little_endian_dates.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%d.%m.%Y %H:%M" +indent_character: "|" diff --git a/features/data/configs/upgrade_from_195_little_endian_dates.json b/features/data/configs/upgrade_from_195_little_endian_dates.json new file mode 100644 index 00000000..7d3c470c --- /dev/null +++ b/features/data/configs/upgrade_from_195_little_endian_dates.json @@ -0,0 +1,11 @@ +{ +"default_hour": 9, +"timeformat": "%d.%m.%Y %H:%M", +"linewrap": 80, +"encrypt": false, +"editor": "", +"default_minute": 0, +"highlight": true, +"journals": {"default": "features/journals/simple_jrnl-1-9-5_little_endian_dates.journal"}, +"tagsymbols": "@" +} diff --git a/features/data/journals/little_endian_dates.journal b/features/data/journals/little_endian_dates.journal new file mode 100644 index 00000000..a3c5c301 --- /dev/null +++ b/features/data/journals/little_endian_dates.journal @@ -0,0 +1,5 @@ +[09.06.2013 15:39] My first entry. +Everything is alright + +[10.06.2013 15:40] Life is good. +But I'm better. diff --git a/features/data/journals/simple_jrnl-1-9-5_little_endian_dates.journal b/features/data/journals/simple_jrnl-1-9-5_little_endian_dates.journal new file mode 100644 index 00000000..328b23f4 --- /dev/null +++ b/features/data/journals/simple_jrnl-1-9-5_little_endian_dates.journal @@ -0,0 +1,13 @@ +10.06.2010 15:00 A life without chocolate is like a bad analogy. + +10.06.2013 15:40 He said "[this] is the best time to be alive". +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada +quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque +augue et venenatis facilisis. + +[03.08.2019 12:55] Some chat log or something + +Suspendisse potenti. Sed dignissim sed nisl eu consequat. Aenean ante ex, +elementum ut interdum et, mattis eget lacus. In commodo nulla nec tellus +placerat, sed ultricies metus bibendum. Duis eget venenatis erat. In at dolor +dui. diff --git a/features/upgrade.feature b/features/upgrade.feature index bce026b8..fd8c1bd4 100644 --- a/features/upgrade.feature +++ b/features/upgrade.feature @@ -20,4 +20,15 @@ Feature: Upgrading Journals from 1.x.x to 2.x.x bad doggie no biscuit """ Then we should see the message "Password" - and the output should contain "2013-06-10 15:40 Life is good" + And the output should contain "2013-06-10 15:40 Life is good" + + Scenario: Upgrade and parse journals with little endian date format + Given we use the config "upgrade_from_195_little_endian_dates.json" + When we run "jrnl -9" and enter "Y" + Then the output should contain + """ + 10.06.2010 15:00 A life without chocolate is like a bad analogy. + + 10.06.2013 15:40 He said "[this] is the best time to be alive". + """ + Then the journal should have 2 entries diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 72fe94b1..83f3b1a2 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -122,7 +122,11 @@ class Journal(object): last_entry_pos = 0 for match in date_blob_re.finditer(journal_txt): date_blob = match.groups()[0] - new_date = time.parse(date_blob) + try: + new_date = datetime.strptime(date_blob, self.config["timeformat"]) + except ValueError: + new_date = time.parse(date_blob) + if new_date: if entries: entries[-1].text = journal_txt[last_entry_pos:match.start()] From 6ad89a26c8f88b7bf84e4f7c7c96c1b7fa2568b1 Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Tue, 29 Oct 2019 18:17:44 +0100 Subject: [PATCH 02/93] Fix crash when no keyring backend available --- jrnl/util.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jrnl/util.py b/jrnl/util.py index bc36ba9b..5609cd7f 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -80,7 +80,10 @@ def get_password(validator, keychain=None, max_attempts=3): def get_keychain(journal_name): import keyring - return keyring.get_password('jrnl', journal_name) + try: + return keyring.get_password('jrnl', journal_name) + except RuntimeError: + return "" def set_keychain(journal_name, password): @@ -88,7 +91,7 @@ def set_keychain(journal_name, password): if password is None: try: keyring.delete_password('jrnl', journal_name) - except: + except RuntimeError: pass elif not TEST: keyring.set_password('jrnl', journal_name, password) From 34f8f858f14707646c0897e5577361eb80781a6a Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Thu, 31 Oct 2019 21:12:55 +0100 Subject: [PATCH 03/93] remove py2 remnants and use mocks in tests --- features/encryption.feature | 8 +-- features/environment.py | 15 +---- features/multiple_journals.feature | 2 +- features/steps/core.py | 68 +++++++++++-------- features/upgrade.feature | 4 +- jrnl/DayOneJournal.py | 3 +- jrnl/EncryptedJournal.py | 8 +-- jrnl/Entry.py | 5 +- jrnl/Journal.py | 24 +++---- jrnl/__main__.py | 1 - jrnl/cli.py | 49 +++++++------- jrnl/export.py | 17 +++-- jrnl/install.py | 14 ++-- jrnl/plugins/__init__.py | 2 - jrnl/plugins/jrnl_importer.py | 9 ++- jrnl/plugins/json_exporter.py | 1 - jrnl/plugins/markdown_exporter.py | 1 - jrnl/plugins/tag_exporter.py | 1 - jrnl/plugins/template.py | 2 +- jrnl/plugins/template_exporter.py | 6 +- jrnl/plugins/text_exporter.py | 15 ++--- jrnl/plugins/xml_exporter.py | 10 ++- jrnl/plugins/yaml_exporter.py | 3 +- jrnl/upgrade.py | 32 ++++----- jrnl/util.py | 101 ++++------------------------- 25 files changed, 155 insertions(+), 246 deletions(-) diff --git a/features/encryption.feature b/features/encryption.feature index 82d971eb..d30c48b3 100644 --- a/features/encryption.feature +++ b/features/encryption.feature @@ -2,7 +2,7 @@ Scenario: Loading an encrypted journal Given we use the config "encrypted.yaml" When we run "jrnl -n 1" and enter "bad doggie no biscuit" - Then we should see the message "Password" + Then the output should contain "Password" and the output should contain "2013-06-10 15:40 Life is good" Scenario: Decrypting a journal @@ -14,16 +14,16 @@ Scenario: Encrypting a journal Given we use the config "basic.yaml" - When we run "jrnl --encrypt" and enter "swordfish" + When we run "jrnl --encrypt" and enter "swordfish" and "n" Then we should see the message "Journal encrypted" and the config for journal "default" should have "encrypt" set to "bool:True" When we run "jrnl -n 1" and enter "swordfish" - Then we should see the message "Password" + Then the output should contain "Password" and the output should contain "2013-06-10 15:40 Life is good" Scenario: Storing a password in Keychain Given we use the config "multiple.yaml" - When we run "jrnl simple --encrypt" and enter "sabertooth" + When we run "jrnl simple --encrypt" and enter "sabertooth" and "y" When we set the keychain password of "simple" to "sabertooth" Then the config for journal "simple" should have "encrypt" set to "bool:True" When we run "jrnl simple -n 1" diff --git a/features/environment.py b/features/environment.py index 6f9ac5df..7a918feb 100644 --- a/features/environment.py +++ b/features/environment.py @@ -1,25 +1,15 @@ -from behave import * import shutil import os -import jrnl -try: - from io import StringIO -except ImportError: - from cStringIO import StringIO + def before_scenario(context, scenario): """Before each scenario, backup all config and journal test data.""" - context.messages = StringIO() - jrnl.util.STDERR = context.messages - jrnl.util.TEST = True - # Clean up in case something went wrong for folder in ("configs", "journals"): working_dir = os.path.join("features", folder) if os.path.exists(working_dir): shutil.rmtree(working_dir) - for folder in ("configs", "journals"): original = os.path.join("features", "data", folder) working_dir = os.path.join("features", folder) @@ -32,10 +22,9 @@ def before_scenario(context, scenario): else: shutil.copy2(source, working_dir) + def after_scenario(context, scenario): """After each scenario, restore all test data and remove working_dirs.""" - context.messages.close() - context.messages = None for folder in ("configs", "journals"): working_dir = os.path.join("features", folder) if os.path.exists(working_dir): diff --git a/features/multiple_journals.feature b/features/multiple_journals.feature index 1d4943ee..28587f96 100644 --- a/features/multiple_journals.feature +++ b/features/multiple_journals.feature @@ -42,5 +42,5 @@ Feature: Multiple journals Scenario: Don't crash if no file exists for a configured encrypted journal Given we use the config "multiple.yaml" - When we run "jrnl new_encrypted Adding first entry" and enter "these three eyes" + When we run "jrnl new_encrypted Adding first entry" and enter "these three eyes" and "y" Then we should see the message "Journal 'new_encrypted' created" diff --git a/features/steps/core.py b/features/steps/core.py index 83981d13..9217f77f 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -1,5 +1,4 @@ -from __future__ import unicode_literals -from __future__ import absolute_import +from unittest.mock import patch from behave import given, when, then from jrnl import cli, install, Journal, util, plugins @@ -10,10 +9,13 @@ import os import json import yaml import keyring +import tzlocal +import shlex +import sys class TestKeyring(keyring.backend.KeyringBackend): - """A test keyring that just stores its valies in a hash""" + """A test keyring that just stores its values in a hash""" priority = 1 keys = defaultdict(dict) @@ -31,15 +33,6 @@ class TestKeyring(keyring.backend.KeyringBackend): keyring.set_keyring(TestKeyring()) -try: - from io import StringIO -except ImportError: - from cStringIO import StringIO -import tzlocal -import shlex -import sys - - def ushlex(command): if sys.version_info[0] == 3: return shlex.split(command) @@ -73,18 +66,41 @@ def set_config(context, config_file): cf.write("version: {}".format(__version__)) +def _mock_getpass(inputs): + def prompt_return(prompt="Password: "): + print(prompt) + return next(inputs) + return prompt_return + + +def _mock_input(inputs): + def prompt_return(prompt=""): + val = next(inputs) + print(prompt, val) + return val + return prompt_return + + @when('we run "{command}" and enter') -@when('we run "{command}" and enter "{inputs}"') -def run_with_input(context, command, inputs=None): - text = inputs or context.text +@when('we run "{command}" and enter "{inputs1}"') +@when('we run "{command}" and enter "{inputs1}" and "{inputs2}"') +def run_with_input(context, command, inputs1="", inputs2=""): + # create an iterator through all inputs. These inputs will be fed one by one + # to the mocked calls for 'input()', 'util.getpass()' and 'sys.stdin.read()' + text = iter((inputs1, inputs2)) if inputs1 else iter(context.text.split("\n")) args = ushlex(command)[1:] - buffer = StringIO(text.strip()) - util.STDIN = buffer - try: - cli.run(args or []) - context.exit_status = 0 - except SystemExit as e: - context.exit_status = e.code + with patch("builtins.input", side_effect=_mock_input(text)) as mock_input: + with patch("jrnl.util.getpass", side_effect=_mock_getpass(text)) as mock_getpass: + with patch("sys.stdin.read", side_effect=text) as mock_read: + try: + cli.run(args or []) + context.exit_status = 0 + except SystemExit as e: + context.exit_status = e.code + + # assert at least one of the mocked input methods got called + assert mock_input.called or mock_getpass.called or mock_read.called + @when('we run "{command}"') @@ -190,28 +206,24 @@ def check_output_time_inline(context, text): def check_output_inline(context, text=None): text = text or context.text out = context.stdout_capture.getvalue() - if isinstance(out, bytes): - out = out.decode('utf-8') assert text in out, text @then('the output should not contain "{text}"') def check_output_not_inline(context, text): out = context.stdout_capture.getvalue() - if isinstance(out, bytes): - out = out.decode('utf-8') assert text not in out @then('we should see the message "{text}"') def check_message(context, text): - out = context.messages.getvalue() + out = context.stderr_capture.getvalue() assert text in out, [text, out] @then('we should not see the message "{text}"') def check_not_message(context, text): - out = context.messages.getvalue() + out = context.stderr_capture.getvalue() assert text not in out, [text, out] diff --git a/features/upgrade.feature b/features/upgrade.feature index bce026b8..ddcce494 100644 --- a/features/upgrade.feature +++ b/features/upgrade.feature @@ -13,11 +13,11 @@ Feature: Upgrading Journals from 1.x.x to 2.x.x Scenario: Upgrading a journal encrypted with jrnl 1.x Given we use the config "encrypted_old.json" - When we run "jrnl -n 1" and enter + When we run "jrnl -n 1" and enter """ Y bad doggie no biscuit bad doggie no biscuit """ - Then we should see the message "Password" + Then the output should contain "Password" and the output should contain "2013-06-10 15:40 Life is good" diff --git a/jrnl/DayOneJournal.py b/jrnl/DayOneJournal.py index 9e988f78..ccec528a 100644 --- a/jrnl/DayOneJournal.py +++ b/jrnl/DayOneJournal.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals from . import Entry from . import Journal from . import time as jrnl_time @@ -83,7 +82,7 @@ class DayOne(Journal.Journal): def editable_str(self): """Turns the journal into a string of entries that can be edited manually and later be parsed with eslf.parse_editable_str.""" - return "\n".join(["# {0}\n{1}".format(e.uuid, e.__unicode__()) for e in self.entries]) + return "\n".join(["# {0}\n{1}".format(e.uuid, str(e)) for e in self.entries]) def parse_editable_str(self, edited): """Parses the output of self.editable_str and updates its entries.""" diff --git a/jrnl/EncryptedJournal.py b/jrnl/EncryptedJournal.py index a83651e4..67345f45 100644 --- a/jrnl/EncryptedJournal.py +++ b/jrnl/EncryptedJournal.py @@ -8,14 +8,13 @@ from cryptography.hazmat.backends import default_backend import sys import os import base64 -import getpass import logging log = logging.getLogger() def make_key(password): - password = util.bytes(password) + password = password.encode("utf-8") kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, @@ -48,9 +47,9 @@ class EncryptedJournal(Journal.Journal): self.config['password'] = password text = "" self._store(filename, text) - util.prompt("[Journal '{0}' created at {1}]".format(self.name, filename)) + print("[Journal '{0}' created at {1}]".format(self.name, filename), file=sys.stderr) else: - util.prompt("No password supplied for encrypted journal") + print("No password supplied for encrypted journal", file=sys.stderr) sys.exit(1) else: text = self._load(filename) @@ -59,7 +58,6 @@ class EncryptedJournal(Journal.Journal): log.debug("opened %s with %d entries", self.__class__.__name__, len(self)) return self - def _load(self, filename, password=None): """Loads an encrypted journal from a file and tries to decrypt it. If password is not provided, will look for password in the keychain diff --git a/jrnl/Entry.py b/jrnl/Entry.py index 1306cef5..cf012e80 100755 --- a/jrnl/Entry.py +++ b/jrnl/Entry.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import unicode_literals import re import textwrap from datetime import datetime @@ -52,13 +51,13 @@ class Entry: @staticmethod def tag_regex(tagsymbols): pattern = r'(?u)(?:^|\s)([{tags}][-+*#/\w]+)'.format(tags=tagsymbols) - return re.compile(pattern, re.UNICODE) + return re.compile(pattern) def _parse_tags(self): tagsymbols = self.journal.config['tagsymbols'] return set(tag.lower() for tag in re.findall(Entry.tag_regex(tagsymbols), self.text)) - def __unicode__(self): + def __str__(self): """Returns a string representation of the entry to be written into a journal file.""" date_str = self.date.strftime(self.journal.config['timeformat']) title = "[{}] {}".format(date_str, self.title.rstrip("\n ")) diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 72fe94b1..35ae801e 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals from . import Entry from . import util from . import time @@ -15,7 +14,7 @@ import logging log = logging.getLogger(__name__) -class Tag(object): +class Tag: def __init__(self, name, count=0): self.name = name self.count = count @@ -27,7 +26,7 @@ class Tag(object): return "".format(self.name) -class Journal(object): +class Journal: def __init__(self, name='default', **kwargs): self.config = { 'journal': "journal.txt", @@ -72,7 +71,7 @@ class Journal(object): filename = filename or self.config['journal'] if not os.path.exists(filename): - util.prompt("[Journal '{0}' created at {1}]".format(self.name, filename)) + print("[Journal '{0}' created at {1}]".format(self.name, filename), file=sys.stderr) self._create(filename) text = self._load(filename) @@ -96,7 +95,7 @@ class Journal(object): return True def _to_text(self): - return "\n".join([e.__unicode__() for e in self.entries]) + return "\n".join([str(e) for e in self.entries]) def _load(self, filename): raise NotImplementedError @@ -140,9 +139,6 @@ class Journal(object): entry._parse_text() return entries - def __unicode__(self): - return self.pprint() - def pprint(self, short=False): """Prettyprints the journal's entries""" sep = "\n" @@ -153,7 +149,7 @@ class Journal(object): tagre = re.compile(re.escape(tag), re.IGNORECASE) pp = re.sub(tagre, lambda match: util.colorize(match.group(0)), - pp, re.UNICODE) + pp) else: pp = re.sub( Entry.Entry.tag_regex(self.config['tagsymbols']), @@ -162,6 +158,9 @@ class Journal(object): ) return pp + def __str__(self): + return self.pprint() + def __repr__(self): return "".format(len(self.entries)) @@ -254,7 +253,7 @@ class Journal(object): def editable_str(self): """Turns the journal into a string of entries that can be edited manually and later be parsed with eslf.parse_editable_str.""" - return "\n".join([e.__unicode__() for e in self.entries]) + return "\n".join([str(e) for e in self.entries]) def parse_editable_str(self, edited): """Parses the output of self.editable_str and updates it's entries.""" @@ -347,8 +346,9 @@ def open_journal(name, config, legacy=False): from . import DayOneJournal return DayOneJournal.DayOne(**config).open() else: - util.prompt( - u"[Error: {0} is a directory, but doesn't seem to be a DayOne journal either.".format(config['journal']) + print( + u"[Error: {0} is a directory, but doesn't seem to be a DayOne journal either.".format(config['journal']), + file=sys.stderr ) sys.exit(1) diff --git a/jrnl/__main__.py b/jrnl/__main__.py index 73e08b33..b01d9ff4 100644 --- a/jrnl/__main__.py +++ b/jrnl/__main__.py @@ -1,6 +1,5 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals from . import cli diff --git a/jrnl/cli.py b/jrnl/cli.py index 65a53516..1bcdbae6 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -7,8 +7,6 @@ license: MIT, see LICENSE for more details. """ -from __future__ import unicode_literals -from __future__ import absolute_import from . import Journal from . import util from . import install @@ -91,7 +89,7 @@ def encrypt(journal, filename=None): if util.yesno("Do you want to store the password in your keychain?", default=True): util.set_keychain(journal.name, journal.config['password']) - util.prompt("Journal encrypted to {0}.".format(filename or new_journal.config['journal'])) + print("Journal encrypted to {0}.".format(filename or new_journal.config['journal']), file=sys.stderr) def decrypt(journal, filename=None): @@ -102,7 +100,7 @@ def decrypt(journal, filename=None): new_journal = Journal.PlainJournal(filename, **journal.config) new_journal.entries = journal.entries new_journal.write(filename) - util.prompt("Journal decrypted to {0}.".format(filename or new_journal.config['journal'])) + print("Journal decrypted to {0}.".format(filename or new_journal.config['journal']), file=sys.stderr) def list_journals(config): @@ -138,20 +136,19 @@ def configure_logger(debug=False): def run(manual_args=None): args = parse_args(manual_args) configure_logger(args.debug) - args.text = [p.decode('utf-8') if util.PY2 and not isinstance(p, unicode) else p for p in args.text] if args.version: version_str = "{0} version {1}".format(jrnl.__title__, jrnl.__version__) - print(util.py2encode(version_str)) + print(version_str) sys.exit(0) try: config = install.load_or_install_jrnl() except UserAbort as err: - util.prompt("\n{}".format(err)) + print("\n{}".format(err), file=sys.stderr) sys.exit(1) if args.ls: - util.prnt(list_journals(config)) + print(list_journals(config)) sys.exit(0) log.debug('Using configuration "%s"', config) @@ -164,8 +161,8 @@ def run(manual_args=None): if journal_name is not 'default': args.text = args.text[1:] elif "default" not in config['journals']: - util.prompt("No default journal configured.") - util.prompt(list_journals(config)) + print("No default journal configured.", file=sys.stderr) + print(list_journals(config), file=sys.stderr) sys.exit(1) config = util.scope_config(config, journal_name) @@ -175,12 +172,12 @@ def run(manual_args=None): try: args.limit = int(args.text[0].lstrip("-")) args.text = args.text[1:] - except: + except ValueError: pass log.debug('Using journal "%s"', journal_name) mode_compose, mode_export, mode_import = guess_mode(args, config) - + # How to quit writing? if "win32" in sys.platform: _exit_multiline_code = "on a blank line, press Ctrl+Z and then Enter" @@ -190,21 +187,22 @@ def run(manual_args=None): if mode_compose and not args.text: if not sys.stdin.isatty(): # Piping data into jrnl - raw = util.py23_read() + raw = sys.stdin.read() elif config['editor']: template = "" if config['template']: try: template = open(config['template']).read() - except: - util.prompt("[Could not read template at '']".format(config['template'])) + except IOError: + print("[Could not read template at '']".format(config['template']), file=sys.stderr) sys.exit(1) raw = util.get_text_from_editor(config, template) else: try: - raw = util.py23_read("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n") + print("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n", file=sys.stderr) + raw = sys.stdin.read() except KeyboardInterrupt: - util.prompt("[Entry NOT saved to journal.]") + print("[Entry NOT saved to journal.]", file=sys.stderr) sys.exit(0) if raw: args.text = [raw] @@ -215,7 +213,7 @@ def run(manual_args=None): try: journal = Journal.open_journal(journal_name, config) except KeyboardInterrupt: - util.prompt("[Interrupted while opening journal]".format(journal_name)) + print("[Interrupted while opening journal]".format(journal_name), file=sys.stderr) sys.exit(1) # Import mode @@ -225,11 +223,9 @@ def run(manual_args=None): # Writing mode elif mode_compose: raw = " ".join(args.text).strip() - if util.PY2 and type(raw) is not unicode: - raw = raw.decode(sys.getfilesystemencoding()) log.debug('Appending raw line "%s" to journal "%s"', raw, journal_name) journal.new_entry(raw) - util.prompt("[Entry added to {0} journal]".format(journal_name)) + print("[Entry added to {0} journal]".format(journal_name), file=sys.stderr) journal.write() if not mode_compose: @@ -246,14 +242,14 @@ def run(manual_args=None): # Reading mode if not mode_compose and not mode_export and not mode_import: - print(util.py2encode(journal.pprint())) + print(journal.pprint()) # Various export modes elif args.short: - print(util.py2encode(journal.pprint(short=True))) + print(journal.pprint(short=True)) elif args.tags: - print(util.py2encode(plugins.get_exporter("tags").export(journal))) + print(plugins.get_exporter("tags").export(journal)) elif args.export is not False: exporter = plugins.get_exporter(args.export) @@ -275,7 +271,8 @@ def run(manual_args=None): elif args.edit: if not config['editor']: - util.prompt("[{1}ERROR{2}: You need to specify an editor in {0} to use the --edit function.]".format(install.CONFIG_FILE_PATH, ERROR_COLOR, RESET_COLOR)) + print("[{1}ERROR{2}: You need to specify an editor in {0} to use the --edit function.]" + .format(install.CONFIG_FILE_PATH, ERROR_COLOR, RESET_COLOR), file=sys.stderr) sys.exit(1) other_entries = [e for e in old_entries if e not in journal.entries] # Edit @@ -290,7 +287,7 @@ def run(manual_args=None): if num_edited: prompts.append("{0} {1} modified".format(num_edited, "entry" if num_deleted == 1 else "entries")) if prompts: - util.prompt("[{0}]".format(", ".join(prompts).capitalize())) + print("[{0}]".format(", ".join(prompts).capitalize()), file=sys.stderr) journal.entries += other_entries journal.sort() journal.write() diff --git a/jrnl/export.py b/jrnl/export.py index d4873314..a69ee00e 100644 --- a/jrnl/export.py +++ b/jrnl/export.py @@ -1,15 +1,14 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals from .util import ERROR_COLOR, RESET_COLOR -from .util import slugify, u -from .template import Template +from .util import slugify +from .plugins.template import Template import os import codecs -class Exporter(object): +class Exporter: """This Exporter can convert entries and journals into text files.""" def __init__(self, format): with open("jrnl/templates/" + format + ".template") as f: @@ -17,8 +16,8 @@ class Exporter(object): self.template = Template(body) def export_entry(self, entry): - """Returns a unicode representation of a single entry.""" - return entry.__unicode__() + """Returns a string representation of a single entry.""" + return str(entry) def _get_vars(self, journal): return { @@ -28,7 +27,7 @@ class Exporter(object): } def export_journal(self, journal): - """Returns a unicode representation of an entire journal.""" + """Returns a string representation of an entire journal.""" return self.template.render_block("journal", **self._get_vars(journal)) def write_file(self, journal, path): @@ -41,7 +40,7 @@ class Exporter(object): return "[{2}ERROR{3}: {0} {1}]".format(e.filename, e.strerror, ERROR_COLOR, RESET_COLOR) def make_filename(self, entry): - return entry.date.strftime("%Y-%m-%d_{0}.{1}".format(slugify(u(entry.title)), self.extension)) + return entry.date.strftime("%Y-%m-%d_{0}.{1}".format(slugify(entry.title), self.extension)) def write_files(self, journal, path): """Exports a journal into individual files for each entry.""" @@ -57,7 +56,7 @@ class Exporter(object): def export(self, journal, format="text", output=None): """Exports to individual files if output is an existing path, or into a single file if output is a file name, or returns the exporter's - representation as unicode if output is None.""" + representation as string if output is None.""" if output and os.path.isdir(output): # multiple files return self.write_files(journal, output) elif output: # single file diff --git a/jrnl/install.py b/jrnl/install.py index 5a80562f..08bb44dd 100644 --- a/jrnl/install.py +++ b/jrnl/install.py @@ -69,7 +69,7 @@ def upgrade_config(config): for key in missing_keys: config[key] = default_config[key] save_config(config) - print("[Configuration updated to newest version at {}]".format(CONFIG_FILE_PATH)) + print("[Configuration updated to newest version at {}]".format(CONFIG_FILE_PATH), file=sys.stderr) def save_config(config): @@ -91,10 +91,10 @@ def load_or_install_jrnl(): try: upgrade.upgrade_jrnl_if_necessary(config_path) except upgrade.UpgradeValidationException: - util.prompt("Aborting upgrade.") - util.prompt("Please tell us about this problem at the following URL:") - util.prompt("https://github.com/jrnl-org/jrnl/issues/new?title=UpgradeValidationException") - util.prompt("Exiting.") + print("Aborting upgrade.", file=sys.stderr) + print("Please tell us about this problem at the following URL:", file=sys.stderr) + print("https://github.com/jrnl-org/jrnl/issues/new?title=UpgradeValidationException", file=sys.stderr) + print("Exiting.", file=sys.stderr) sys.exit(1) upgrade_config(config) @@ -121,7 +121,7 @@ def install(): # Where to create the journal? path_query = 'Path to your journal file (leave blank for {}): '.format(JOURNAL_FILE_PATH) - journal_path = util.py23_input(path_query).strip() or JOURNAL_FILE_PATH + journal_path = input(path_query).strip() or JOURNAL_FILE_PATH default_config['journals']['default'] = os.path.expanduser(os.path.expandvars(journal_path)) path = os.path.split(default_config['journals']['default'])[0] # If the folder doesn't exist, create it @@ -139,7 +139,7 @@ def install(): else: util.set_keychain("default", None) EncryptedJournal._create(default_config['journals']['default'], password) - print("Journal will be encrypted.") + print("Journal will be encrypted.", file=sys.stderr) else: PlainJournal._create(default_config['journals']['default']) diff --git a/jrnl/plugins/__init__.py b/jrnl/plugins/__init__.py index 64d7b3ba..bb2ea176 100644 --- a/jrnl/plugins/__init__.py +++ b/jrnl/plugins/__init__.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals - from .text_exporter import TextExporter from .jrnl_importer import JRNLImporter from .json_exporter import JSONExporter diff --git a/jrnl/plugins/jrnl_importer.py b/jrnl/plugins/jrnl_importer.py index 85615e75..1015c262 100644 --- a/jrnl/plugins/jrnl_importer.py +++ b/jrnl/plugins/jrnl_importer.py @@ -1,12 +1,11 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals import codecs import sys from .. import util -class JRNLImporter(object): +class JRNLImporter: """This plugin imports entries from other jrnl files.""" names = ["jrnl"] @@ -21,11 +20,11 @@ class JRNLImporter(object): other_journal_txt = f.read() else: try: - other_journal_txt = util.py23_read() + other_journal_txt = sys.stdin.read() except KeyboardInterrupt: - util.prompt("[Entries NOT imported into journal.]") + print("[Entries NOT imported into journal.]", file=sys.stderr) sys.exit(0) journal.import_(other_journal_txt) new_cnt = len(journal.entries) - util.prompt("[{0} imported to {1} journal]".format(new_cnt - old_cnt, journal.name)) + print("[{0} imported to {1} journal]".format(new_cnt - old_cnt, journal.name), file=sys.stderr) journal.write() diff --git a/jrnl/plugins/json_exporter.py b/jrnl/plugins/json_exporter.py index 5abaf916..e6591302 100644 --- a/jrnl/plugins/json_exporter.py +++ b/jrnl/plugins/json_exporter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals from .text_exporter import TextExporter import json from .util import get_tags_count diff --git a/jrnl/plugins/markdown_exporter.py b/jrnl/plugins/markdown_exporter.py index 19b5404d..0452a5f8 100644 --- a/jrnl/plugins/markdown_exporter.py +++ b/jrnl/plugins/markdown_exporter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals, print_function from .text_exporter import TextExporter import os import re diff --git a/jrnl/plugins/tag_exporter.py b/jrnl/plugins/tag_exporter.py index 439bac7c..269a762d 100644 --- a/jrnl/plugins/tag_exporter.py +++ b/jrnl/plugins/tag_exporter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals from .text_exporter import TextExporter from .util import get_tags_count diff --git a/jrnl/plugins/template.py b/jrnl/plugins/template.py index 21fb2896..7f72e2f8 100644 --- a/jrnl/plugins/template.py +++ b/jrnl/plugins/template.py @@ -13,7 +13,7 @@ BLOCK_RE = r"{% *block +(.+?) *%}((?:.|\n)+?){% *endblock *%}" INCLUDE_RE = r"{% *include +(.+?) *%}" -class Template(object): +class Template: def __init__(self, template): self.template = template self.clean_template = None diff --git a/jrnl/plugins/template_exporter.py b/jrnl/plugins/template_exporter.py index 85aa2236..ecb9ac87 100644 --- a/jrnl/plugins/template_exporter.py +++ b/jrnl/plugins/template_exporter.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals - from .text_exporter import TextExporter from .template import Template import os @@ -14,7 +12,7 @@ class GenericTemplateExporter(TextExporter): @classmethod def export_entry(cls, entry): - """Returns a unicode representation of a single entry.""" + """Returns a string representation of a single entry.""" vars = { 'entry': entry, 'tags': entry.tags @@ -23,7 +21,7 @@ class GenericTemplateExporter(TextExporter): @classmethod def export_journal(cls, journal): - """Returns a unicode representation of an entire journal.""" + """Returns a string representation of an entire journal.""" vars = { 'journal': journal, 'entries': journal.entries, diff --git a/jrnl/plugins/text_exporter.py b/jrnl/plugins/text_exporter.py index dbb54d04..f8ade519 100644 --- a/jrnl/plugins/text_exporter.py +++ b/jrnl/plugins/text_exporter.py @@ -1,26 +1,25 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals import codecs -from ..util import u, slugify +from ..util import slugify import os from ..util import ERROR_COLOR, RESET_COLOR -class TextExporter(object): +class TextExporter: """This Exporter can convert entries and journals into text files.""" names = ["text", "txt"] extension = "txt" @classmethod def export_entry(cls, entry): - """Returns a unicode representation of a single entry.""" - return entry.__unicode__() + """Returns a string representation of a single entry.""" + return str(entry) @classmethod def export_journal(cls, journal): - """Returns a unicode representation of an entire journal.""" + """Returns a string representation of an entire journal.""" return "\n".join(cls.export_entry(entry) for entry in journal) @classmethod @@ -35,7 +34,7 @@ class TextExporter(object): @classmethod def make_filename(cls, entry): - return entry.date.strftime("%Y-%m-%d_{0}.{1}".format(slugify(u(entry.title)), cls.extension)) + return entry.date.strftime("%Y-%m-%d_{0}.{1}".format(slugify(str(entry.title)), cls.extension)) @classmethod def write_files(cls, journal, path): @@ -53,7 +52,7 @@ class TextExporter(object): def export(cls, journal, output=None): """Exports to individual files if output is an existing path, or into a single file if output is a file name, or returns the exporter's - representation as unicode if output is None.""" + representation as string if output is None.""" if output and os.path.isdir(output): # multiple files return cls.write_files(journal, output) elif output: # single file diff --git a/jrnl/plugins/xml_exporter.py b/jrnl/plugins/xml_exporter.py index 0af2ed47..2783663b 100644 --- a/jrnl/plugins/xml_exporter.py +++ b/jrnl/plugins/xml_exporter.py @@ -1,10 +1,8 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals from .json_exporter import JSONExporter from .util import get_tags_count -from ..util import u from xml.dom import minidom @@ -20,7 +18,7 @@ class XMLExporter(JSONExporter): entry_el = doc_el.createElement('entry') for key, value in cls.entry_to_dict(entry).items(): elem = doc_el.createElement(key) - elem.appendChild(doc_el.createTextNode(u(value))) + elem.appendChild(doc_el.createTextNode(value)) entry_el.appendChild(elem) if not doc: doc_el.appendChild(entry_el) @@ -33,8 +31,8 @@ class XMLExporter(JSONExporter): entry_el = doc.createElement('entry') entry_el.setAttribute('date', entry.date.isoformat()) if hasattr(entry, "uuid"): - entry_el.setAttribute('uuid', u(entry.uuid)) - entry_el.setAttribute('starred', u(entry.starred)) + entry_el.setAttribute('uuid', entry.uuid) + entry_el.setAttribute('starred', entry.starred) entry_el.appendChild(doc.createTextNode(entry.fulltext)) return entry_el @@ -49,7 +47,7 @@ class XMLExporter(JSONExporter): for count, tag in tags: tag_el = doc.createElement('tag') tag_el.setAttribute('name', tag) - count_node = doc.createTextNode(u(count)) + count_node = doc.createTextNode(str(count)) tag_el.appendChild(count_node) tags_el.appendChild(tag_el) for entry in journal.entries: diff --git a/jrnl/plugins/yaml_exporter.py b/jrnl/plugins/yaml_exporter.py index c0735811..eec9b0b3 100644 --- a/jrnl/plugins/yaml_exporter.py +++ b/jrnl/plugins/yaml_exporter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import, unicode_literals, print_function from .text_exporter import TextExporter import os import re @@ -27,7 +26,7 @@ class YAMLExporter(TextExporter): tagsymbols = entry.journal.config['tagsymbols'] # see also Entry.Entry.rag_regex - multi_tag_regex = re.compile(r'(?u)^\s*([{tags}][-+*#/\w]+\s*)+$'.format(tags=tagsymbols), re.UNICODE) + multi_tag_regex = re.compile(r'(?u)^\s*([{tags}][-+*#/\w]+\s*)+$'.format(tags=tagsymbols)) '''Increase heading levels in body text''' newbody = '' diff --git a/jrnl/upgrade.py b/jrnl/upgrade.py index f2b80af3..2a5c1192 100644 --- a/jrnl/upgrade.py +++ b/jrnl/upgrade.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +import sys from . import __version__ from . import Journal @@ -10,7 +10,7 @@ import codecs def backup(filename, binary=False): - util.prompt(" Created a backup at {}.backup".format(filename)) + print(" Created a backup at {}.backup".format(filename), file=sys.stderr) filename = os.path.expanduser(os.path.expandvars(filename)) with open(filename, 'rb' if binary else 'r') as original: contents = original.read() @@ -26,7 +26,7 @@ def upgrade_jrnl_if_necessary(config_path): config = util.load_config(config_path) - util.prompt("""Welcome to jrnl {}. + print("""Welcome to jrnl {}. It looks like you've been using an older version of jrnl until now. That's okay - jrnl will now upgrade your configuration and journal files. Afterwards @@ -63,19 +63,19 @@ older versions of jrnl anymore. longest_journal_name = max([len(journal) for journal in config['journals']]) if encrypted_journals: - util.prompt("\nFollowing encrypted journals will be upgraded to jrnl {}:".format(__version__)) + print("\nFollowing encrypted journals will be upgraded to jrnl {}:".format(__version__), file=sys.stderr) for journal, path in encrypted_journals.items(): - util.prompt(" {:{pad}} -> {}".format(journal, path, pad=longest_journal_name)) + print(" {:{pad}} -> {}".format(journal, path, pad=longest_journal_name), file=sys.stderr) if plain_journals: - util.prompt("\nFollowing plain text journals will upgraded to jrnl {}:".format(__version__)) + print("\nFollowing plain text journals will upgraded to jrnl {}:".format(__version__), file=sys.stderr) for journal, path in plain_journals.items(): - util.prompt(" {:{pad}} -> {}".format(journal, path, pad=longest_journal_name)) + print(" {:{pad}} -> {}".format(journal, path, pad=longest_journal_name), file=sys.stderr) if other_journals: - util.prompt("\nFollowing journals will be not be touched:") + print("\nFollowing journals will be not be touched:", file=sys.stderr) for journal, path in other_journals.items(): - util.prompt(" {:{pad}} -> {}".format(journal, path, pad=longest_journal_name)) + print(" {:{pad}} -> {}".format(journal, path, pad=longest_journal_name), file=sys.stderr) try: cont = util.yesno("\nContinue upgrading jrnl?", default=False) @@ -85,13 +85,13 @@ older versions of jrnl anymore. raise UserAbort("jrnl NOT upgraded, exiting.") for journal_name, path in encrypted_journals.items(): - util.prompt("\nUpgrading encrypted '{}' journal stored in {}...".format(journal_name, path)) + print("\nUpgrading encrypted '{}' journal stored in {}...".format(journal_name, path), file=sys.stderr) backup(path, binary=True) old_journal = Journal.open_journal(journal_name, util.scope_config(config, journal_name), legacy=True) all_journals.append(EncryptedJournal.from_journal(old_journal)) for journal_name, path in plain_journals.items(): - util.prompt("\nUpgrading plain text '{}' journal stored in {}...".format(journal_name, path)) + print("\nUpgrading plain text '{}' journal stored in {}...".format(journal_name, path), file=sys.stderr) backup(path) old_journal = Journal.open_journal(journal_name, util.scope_config(config, journal_name), legacy=True) all_journals.append(Journal.PlainJournal.from_journal(old_journal)) @@ -100,8 +100,9 @@ older versions of jrnl anymore. failed_journals = [j for j in all_journals if not j.validate_parsing()] if len(failed_journals) > 0: - util.prompt("\nThe following journal{} failed to upgrade:\n{}".format( - 's' if len(failed_journals) > 1 else '', "\n".join(j.name for j in failed_journals)) + print("\nThe following journal{} failed to upgrade:\n{}".format( + 's' if len(failed_journals) > 1 else '', "\n".join(j.name for j in failed_journals)), + file=sys.stderr ) raise UpgradeValidationException @@ -110,10 +111,11 @@ older versions of jrnl anymore. for j in all_journals: j.write() - util.prompt("\nUpgrading config...") + print("\nUpgrading config...", file=sys.stderr) backup(config_path) - util.prompt("\nWe're all done here and you can start enjoying jrnl 2.".format(config_path)) + print("\nWe're all done here and you can start enjoying jrnl 2.".format(config_path), file=sys.stderr) + class UpgradeValidationException(Exception): """Raised when the contents of an upgraded journal do not match the old journal""" diff --git a/jrnl/util.py b/jrnl/util.py index bc36ba9b..7b5e2b5c 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -1,9 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import unicode_literals -from __future__ import absolute_import - import sys import os import getpass as gp @@ -21,15 +18,6 @@ import logging log = logging.getLogger(__name__) - -PY3 = sys.version_info[0] == 3 -PY2 = sys.version_info[0] == 2 -STDIN = sys.stdin -STDERR = sys.stderr -STDOUT = sys.stdout -TEST = False -__cached_tz = None - WARNING_COLOR = "\033[33m" ERROR_COLOR = "\033[31m" RESET_COLOR = "\033[0m" @@ -44,18 +32,14 @@ SENTENCE_SPLITTER = re.compile(r""" \s+ # a sequence of required spaces. | # Otherwise, \n # a sentence also terminates newlines. -)""", re.UNICODE | re.VERBOSE) +)""", re.VERBOSE) class UserAbort(Exception): pass -def getpass(prompt="Password: "): - if not TEST: - return gp.getpass(bytes(prompt)) - else: - return py23_input(prompt) +getpass = gp.getpass def get_password(validator, keychain=None, max_attempts=3): @@ -67,14 +51,14 @@ def get_password(validator, keychain=None, max_attempts=3): set_keychain(keychain, None) attempt = 1 while result is None and attempt < max_attempts: - prompt("Wrong password, try again.") - password = getpass() + print("Wrong password, try again.", file=sys.stderr) + password = gp.getpass() result = validator(password) attempt += 1 if result is not None: return result else: - prompt("Extremely wrong password.") + print("Extremely wrong password.", file=sys.stderr) sys.exit(1) @@ -88,57 +72,16 @@ def set_keychain(journal_name, password): if password is None: try: keyring.delete_password('jrnl', journal_name) - except: + except RuntimeError: pass - elif not TEST: + else: keyring.set_password('jrnl', journal_name, password) -def u(s): - """Mock unicode function for python 2 and 3 compatibility.""" - if not isinstance(s, str): - s = str(s) - return s if PY3 or type(s) is unicode else s.decode("utf-8") - - -def py2encode(s): - """Encodes to UTF-8 in Python 2 but not in Python 3.""" - return s.encode("utf-8") if PY2 and type(s) is unicode else s - - -def bytes(s): - """Returns bytes, no matter what.""" - if PY3: - return s.encode("utf-8") if type(s) is not bytes else s - return s.encode("utf-8") if type(s) is unicode else s - - -def prnt(s): - """Encode and print a string""" - STDOUT.write(u(s + "\n")) - - -def prompt(msg): - """Prints a message to the std err stream defined in util.""" - if not msg.endswith("\n"): - msg += "\n" - STDERR.write(u(msg)) - - -def py23_input(msg=""): - prompt(msg) - return STDIN.readline().strip() - - -def py23_read(msg=""): - print(msg) - return STDIN.read() - - def yesno(prompt, default=True): - prompt = prompt.strip() + (" [Y/n]" if default else " [y/N]") - raw = py23_input(prompt) - return {'y': True, 'n': False}.get(raw.lower(), default) + prompt = f"{prompt.strip()} {'[Y/n]' if default else '[y/N]'}" + response = input(prompt) + return {"y": True, "n": False}.get(response.lower(), default) def load_config(config_path): @@ -176,7 +119,7 @@ def get_text_from_editor(config, template=""): os.close(filehandle) os.remove(tmpfile) if not raw: - prompt('[Nothing saved to file]') + print('[Nothing saved to file]', file=sys.stderr) return raw @@ -188,27 +131,11 @@ def colorize(string): def slugify(string): """Slugifies a string. Based on public domain code from https://github.com/zacharyvoase/slugify - and ported to deal with all kinds of python 2 and 3 strings """ - string = u(string) - ascii_string = str(unicodedata.normalize('NFKD', string).encode('ascii', 'ignore')) - if PY3: - ascii_string = ascii_string[1:] # removed the leading 'b' - no_punctuation = re.sub(r'[^\w\s-]', '', ascii_string).strip().lower() + normalized_string = str(unicodedata.normalize('NFKD', string)) + no_punctuation = re.sub(r'[^\w\s-]', '', normalized_string).strip().lower() slug = re.sub(r'[-\s]+', '-', no_punctuation) - return u(slug) - - -def int2byte(i): - """Converts an integer to a byte. - This is equivalent to chr() in Python 2 and bytes((i,)) in Python 3.""" - return chr(i) if PY2 else bytes((i,)) - - -def byte2int(b): - """Converts a byte to an integer. - This is equivalent to ord(bs[0]) on Python 2 and bs[0] on Python 3.""" - return ord(b)if PY2 else b + return slug def split_title(text): From 1c403904e57a9b2f9c83d51740003e183a598183 Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Fri, 1 Nov 2019 10:37:12 +0100 Subject: [PATCH 04/93] fstring wip --- jrnl/DayOneJournal.py | 2 +- jrnl/Journal.py | 2 +- jrnl/cli.py | 2 +- jrnl/plugins/markdown_exporter.py | 12 ++++-------- jrnl/plugins/template_exporter.py | 2 +- jrnl/plugins/text_exporter.py | 4 ++-- jrnl/util.py | 2 +- 7 files changed, 11 insertions(+), 15 deletions(-) diff --git a/jrnl/DayOneJournal.py b/jrnl/DayOneJournal.py index ccec528a..30846f71 100644 --- a/jrnl/DayOneJournal.py +++ b/jrnl/DayOneJournal.py @@ -82,7 +82,7 @@ class DayOne(Journal.Journal): def editable_str(self): """Turns the journal into a string of entries that can be edited manually and later be parsed with eslf.parse_editable_str.""" - return "\n".join(["# {0}\n{1}".format(e.uuid, str(e)) for e in self.entries]) + return "\n".join([f"# {e.uuid}\n{str(e)}" for e in self.entries]) def parse_editable_str(self, edited): """Parses the output of self.editable_str and updates its entries.""" diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 35ae801e..aeab7385 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -23,7 +23,7 @@ class Tag: return self.name def __repr__(self): - return "".format(self.name) + return f"" class Journal: diff --git a/jrnl/cli.py b/jrnl/cli.py index 1bcdbae6..464c8c39 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -144,7 +144,7 @@ def run(manual_args=None): try: config = install.load_or_install_jrnl() except UserAbort as err: - print("\n{}".format(err), file=sys.stderr) + print(f"\n{err}", file=sys.stderr) sys.exit(1) if args.ls: diff --git a/jrnl/plugins/markdown_exporter.py b/jrnl/plugins/markdown_exporter.py index 0452a5f8..da2b5748 100644 --- a/jrnl/plugins/markdown_exporter.py +++ b/jrnl/plugins/markdown_exporter.py @@ -50,15 +50,11 @@ class MarkdownExporter(TextExporter): newbody = newbody + previous_line # add very last line if warn_on_heading_level is True: - print("{}WARNING{}: Headings increased past H6 on export - {} {}".format(WARNING_COLOR, RESET_COLOR, date_str, entry.title), file=sys.stderr) + print(f"{WARNING_COLOR}WARNING{RESET_COLOR}: " + f"Headings increased past H6 on export - {date_str} {entry.title}", + file=sys.stderr) - return "{md} {date} {title}\n{body} {space}".format( - md=heading, - date=date_str, - title=entry.title, - body=newbody, - space="" - ) + return f"{heading} {date_str} {entry.title}\n{newbody} " @classmethod def export_journal(cls, journal): diff --git a/jrnl/plugins/template_exporter.py b/jrnl/plugins/template_exporter.py index ecb9ac87..4ae618ba 100644 --- a/jrnl/plugins/template_exporter.py +++ b/jrnl/plugins/template_exporter.py @@ -34,7 +34,7 @@ def __exporter_from_file(template_file): """Create a template class from a file""" name = os.path.basename(template_file).replace(".template", "") template = Template.from_file(template_file) - return type(str("{}Exporter".format(name.title())), (GenericTemplateExporter, ), { + return type(str(f"{name.title(}Exporter")), (GenericTemplateExporter, ), { "names": [name], "extension": template.extension, "template": template diff --git a/jrnl/plugins/text_exporter.py b/jrnl/plugins/text_exporter.py index f8ade519..c72e8d93 100644 --- a/jrnl/plugins/text_exporter.py +++ b/jrnl/plugins/text_exporter.py @@ -28,9 +28,9 @@ class TextExporter: try: with codecs.open(path, "w", "utf-8") as f: f.write(cls.export_journal(journal)) - return "[Journal exported to {0}]".format(path) + return f"[Journal exported to {path}]" except IOError as e: - return "[{2}ERROR{3}: {0} {1}]".format(e.filename, e.strerror, ERROR_COLOR, RESET_COLOR) + return f"[{ERROR_COLOR}ERROR{RESET_COLOR}: {e.filename} {e.strerror}]" @classmethod def make_filename(cls, entry): diff --git a/jrnl/util.py b/jrnl/util.py index 7b5e2b5c..52f7e4c4 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -125,7 +125,7 @@ def get_text_from_editor(config, template=""): def colorize(string): """Returns the string wrapped in cyan ANSI escape""" - return u"\033[36m{}\033[39m".format(string) + return f"\033[36m{string}\033[39m" def slugify(string): From 30caf9cae2d2dfeee21c088705897b138ed8390e Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Fri, 1 Nov 2019 11:16:12 +0100 Subject: [PATCH 05/93] Run pyupgrade --- jrnl/DayOneJournal.py | 5 ++--- jrnl/EncryptedJournal.py | 6 +++--- jrnl/Entry.py | 7 +++---- jrnl/Journal.py | 19 +++++++++---------- jrnl/__init__.py | 1 - jrnl/__main__.py | 1 - jrnl/cli.py | 25 ++++++++++++------------- jrnl/export.py | 15 +++++++-------- jrnl/install.py | 6 ++---- jrnl/time.py | 2 +- jrnl/upgrade.py | 12 ++++++------ jrnl/util.py | 1 - 12 files changed, 45 insertions(+), 55 deletions(-) diff --git a/jrnl/DayOneJournal.py b/jrnl/DayOneJournal.py index 30846f71..59314c4b 100644 --- a/jrnl/DayOneJournal.py +++ b/jrnl/DayOneJournal.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# encoding: utf-8 from . import Entry from . import Journal @@ -25,7 +24,7 @@ class DayOne(Journal.Journal): def __init__(self, **kwargs): self.entries = [] self._deleted_entries = [] - super(DayOne, self).__init__(**kwargs) + super().__init__(**kwargs) def open(self): filenames = [os.path.join(self.config['journal'], "entries", f) for f in os.listdir(os.path.join(self.config['journal'], "entries"))] @@ -106,7 +105,7 @@ class DayOne(Journal.Journal): current_entry.modified = False current_entry.uuid = m.group(1).lower() else: - date_blob_re = re.compile("^\[[^\\]]+\] ") + date_blob_re = re.compile("^\\[[^\\]]+\\] ") date_blob = date_blob_re.findall(line) if date_blob: date_blob = date_blob[0] diff --git a/jrnl/EncryptedJournal.py b/jrnl/EncryptedJournal.py index 67345f45..1cce66b8 100644 --- a/jrnl/EncryptedJournal.py +++ b/jrnl/EncryptedJournal.py @@ -29,7 +29,7 @@ def make_key(password): class EncryptedJournal(Journal.Journal): def __init__(self, name='default', **kwargs): - super(EncryptedJournal, self).__init__(name, **kwargs) + super().__init__(name, **kwargs) self.config['encrypt'] = True def open(self, filename=None): @@ -47,7 +47,7 @@ class EncryptedJournal(Journal.Journal): self.config['password'] = password text = "" self._store(filename, text) - print("[Journal '{0}' created at {1}]".format(self.name, filename), file=sys.stderr) + print(f"[Journal '{self.name}' created at {filename}]", file=sys.stderr) else: print("No password supplied for encrypted journal", file=sys.stderr) sys.exit(1) @@ -97,7 +97,7 @@ class LegacyEncryptedJournal(Journal.LegacyJournal): """Legacy class to support opening journals encrypted with the jrnl 1.x standard. You'll not be able to save these journals anymore.""" def __init__(self, name='default', **kwargs): - super(LegacyEncryptedJournal, self).__init__(name, **kwargs) + super().__init__(name, **kwargs) self.config['encrypt'] = True def _load(self, filename, password=None): diff --git a/jrnl/Entry.py b/jrnl/Entry.py index cf012e80..ca80b231 100755 --- a/jrnl/Entry.py +++ b/jrnl/Entry.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# encoding: utf-8 import re import textwrap @@ -50,12 +49,12 @@ class Entry: @staticmethod def tag_regex(tagsymbols): - pattern = r'(?u)(?:^|\s)([{tags}][-+*#/\w]+)'.format(tags=tagsymbols) + pattern = fr'(?u)(?:^|\s)([{tagsymbols}][-+*#/\w]+)' return re.compile(pattern) def _parse_tags(self): tagsymbols = self.journal.config['tagsymbols'] - return set(tag.lower() for tag in re.findall(Entry.tag_regex(tagsymbols), self.text)) + return {tag.lower() for tag in re.findall(Entry.tag_regex(tagsymbols), self.text)} def __str__(self): """Returns a string representation of the entry to be written into a journal file.""" @@ -105,7 +104,7 @@ class Entry: ) def __repr__(self): - return "".format(self.title.strip(), self.date.strftime("%Y-%m-%d %H:%M")) + return "".format(self.title.strip(), self.date.strftime("%Y-%m-%d %H:%M")) def __hash__(self): return hash(self.__repr__()) diff --git a/jrnl/Journal.py b/jrnl/Journal.py index aeab7385..e5f87e03 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# encoding: utf-8 from . import Entry from . import util @@ -71,7 +70,7 @@ class Journal: filename = filename or self.config['journal'] if not os.path.exists(filename): - print("[Journal '{0}' created at {1}]".format(self.name, filename), file=sys.stderr) + print(f"[Journal '{self.name}' created at {filename}]", file=sys.stderr) self._create(filename) text = self._load(filename) @@ -117,7 +116,7 @@ class Journal: # Initialise our current entry entries = [] - date_blob_re = re.compile("(?:^|\n)\[([^\\]]+)\] ") + date_blob_re = re.compile("(?:^|\n)\\[([^\\]]+)\\] ") last_entry_pos = 0 for match in date_blob_re.finditer(journal_txt): date_blob = match.groups()[0] @@ -162,7 +161,7 @@ class Journal: return self.pprint() def __repr__(self): - return "".format(len(self.entries)) + return f"" def sort(self): """Sorts the Journal's entries by date""" @@ -182,7 +181,7 @@ class Journal: for entry in self.entries for tag in set(entry.tags)] # To be read: [for entry in journal.entries: for tag in set(entry.tags): tag] - tag_counts = set([(tags.count(tag), tag) for tag in tags]) + tag_counts = {(tags.count(tag), tag) for tag in tags} return [Tag(tag, count=count) for count, tag in sorted(tag_counts)] def filter(self, tags=[], start_date=None, end_date=None, starred=False, strict=False, short=False, exclude=[]): @@ -199,8 +198,8 @@ class Journal: exclude is a list of the tags which should not appear in the results. entry is kept if any tag is present, unless they appear in exclude.""" - self.search_tags = set([tag.lower() for tag in tags]) - excluded_tags = set([tag.lower() for tag in exclude]) + self.search_tags = {tag.lower() for tag in tags} + excluded_tags = {tag.lower() for tag in exclude} end_date = time.parse(end_date, inclusive=True) start_date = time.parse(start_date) @@ -225,7 +224,7 @@ class Journal: raw = raw.replace('\\n ', '\n').replace('\\n', '\n') starred = False # Split raw text into title and body - sep = re.search("\n|[\?!.]+ +\n?", raw) + sep = re.search("\n|[\\?!.]+ +\n?", raw) first_line = raw[:sep.end()].strip() if sep else raw starred = False @@ -322,7 +321,7 @@ class LegacyJournal(Journal): # escaping for the new format). line = new_date_format_regex.sub(r' \1', line) if current_entry: - current_entry.text += line + u"\n" + current_entry.text += line + "\n" # Append last entry if current_entry: @@ -347,7 +346,7 @@ def open_journal(name, config, legacy=False): return DayOneJournal.DayOne(**config).open() else: print( - u"[Error: {0} is a directory, but doesn't seem to be a DayOne journal either.".format(config['journal']), + f"[Error: {config['journal']} is a directory, but doesn't seem to be a DayOne journal either.", file=sys.stderr ) diff --git a/jrnl/__init__.py b/jrnl/__init__.py index 57664dbb..1905b195 100644 --- a/jrnl/__init__.py +++ b/jrnl/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# encoding: utf-8 import pkg_resources diff --git a/jrnl/__main__.py b/jrnl/__main__.py index b01d9ff4..3ce86730 100644 --- a/jrnl/__main__.py +++ b/jrnl/__main__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# encoding: utf-8 from . import cli diff --git a/jrnl/cli.py b/jrnl/cli.py index 464c8c39..3cf67030 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# encoding: utf-8 """ jrnl @@ -89,7 +88,7 @@ def encrypt(journal, filename=None): if util.yesno("Do you want to store the password in your keychain?", default=True): util.set_keychain(journal.name, journal.config['password']) - print("Journal encrypted to {0}.".format(filename or new_journal.config['journal']), file=sys.stderr) + print("Journal encrypted to {}.".format(filename or new_journal.config['journal']), file=sys.stderr) def decrypt(journal, filename=None): @@ -100,12 +99,12 @@ def decrypt(journal, filename=None): new_journal = Journal.PlainJournal(filename, **journal.config) new_journal.entries = journal.entries new_journal.write(filename) - print("Journal decrypted to {0}.".format(filename or new_journal.config['journal']), file=sys.stderr) + print("Journal decrypted to {}.".format(filename or new_journal.config['journal']), file=sys.stderr) def list_journals(config): """List the journals specified in the configuration file""" - result = "Journals defined in {}\n".format(install.CONFIG_FILE_PATH) + result = f"Journals defined in {install.CONFIG_FILE_PATH}\n" ml = min(max(len(k) for k in config['journals']), 20) for journal, cfg in config['journals'].items(): result += " * {:{}} -> {}\n".format(journal, ml, cfg['journal'] if isinstance(cfg, dict) else cfg) @@ -137,7 +136,7 @@ def run(manual_args=None): args = parse_args(manual_args) configure_logger(args.debug) if args.version: - version_str = "{0} version {1}".format(jrnl.__title__, jrnl.__version__) + version_str = f"{jrnl.__title__} version {jrnl.__version__}" print(version_str) sys.exit(0) @@ -158,7 +157,7 @@ def run(manual_args=None): # use this! journal_name = args.text[0] if (args.text and args.text[0] in config['journals']) else 'default' - if journal_name is not 'default': + if journal_name != 'default': args.text = args.text[1:] elif "default" not in config['journals']: print("No default journal configured.", file=sys.stderr) @@ -193,8 +192,8 @@ def run(manual_args=None): if config['template']: try: template = open(config['template']).read() - except IOError: - print("[Could not read template at '']".format(config['template']), file=sys.stderr) + except OSError: + print(f"[Could not read template at '{config['template']}']", file=sys.stderr) sys.exit(1) raw = util.get_text_from_editor(config, template) else: @@ -213,7 +212,7 @@ def run(manual_args=None): try: journal = Journal.open_journal(journal_name, config) except KeyboardInterrupt: - print("[Interrupted while opening journal]".format(journal_name), file=sys.stderr) + print(f"[Interrupted while opening journal]", file=sys.stderr) sys.exit(1) # Import mode @@ -225,7 +224,7 @@ def run(manual_args=None): raw = " ".join(args.text).strip() log.debug('Appending raw line "%s" to journal "%s"', raw, journal_name) journal.new_entry(raw) - print("[Entry added to {0} journal]".format(journal_name), file=sys.stderr) + print(f"[Entry added to {journal_name} journal]", file=sys.stderr) journal.write() if not mode_compose: @@ -283,11 +282,11 @@ def run(manual_args=None): num_edited = len([e for e in journal.entries if e.modified]) prompts = [] if num_deleted: - prompts.append("{0} {1} deleted".format(num_deleted, "entry" if num_deleted == 1 else "entries")) + prompts.append("{} {} deleted".format(num_deleted, "entry" if num_deleted == 1 else "entries")) if num_edited: - prompts.append("{0} {1} modified".format(num_edited, "entry" if num_deleted == 1 else "entries")) + prompts.append("{} {} modified".format(num_edited, "entry" if num_deleted == 1 else "entries")) if prompts: - print("[{0}]".format(", ".join(prompts).capitalize()), file=sys.stderr) + print("[{}]".format(", ".join(prompts).capitalize()), file=sys.stderr) journal.entries += other_entries journal.sort() journal.write() diff --git a/jrnl/export.py b/jrnl/export.py index a69ee00e..399f2db0 100644 --- a/jrnl/export.py +++ b/jrnl/export.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# encoding: utf-8 from .util import ERROR_COLOR, RESET_COLOR from .util import slugify @@ -35,12 +34,12 @@ class Exporter: try: with codecs.open(path, "w", "utf-8") as f: f.write(self.export_journal(journal)) - return "[Journal exported to {0}]".format(path) - except IOError as e: - return "[{2}ERROR{3}: {0} {1}]".format(e.filename, e.strerror, ERROR_COLOR, RESET_COLOR) + return f"[Journal exported to {path}]" + except OSError as e: + return f"[{ERROR_COLOR}ERROR{RESET_COLOR}: {e.filename} {e.strerror}]" def make_filename(self, entry): - return entry.date.strftime("%Y-%m-%d_{0}.{1}".format(slugify(entry.title), self.extension)) + return entry.date.strftime("%Y-%m-%d_{}.{}".format(slugify(entry.title), self.extension)) def write_files(self, journal, path): """Exports a journal into individual files for each entry.""" @@ -49,9 +48,9 @@ class Exporter: full_path = os.path.join(path, self.make_filename(entry)) with codecs.open(full_path, "w", "utf-8") as f: f.write(self.export_entry(entry)) - except IOError as e: - return "[{2}ERROR{3}: {0} {1}]".format(e.filename, e.strerror, ERROR_COLOR, RESET_COLOR) - return "[Journal exported to {0}]".format(path) + except OSError as e: + return f"[{ERROR_COLOR}ERROR{RESET_COLOR}: {e.filename} {e.strerror}]" + return f"[Journal exported to {path}]" def export(self, journal, format="text", output=None): """Exports to individual files if output is an existing path, or into diff --git a/jrnl/install.py b/jrnl/install.py index 08bb44dd..c104b46b 100644 --- a/jrnl/install.py +++ b/jrnl/install.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -# encoding: utf-8 -from __future__ import absolute_import import readline import glob import getpass @@ -69,7 +67,7 @@ def upgrade_config(config): for key in missing_keys: config[key] = default_config[key] save_config(config) - print("[Configuration updated to newest version at {}]".format(CONFIG_FILE_PATH), file=sys.stderr) + print(f"[Configuration updated to newest version at {CONFIG_FILE_PATH}]", file=sys.stderr) def save_config(config): @@ -120,7 +118,7 @@ def install(): readline.set_completer(autocomplete) # Where to create the journal? - path_query = 'Path to your journal file (leave blank for {}): '.format(JOURNAL_FILE_PATH) + path_query = f'Path to your journal file (leave blank for {JOURNAL_FILE_PATH}): ' journal_path = input(path_query).strip() or JOURNAL_FILE_PATH default_config['journals']['default'] = os.path.expanduser(os.path.expandvars(journal_path)) diff --git a/jrnl/time.py b/jrnl/time.py index 9ff125aa..eee3fc20 100644 --- a/jrnl/time.py +++ b/jrnl/time.py @@ -51,7 +51,7 @@ def parse(date_str, inclusive=False, default_hour=None, default_minute=None): except TypeError: return None - if flag is 1: # Date found, but no time. Use the default time. + if flag == 1: # Date found, but no time. Use the default time. date = datetime(*date[:3], hour=default_hour or 0, minute=default_minute or 0) else: date = datetime(*date[:6]) diff --git a/jrnl/upgrade.py b/jrnl/upgrade.py index 2a5c1192..d1b83809 100644 --- a/jrnl/upgrade.py +++ b/jrnl/upgrade.py @@ -10,7 +10,7 @@ import codecs def backup(filename, binary=False): - print(" Created a backup at {}.backup".format(filename), file=sys.stderr) + print(f" Created a backup at {filename}.backup", file=sys.stderr) filename = os.path.expanduser(os.path.expandvars(filename)) with open(filename, 'rb' if binary else 'r') as original: contents = original.read() @@ -63,12 +63,12 @@ older versions of jrnl anymore. longest_journal_name = max([len(journal) for journal in config['journals']]) if encrypted_journals: - print("\nFollowing encrypted journals will be upgraded to jrnl {}:".format(__version__), file=sys.stderr) + print(f"\nFollowing encrypted journals will be upgraded to jrnl {__version__}:", file=sys.stderr) for journal, path in encrypted_journals.items(): print(" {:{pad}} -> {}".format(journal, path, pad=longest_journal_name), file=sys.stderr) if plain_journals: - print("\nFollowing plain text journals will upgraded to jrnl {}:".format(__version__), file=sys.stderr) + print(f"\nFollowing plain text journals will upgraded to jrnl {__version__}:", file=sys.stderr) for journal, path in plain_journals.items(): print(" {:{pad}} -> {}".format(journal, path, pad=longest_journal_name), file=sys.stderr) @@ -85,13 +85,13 @@ older versions of jrnl anymore. raise UserAbort("jrnl NOT upgraded, exiting.") for journal_name, path in encrypted_journals.items(): - print("\nUpgrading encrypted '{}' journal stored in {}...".format(journal_name, path), file=sys.stderr) + print(f"\nUpgrading encrypted '{journal_name}' journal stored in {path}...", file=sys.stderr) backup(path, binary=True) old_journal = Journal.open_journal(journal_name, util.scope_config(config, journal_name), legacy=True) all_journals.append(EncryptedJournal.from_journal(old_journal)) for journal_name, path in plain_journals.items(): - print("\nUpgrading plain text '{}' journal stored in {}...".format(journal_name, path), file=sys.stderr) + print(f"\nUpgrading plain text '{journal_name}' journal stored in {path}...", file=sys.stderr) backup(path) old_journal = Journal.open_journal(journal_name, util.scope_config(config, journal_name), legacy=True) all_journals.append(Journal.PlainJournal.from_journal(old_journal)) @@ -114,7 +114,7 @@ older versions of jrnl anymore. print("\nUpgrading config...", file=sys.stderr) backup(config_path) - print("\nWe're all done here and you can start enjoying jrnl 2.".format(config_path), file=sys.stderr) + print("\nWe're all done here and you can start enjoying jrnl 2.", file=sys.stderr) class UpgradeValidationException(Exception): diff --git a/jrnl/util.py b/jrnl/util.py index 52f7e4c4..d2bdf9c4 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# encoding: utf-8 import sys import os From dace253513dc0c750c6f5a223302262ca4f9b26e Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Fri, 1 Nov 2019 13:16:10 +0100 Subject: [PATCH 06/93] fix broken pyupgrade fstring --- jrnl/plugins/template_exporter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/plugins/template_exporter.py b/jrnl/plugins/template_exporter.py index 4ae618ba..f15328f2 100644 --- a/jrnl/plugins/template_exporter.py +++ b/jrnl/plugins/template_exporter.py @@ -34,7 +34,7 @@ def __exporter_from_file(template_file): """Create a template class from a file""" name = os.path.basename(template_file).replace(".template", "") template = Template.from_file(template_file) - return type(str(f"{name.title(}Exporter")), (GenericTemplateExporter, ), { + return type(str(f"{name.title()}Exporter"), (GenericTemplateExporter, ), { "names": [name], "extension": template.extension, "template": template From 5cee4fb783fbc47a48a7c5df33595c1200ee4900 Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Fri, 1 Nov 2019 19:57:24 +0100 Subject: [PATCH 07/93] run pyupgrade on plugin dir --- jrnl/plugins/__init__.py | 4 ++-- jrnl/plugins/jrnl_importer.py | 2 +- jrnl/plugins/json_exporter.py | 2 +- jrnl/plugins/tag_exporter.py | 2 +- jrnl/plugins/text_exporter.py | 4 ++-- jrnl/plugins/util.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/jrnl/plugins/__init__.py b/jrnl/plugins/__init__.py index bb2ea176..8d59b556 100644 --- a/jrnl/plugins/__init__.py +++ b/jrnl/plugins/__init__.py @@ -13,8 +13,8 @@ from .template_exporter import __all__ as template_exporters __exporters =[JSONExporter, MarkdownExporter, TagExporter, TextExporter, XMLExporter, YAMLExporter] + template_exporters __importers =[JRNLImporter] -__exporter_types = dict([(name, plugin) for plugin in __exporters for name in plugin.names]) -__importer_types = dict([(name, plugin) for plugin in __importers for name in plugin.names]) +__exporter_types = {name: plugin for plugin in __exporters for name in plugin.names} +__importer_types = {name: plugin for plugin in __importers for name in plugin.names} EXPORT_FORMATS = sorted(__exporter_types.keys()) IMPORT_FORMATS = sorted(__importer_types.keys()) diff --git a/jrnl/plugins/jrnl_importer.py b/jrnl/plugins/jrnl_importer.py index 1015c262..e2145232 100644 --- a/jrnl/plugins/jrnl_importer.py +++ b/jrnl/plugins/jrnl_importer.py @@ -26,5 +26,5 @@ class JRNLImporter: sys.exit(0) journal.import_(other_journal_txt) new_cnt = len(journal.entries) - print("[{0} imported to {1} journal]".format(new_cnt - old_cnt, journal.name), file=sys.stderr) + print("[{} imported to {} journal]".format(new_cnt - old_cnt, journal.name), file=sys.stderr) journal.write() diff --git a/jrnl/plugins/json_exporter.py b/jrnl/plugins/json_exporter.py index e6591302..e368a300 100644 --- a/jrnl/plugins/json_exporter.py +++ b/jrnl/plugins/json_exporter.py @@ -34,7 +34,7 @@ class JSONExporter(TextExporter): """Returns a json representation of an entire journal.""" tags = get_tags_count(journal) result = { - "tags": dict((tag, count) for count, tag in tags), + "tags": {tag: count for count, tag in tags}, "entries": [cls.entry_to_dict(e) for e in journal.entries] } return json.dumps(result, indent=2) diff --git a/jrnl/plugins/tag_exporter.py b/jrnl/plugins/tag_exporter.py index 269a762d..f5453ced 100644 --- a/jrnl/plugins/tag_exporter.py +++ b/jrnl/plugins/tag_exporter.py @@ -25,5 +25,5 @@ class TagExporter(TextExporter): elif min(tag_counts)[0] == 0: tag_counts = filter(lambda x: x[0] > 1, tag_counts) result += '[Removed tags that appear only once.]\n' - result += "\n".join("{0:20} : {1}".format(tag, n) for n, tag in sorted(tag_counts, reverse=True)) + result += "\n".join("{:20} : {}".format(tag, n) for n, tag in sorted(tag_counts, reverse=True)) return result diff --git a/jrnl/plugins/text_exporter.py b/jrnl/plugins/text_exporter.py index c72e8d93..687de750 100644 --- a/jrnl/plugins/text_exporter.py +++ b/jrnl/plugins/text_exporter.py @@ -34,7 +34,7 @@ class TextExporter: @classmethod def make_filename(cls, entry): - return entry.date.strftime("%Y-%m-%d_{0}.{1}".format(slugify(str(entry.title)), cls.extension)) + return entry.date.strftime("%Y-%m-%d_{}.{}".format(slugify(str(entry.title)), cls.extension)) @classmethod def write_files(cls, journal, path): @@ -46,7 +46,7 @@ class TextExporter: f.write(cls.export_entry(entry)) except IOError as e: return "[{2}ERROR{3}: {0} {1}]".format(e.filename, e.strerror, ERROR_COLOR, RESET_COLOR) - return "[Journal exported to {0}]".format(path) + return "[Journal exported to {}]".format(path) @classmethod def export(cls, journal, output=None): diff --git a/jrnl/plugins/util.py b/jrnl/plugins/util.py index 0a642cb2..a056b19a 100644 --- a/jrnl/plugins/util.py +++ b/jrnl/plugins/util.py @@ -10,7 +10,7 @@ def get_tags_count(journal): for entry in journal.entries for tag in set(entry.tags)] # To be read: [for entry in journal.entries: for tag in set(entry.tags): tag] - tag_counts = set([(tags.count(tag), tag) for tag in tags]) + tag_counts = {(tags.count(tag), tag) for tag in tags} return tag_counts From d47e1ed4799139ccc44c8dc6d1ffd95964ee9fab Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Fri, 1 Nov 2019 19:58:32 +0100 Subject: [PATCH 08/93] fixup! remove py2 remnants and use mocks in tests --- jrnl/Journal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/Journal.py b/jrnl/Journal.py index e5f87e03..9ecc2515 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -224,7 +224,7 @@ class Journal: raw = raw.replace('\\n ', '\n').replace('\\n', '\n') starred = False # Split raw text into title and body - sep = re.search("\n|[\\?!.]+ +\n?", raw) + sep = re.search(r"\n|[?!.]+ +\n?", raw) first_line = raw[:sep.end()].strip() if sep else raw starred = False From 65adb92ed4b316081ecfb9d348e0aecb3813f2c8 Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Fri, 1 Nov 2019 20:05:06 +0100 Subject: [PATCH 09/93] small print bugfix The file=sys.stderr was part of the format(), so an error got printed to stdout --- jrnl/plugins/yaml_exporter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jrnl/plugins/yaml_exporter.py b/jrnl/plugins/yaml_exporter.py index eec9b0b3..4a75667f 100644 --- a/jrnl/plugins/yaml_exporter.py +++ b/jrnl/plugins/yaml_exporter.py @@ -17,7 +17,8 @@ class YAMLExporter(TextExporter): def export_entry(cls, entry, to_multifile=True): """Returns a markdown representation of a single entry, with YAML front matter.""" if to_multifile is False: - print("{}ERROR{}: YAML export must be to individual files. Please specify a directory to export to.".format("\033[31m", "\033[0m", file=sys.stderr)) + print("{}ERROR{}: YAML export must be to individual files. " + "Please specify a directory to export to.".format("\033[31m", "\033[0m"), file=sys.stderr) return date_str = entry.date.strftime(entry.journal.config['timeformat']) From 827a598dd870854820fac0f81ed92bda98e07cce Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Fri, 1 Nov 2019 21:48:04 +0100 Subject: [PATCH 10/93] Drop use of codecs package Use builtins.open() instead --- jrnl/Journal.py | 9 ++++----- jrnl/export.py | 5 ++--- jrnl/plugins/jrnl_importer.py | 3 +-- jrnl/plugins/text_exporter.py | 5 ++--- jrnl/upgrade.py | 3 +-- jrnl/util.py | 5 ++--- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 9ecc2515..f130823d 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -5,7 +5,6 @@ from . import util from . import time import os import sys -import codecs import re from datetime import datetime import logging @@ -268,15 +267,15 @@ class Journal: class PlainJournal(Journal): @classmethod def _create(cls, filename): - with codecs.open(filename, "a", "utf-8"): + with open(filename, "a", encoding="utf-8"): pass def _load(self, filename): - with codecs.open(filename, "r", "utf-8") as f: + with open(filename, "r", encoding="utf-8") as f: return f.read() def _store(self, filename, text): - with codecs.open(filename, 'w', "utf-8") as f: + with open(filename, 'w', encoding="utf-8") as f: f.write(text) @@ -285,7 +284,7 @@ class LegacyJournal(Journal): standard. Main difference here is that in 1.x, timestamps were not cuddled by square brackets. You'll not be able to save these journals anymore.""" def _load(self, filename): - with codecs.open(filename, "r", "utf-8") as f: + with open(filename, "r", encoding="utf-8") as f: return f.read() def _parse(self, journal_txt): diff --git a/jrnl/export.py b/jrnl/export.py index 399f2db0..1ee4e6ff 100644 --- a/jrnl/export.py +++ b/jrnl/export.py @@ -4,7 +4,6 @@ from .util import ERROR_COLOR, RESET_COLOR from .util import slugify from .plugins.template import Template import os -import codecs class Exporter: @@ -32,7 +31,7 @@ class Exporter: def write_file(self, journal, path): """Exports a journal into a single file.""" try: - with codecs.open(path, "w", "utf-8") as f: + with open(path, "w", encoding="utf-8") as f: f.write(self.export_journal(journal)) return f"[Journal exported to {path}]" except OSError as e: @@ -46,7 +45,7 @@ class Exporter: for entry in journal.entries: try: full_path = os.path.join(path, self.make_filename(entry)) - with codecs.open(full_path, "w", "utf-8") as f: + with open(full_path, "w", encoding="utf-8") as f: f.write(self.export_entry(entry)) except OSError as e: return f"[{ERROR_COLOR}ERROR{RESET_COLOR}: {e.filename} {e.strerror}]" diff --git a/jrnl/plugins/jrnl_importer.py b/jrnl/plugins/jrnl_importer.py index e2145232..83341cd9 100644 --- a/jrnl/plugins/jrnl_importer.py +++ b/jrnl/plugins/jrnl_importer.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -import codecs import sys from .. import util @@ -16,7 +15,7 @@ class JRNLImporter: old_cnt = len(journal.entries) old_entries = journal.entries if input: - with codecs.open(input, "r", "utf-8") as f: + with open(input, "r", encoding="utf-8") as f: other_journal_txt = f.read() else: try: diff --git a/jrnl/plugins/text_exporter.py b/jrnl/plugins/text_exporter.py index 687de750..ce2e71de 100644 --- a/jrnl/plugins/text_exporter.py +++ b/jrnl/plugins/text_exporter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -import codecs from ..util import slugify import os from ..util import ERROR_COLOR, RESET_COLOR @@ -26,7 +25,7 @@ class TextExporter: def write_file(cls, journal, path): """Exports a journal into a single file.""" try: - with codecs.open(path, "w", "utf-8") as f: + with open(path, "w", encoding="utf-8") as f: f.write(cls.export_journal(journal)) return f"[Journal exported to {path}]" except IOError as e: @@ -42,7 +41,7 @@ class TextExporter: for entry in journal.entries: try: full_path = os.path.join(path, cls.make_filename(entry)) - with codecs.open(full_path, "w", "utf-8") as f: + with open(full_path, "w", encoding="utf-8") as f: f.write(cls.export_entry(entry)) except IOError as e: return "[{2}ERROR{3}: {0} {1}]".format(e.filename, e.strerror, ERROR_COLOR, RESET_COLOR) diff --git a/jrnl/upgrade.py b/jrnl/upgrade.py index d1b83809..216987a0 100644 --- a/jrnl/upgrade.py +++ b/jrnl/upgrade.py @@ -6,7 +6,6 @@ from . import util from .EncryptedJournal import EncryptedJournal from .util import UserAbort import os -import codecs def backup(filename, binary=False): @@ -19,7 +18,7 @@ def backup(filename, binary=False): def upgrade_jrnl_if_necessary(config_path): - with codecs.open(config_path, "r", "utf-8") as f: + with open(config_path, "r", encoding="utf-8") as f: config_file = f.read() if not config_file.strip().startswith("{"): return diff --git a/jrnl/util.py b/jrnl/util.py index d2bdf9c4..1290adc8 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -10,7 +10,6 @@ if "win32" in sys.platform: import re import tempfile import subprocess -import codecs import unicodedata import shlex import logging @@ -106,14 +105,14 @@ def scope_config(config, journal_name): def get_text_from_editor(config, template=""): filehandle, tmpfile = tempfile.mkstemp(prefix="jrnl", text=True, suffix=".txt") - with codecs.open(tmpfile, 'w', "utf-8") as f: + with open(tmpfile, 'w', encoding="utf-8") as f: if template: f.write(template) try: subprocess.call(shlex.split(config['editor'], posix="win" not in sys.platform) + [tmpfile]) except AttributeError: subprocess.call(config['editor'] + [tmpfile]) - with codecs.open(tmpfile, "r", "utf-8") as f: + with open(tmpfile, "r", encoding="utf-8") as f: raw = f.read() os.close(filehandle) os.remove(tmpfile) From 7d9795ace6e105092a482db9f34d050f7e55337e Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Mon, 4 Nov 2019 16:36:16 +0100 Subject: [PATCH 11/93] fixup! remove py2 remnants and use mocks in tests --- jrnl/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/util.py b/jrnl/util.py index 1290adc8..959d63a1 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -77,7 +77,7 @@ def set_keychain(journal_name, password): def yesno(prompt, default=True): - prompt = f"{prompt.strip()} {'[Y/n]' if default else '[y/N]'}" + prompt = f"{prompt.strip()} {'[Y/n]' if default else '[y/N]'} " response = input(prompt) return {"y": True, "n": False}.get(response.lower(), default) From ffc2e6245e69ba0d0b4aa1b375fdc35ac0f0c2fb Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Mon, 4 Nov 2019 15:30:47 -0800 Subject: [PATCH 12/93] Updating Issue Template Workflow --- .github/ISSUE_TEMPLATE.md | 35 ------------------- .../{Bug_report.md => bug_report.md} | 18 ++++++---- .github/ISSUE_TEMPLATE/config.yml | 1 + ...{Feature_request.md => feature_request.md} | 12 ++++++- ...{Support_request.md => support_request.md} | 14 ++++++-- 5 files changed, 35 insertions(+), 45 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md rename .github/ISSUE_TEMPLATE/{Bug_report.md => bug_report.md} (76%) create mode 100644 .github/ISSUE_TEMPLATE/config.yml rename .github/ISSUE_TEMPLATE/{Feature_request.md => feature_request.md} (76%) rename .github/ISSUE_TEMPLATE/{Support_request.md => support_request.md} (84%) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 8ca44464..00000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,35 +0,0 @@ -Hello! Thank you for reporting an issue! -If you would fill out the below points, that would make our process a whole lot easier! - -## **I'm submitting a ...** - - [ ] bug report - - [ ] feature request - - [ ] support request - - - - -## Regarding bug reports: - -* **Please tell us about your environment:** - - Jrnl version: (run `jrnl -v`) - - How you installed Jrnl - - - Operating system [MacOS, Linux, Windows?] - -* **What is the current behavior?** - -* **Please provide the steps to reproduce and if possible a minimal demo of the problem** - - -* **What is the expected behavior?** - -* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc) - -## Regarding Feature requests: - -* **What is the motivation / use case for changing the behavior?** - -* **Please provide examples of the usage** - -* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md similarity index 76% rename from .github/ISSUE_TEMPLATE/Bug_report.md rename to .github/ISSUE_TEMPLATE/bug_report.md index 38cf527e..0019514d 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,20 +1,26 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + ## Bug report + Hello! Thank you for reporting an issue! If you would fill out the below points, that would make our process a whole lot easier! - -* **Please tell us about your environment:** +* **Please tell us about your environment:** - Jrnl version: (run `jrnl -v`) - How you installed Jrnl - - Operating system [MacOS, Linux, Windows?] * **What is the current behavior?** -* **Please provide the steps to reproduce and if possible a minimal demo of the problem** - +* **Please provide the steps to reproduce and if possible a minimal demo of the problem** * **What is the expected behavior?** * **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc) - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..3ba13e0c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md similarity index 76% rename from .github/ISSUE_TEMPLATE/Feature_request.md rename to .github/ISSUE_TEMPLATE/feature_request.md index f9357f42..a2970a2f 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,4 +1,14 @@ -#Feature Request +--- +name: Feature request +about: Suggest an idea for jrnl +title: '' +labels: enhancement +assignees: '' + +--- + +## Feature Request + Hello! Thank you for reporting an issue! If you would fill out the below points, that would make our process a whole lot easier! diff --git a/.github/ISSUE_TEMPLATE/Support_request.md b/.github/ISSUE_TEMPLATE/support_request.md similarity index 84% rename from .github/ISSUE_TEMPLATE/Support_request.md rename to .github/ISSUE_TEMPLATE/support_request.md index 814d1f70..d3ce69b0 100644 --- a/.github/ISSUE_TEMPLATE/Support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -1,14 +1,22 @@ +--- +name: Support Request +about: Get help with jrnl +title: '' +labels: support +assignees: '' + +--- + ## Support request + Hello! Thank you for reporting an issue! If you would fill out the below points, that would make our process a whole lot easier! - - * **Please tell us about your environment:** - Jrnl version: (run `jrnl -v`) - How you installed Jrnl - + - Operating system [MacOS, Linux, Windows?] * **What are you trying to do?** From 971560edace4ab350b8f530f4d4a185240d71540 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 7 Nov 2019 18:20:57 -0700 Subject: [PATCH 13/93] [Upgrade to 2.0] Expand User directory (#704) --- jrnl/upgrade.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jrnl/upgrade.py b/jrnl/upgrade.py index f2b80af3..839270e2 100644 --- a/jrnl/upgrade.py +++ b/jrnl/upgrade.py @@ -54,6 +54,8 @@ older versions of jrnl anymore. encrypt = config.get('encrypt') path = journal_conf + path = os.path.expanduser(path) + if encrypt: encrypted_journals[journal_name] = path elif os.path.isdir(path): From 721656cb079c2e7b63d2d3bd25e8d56b01a185d4 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Sat, 9 Nov 2019 14:17:13 -0800 Subject: [PATCH 14/93] [#715] Make bot that will auto-increment version in code after deploy --- .travis.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fea48f50..1715093a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ dist: xenial # required for Python >= 3.7 language: python python: "3.7" +git: + depth: false before_install: - pip install poetry install: @@ -11,7 +13,6 @@ script: - poetry run python --version - poetry run behave before_deploy: - - pip install poetry - poetry config http-basic.pypi $PYPI_USER $PYPI_PASS - poetry version $TRAVIS_TAG - poetry build @@ -22,3 +23,10 @@ deploy: on: branch: master tags: true +after_deploy: + - git config --global user.email "jrnl.bot@gmail.com" + - git config --global user.name "Jrnl Bot" + - git checkout master + - git add pyproject.toml + - git commit -m "Incrementing version to ${TRAVIS_TAG}" + - git push https://${GITHUB_TOKEN}@github.com/jrnl-org/jrnl.git master From bb15e68fc79c83837006e212a2de0efaf09a947e Mon Sep 17 00:00:00 2001 From: Jrnl Bot Date: Sat, 9 Nov 2019 22:26:33 +0000 Subject: [PATCH 15/93] Incrementing version to v2.1-beta6 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1b84acde..90cfb85a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "jrnl" -version = "0.0.0-source" +version = "v2.1-beta6" description = "Collect your thoughts and notes without leaving the command line." authors = [ "Manuel Ebert ", From 1bf01aa7486d8204b30a14e10232751d5e967d49 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Mon, 11 Nov 2019 13:03:05 -0700 Subject: [PATCH 16/93] fix typos, spelling (#734) --- CHANGELOG.md | 2 +- CONTRIBUTING.md | 4 ++-- LICENSE | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c400b8c..81c93758 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ Changelog ## 2.0 -* Cryptographical backend changed from PyCrypto to cryptography.io +* Cryptographic backend changed from PyCrypto to cryptography.io * Config now respects XDG conventions and may move accordingly * Config now saved as YAML * Config name changed from `journals.jrnl_name.journal` to `journals.jrnl_name.path` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 989ccf7e..0de5982e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,13 +18,13 @@ Unfortunately, bugs happen. If you found one, please [open a new issue](https:// Feature requests and ideas -------------------------- -So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues) and describe the goal of the feature, and any relvant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. +So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. When discussing new features, please keep in mind our design goals. jrnl strives to do one thing well. To us, that means: * be _slim_ * have a simple interface -* avoid dupicating functionality +* avoid duplicating functionality A short note for new programmers and programmers new to python diff --git a/LICENSE b/LICENSE index dc4571c0..4419cd43 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Manuel Ebert +Copyright (c) 2014-2019 Manuel Ebert Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From cdb04974e2f9b2921260ecc0b2805c51335f0762 Mon Sep 17 00:00:00 2001 From: Jrnl Bot Date: Tue, 12 Nov 2019 02:43:42 +0000 Subject: [PATCH 17/93] Incrementing version to v2.1.post2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 90cfb85a..7a284da8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "jrnl" -version = "v2.1-beta6" +version = "v2.1.post2" description = "Collect your thoughts and notes without leaving the command line." authors = [ "Manuel Ebert ", From ad55402a11566357e72ffe2256428cd849b54a2f Mon Sep 17 00:00:00 2001 From: Greg Bodnar Date: Wed, 24 Jul 2019 19:56:03 +1200 Subject: [PATCH 18/93] Add recipe to display random entry --- docs/recipes.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/recipes.md b/docs/recipes.md index 7af5b246..45ef18b4 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -94,6 +94,15 @@ log_question 'What did I achieve today?' log_question 'What did I make progress with?' ``` +### Display random entry + +You can use this to select one title at random and then display the whole +entry. + +``` sh +jrnl -on "$(jrnl --short | shuf -n 1 | cut -d' ' -f1,2)" +``` + ## External editors To use external editors for writing and editing journal entries, set From 3ef086c71a64cf296aeeed817defe580b5c0b234 Mon Sep 17 00:00:00 2001 From: Greg Bodnar Date: Sun, 4 Aug 2019 15:23:33 +1200 Subject: [PATCH 19/93] Add doc about extracting timestamps with no spaces Timestamp formats may have no space between date and time components and cause the script to fail. --- docs/recipes.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/recipes.md b/docs/recipes.md index 45ef18b4..570b3a8c 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -97,7 +97,10 @@ log_question 'What did I make progress with?' ### Display random entry You can use this to select one title at random and then display the whole -entry. +entry. The invocation of `cut` needs to match the format of the timestamp. +For timestamps that have a space between data and time components, select +fields 1 and 2 as shown. For timestamps that have no whitespace, select +only field 1. ``` sh jrnl -on "$(jrnl --short | shuf -n 1 | cut -d' ' -f1,2)" From a7d5618025be83f92e20eed07a9a87fd4503ec5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rory=20O=E2=80=99Kane?= Date: Mon, 19 Aug 2019 03:50:41 -0400 Subject: [PATCH 20/93] In Encryption docs, explain how to prevent Fish history logging --- docs/encryption.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/encryption.md b/docs/encryption.md index 19bb178b..63859d91 100644 --- a/docs/encryption.md +++ b/docs/encryption.md @@ -56,6 +56,12 @@ setopt HIST_IGNORE_SPACE alias jrnl=" jrnl" ``` +The fish shell does not support automatically preventing logging like +this. To prevent `jrnl` commands being logged by fish, you must make +sure to type a space before every `jrnl` command you enter. To delete +existing `jrnl` commands from fish’s history, run +`history delete --prefix 'jrnl '`. + ## Manual decryption Should you ever want to decrypt your journal manually, you can do so From cc44cb1e175f5ea1870cb8340255b94021aaa4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rory=20O=E2=80=99Kane?= Date: Mon, 19 Aug 2019 04:01:33 -0400 Subject: [PATCH 21/93] In Encryption docs, improve grammar and typography --- docs/encryption.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/encryption.md b/docs/encryption.md index 63859d91..2cb5d547 100644 --- a/docs/encryption.md +++ b/docs/encryption.md @@ -2,9 +2,9 @@ ## Encrypting and decrypting -If you don't choose to encrypt your file when you run +If you don’t choose to encrypt your file when you run `jrnl` for the first time, you can encrypt -your existing journal file or change its password using +your existing journal file or change its password using this: ``` sh jrnl --encrypt @@ -18,38 +18,38 @@ replaced by the encrypted file. Conversely, jrnl --decrypt ``` -will replace your encrypted journal file by a Journal in plain text. You -can also specify a filename, ie. `jrnl --decrypt plain_text_copy.txt`, +will replace your encrypted journal file with a journal in plain text. You +can also specify a filename, i.e. `jrnl --decrypt plain_text_copy.txt`, to leave your original file untouched. ## Storing passwords in your keychain Whenever you encrypt your journal, you are asked whether you want to store the encryption password in your keychain. If you do this, you -won't have to enter your password every time you want to write or read +won’t have to enter your password every time you want to write or read your journal. -If you don't initially store the password in the keychain but decide to -do so at a later point -- or maybe want to store it on one computer but -not on another -- you can simply run `jrnl --encrypt` on an encrypted +If you don’t initially store the password in the keychain but decide to +do so at a later point – or maybe want to store it on one computer but +not on another – you can simply run `jrnl --encrypt` on an encrypted journal and use the same password again. ## A note on security While jrnl follows best practises, true security is an illusion. Specifically, jrnl will leave traces in your memory and your shell -history -- it's meant to keep journals secure in transit, for example +history – it’s meant to keep journals secure in transit, for example when storing it on an [untrusted](http://techcrunch.com/2014/04/09/condoleezza-rice-joins-dropboxs-board/) -services such as Dropbox. If you're concerned about security, disable -history logging for journal in your `.bashrc` +services such as Dropbox. If you’re concerned about security, disable +history logging for journal in your `.bashrc`: ``` sh HISTIGNORE="$HISTIGNORE:jrnl *" ``` -If you are using zsh instead of bash, you can get the same behaviour -adding this to your `zshrc` +If you are using zsh instead of bash, you can get the same behaviour by +adding this to your `zshrc`: ``` sh setopt HIST_IGNORE_SPACE @@ -69,8 +69,8 @@ with any program that supports the AES algorithm in CBC. The key used for encryption is the SHA-256-hash of your password, the IV (initialisation vector) is stored in the first 16 bytes of the encrypted file. The plain text is encoded in UTF-8 and padded according to PKCS\#7 -before being encrypted. Here's a Python script that you can use to -decrypt your journal +before being encrypted. Here’s a Python script that you can use to +decrypt your journal: ``` python #!/usr/bin/env python3 From e10fb7f9335d5ba09676d61a8d2ac1e8d4fe7a56 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Sat, 5 Oct 2019 15:50:04 -0700 Subject: [PATCH 22/93] [GH-666] updating documentation to reflect v2 behavior: * new config location and file type * removing mentions of DayOne support * removing mention of pip install jrnl[encrypted] --- docs/advanced.md | 80 ++++++++++---------------------- docs/installation.md | 17 +------ docs/overview.md | 4 -- docs/recipes.md | 72 +++++++++++++---------------- docs/theme/index.html | 5 -- docs/usage.md | 104 +++++++++++++++++------------------------- 6 files changed, 98 insertions(+), 184 deletions(-) diff --git a/docs/advanced.md b/docs/advanced.md index 2a6fd6b4..0375da10 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -3,15 +3,19 @@ ## Configuration File You can configure the way jrnl behaves in a configuration file. By -default, this is `~/.jrnl_config`. If you have the `XDG_CONFIG_HOME` +default, this is `~/.config/jrnl/jrnl.yaml`. If you have the `XDG_CONFIG_HOME` variable set, the configuration file will be saved as -`$XDG_CONFIG_HOME/jrnl/.jrnl_config`. +`$XDG_CONFIG_HOME/jrnl/jrnl.yaml`. !!! note - On Windows, The configuration file is typically found at `C:\Users\[Your Username]\.jrnl_config`. + On Windows, the configuration file is typically found at `%USERPROFILE%\.config\jrnl\jrnl.yaml`. -The configuration file is a simple JSON file with the following options -and can be edited with any plain text editor. +The configuration file is a YAML file with the following options +and can be edited with a plain text editor. + +!!! note + Backup your config file before editing. Changes to the config file + have destructive effects on your journal! - `journals` paths to your journal files @@ -51,46 +55,16 @@ and can be edited with any plain text editor. Or use the built-in prompt or an external editor to compose your entries. -## DayOne Integration - -Using your DayOne journal instead of a flat text file is dead simple -- -instead of pointing to a text file, change your `.jrnl_config` to point -to your DayOne journal. This is a folder named something like -`Journal_dayone` or `Journal.dayone`, and it's located at - - - `~/Library/Application Support/Day One/` by default - - `~/Dropbox/Apps/Day One/` if you're syncing with Dropbox and - - `~/Library/Mobile - Documents/5U8NS4GX82~com~dayoneapp~dayone/Documents/` if you're - syncing with iCloud. - -Instead of all entries being in a single file, each entry will live in a -separate `plist` file. So your `.jrnl_config` should look like this: - -``` javascript -{ - ... - "journals": { - "default": "~/journal.txt", - "dayone": "~/Library/Mobile Documents/5U8NS4GX82~com~dayoneapp~dayone/Documents/Journal_dayone" - } -} -``` - ## Multiple journal files You can configure `jrnl`to use with multiple journals (eg. -`private` and `work`) by defining more journals in your `.jrnl_config`, +`private` and `work`) by defining more journals in your `jrnl.yaml`, for example: -``` javascript -{ -... - "journals": { - "default": "~/journal.txt", - "work": "~/work.txt" - } -} +``` yaml +journals: + default: ~\journal.txt + work: ~\work.txt ``` The `default` journal gets created the first time you start `jrnl` @@ -106,26 +80,22 @@ will both use `~/work.txt`, while `jrnl -n 3` will display the last three entries from `~/journal.txt` (and so does `jrnl default -n 3`). You can also override the default options for each individual journal. -If you `.jrnl_config` looks like this: +If your `jrnl.yaml` looks like this: -``` javascript -{ - ... - "encrypt": false - "journals": { - "default": "~/journal.txt", - "work": { - "journal": "~/work.txt", - "encrypt": true - }, - "food": "~/my_recipes.txt", -} +``` yaml +encrypt: false +journals: +default: ~/journal.txt +work: + journal: ~/work.txt + encrypt: true +food: ~/my_recipes.txt ``` Your `default` and your `food` journals won't be encrypted, however your `work` journal will! You can override all options that are present at -the top level of `.jrnl_config`, just make sure that at the very least -you specify a `"journal": ...` key that points to the journal file of +the top level of `jrnl.yaml`, just make sure that at the very least +you specify a `journal: ...` key that points to the journal file of that journal. !!! note diff --git a/docs/installation.md b/docs/installation.md index 36b694ce..8bf29bb0 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -15,27 +15,12 @@ On other platforms, install *jrnl* using pip pip install jrnl ``` -Or, if you want the option to encrypt your journal, - -``` sh -pip install jrnl[encrypted] -``` - -to install the dependencies for encrypting journals as well. - - -!!! note - Installing the encryption library, `pycrypto`, requires a `gcc` compiler. For this reason, jrnl will - not install `pycrypto` unless explicitly told so like this. You can [install PyCrypto manually](https://www.dlitz.net/software/pycrypto/) - first or install it with `pip install pycrypto` if you have a `gcc` compiler. - Also note that when using zsh, the correct syntax is `pip install "jrnl[encrypted]"` (note the quotes). - The first time you run `jrnl` you will be asked where your journal file should be created and whether you wish to encrypt it. ## Quickstart -to make a new entry, just type +To make a new entry, just type ``` sh jrnl yesterday: Called in sick. Used the time to clean the house and spent 4h on writing my book. diff --git a/docs/overview.md b/docs/overview.md index 8a0ec10f..86211814 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -8,10 +8,6 @@ files - you can put them into a Dropbox folder for instant syncing and you can be assured that your journal will still be readable in 2050, when all your fancy iPad journal applications will long be forgotten. -`jrnl` also plays nice with the fabulous -[DayOne](http://dayoneapp.com) and can read and write directly from and -to DayOne Journals. - Optionally, your journal can be encrypted using the [256-bit AES](http://en.wikipedia.org/wiki/Advanced_Encryption_Standard). diff --git a/docs/recipes.md b/docs/recipes.md index 570b3a8c..04604e6d 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -7,7 +7,7 @@ If I want to find out how often I mentioned my flatmates Alberto and Melo in the same entry, I run -``` sh +```sh jrnl @alberto --tags | grep @melo ``` @@ -22,7 +22,7 @@ each tag occurred in this filtered journal. Finally, we pipe this to You can do things like -``` sh +```sh jrnl @fixed -starred -n 10 -until "jan 2013" --short ``` @@ -33,14 +33,14 @@ January 1, 2013 that are tagged with `@fixed`. How much did I write last year? -``` sh +```sh jrnl -from "jan 1 2013" -until "dec 31 2013" | wc -w ``` Will give you the number of words you wrote in 2013. How long is my average entry? -``` sh +```sh expr $(jrnl --export text | wc -w) / $(jrnl --short | wc -l) ``` @@ -50,11 +50,10 @@ print exactly one line per entry). ### Importing older files -If you want to import a file as an entry to jrnl, you can just do `jrnl -< entry.ext`. But what if you want the modification date of the file to +If you want to import a file as an entry to jrnl, you can just do `jrnl < entry.ext`. But what if you want the modification date of the file to be the date of the entry in jrnl? Try this -``` sh +```sh echo `stat -f %Sm -t '%d %b %Y at %H:%M: ' entry.txt` `cat entry.txt` | jrnl ``` @@ -63,7 +62,7 @@ then combine it with the contents of the file before piping it to jrnl. If you do that often, consider creating a function in your `.bashrc` or `.bash_profile` -``` sh +```sh jrnlimport () { echo `stat -f %Sm -t '%d %b %Y at %H:%M: ' $1` `cat $1` | jrnl } @@ -83,7 +82,7 @@ Another nice solution that allows you to define individual prompts comes from [Jacobo de Vera](https://github.com/maebert/jrnl/issues/194#issuecomment-47402869): -``` sh +```sh function log_question() { echo $1 @@ -102,26 +101,24 @@ For timestamps that have a space between data and time components, select fields 1 and 2 as shown. For timestamps that have no whitespace, select only field 1. -``` sh +```sh jrnl -on "$(jrnl --short | shuf -n 1 | cut -d' ' -f1,2)" ``` ## External editors To use external editors for writing and editing journal entries, set -them up in your `.jrnl_config` (see `advanced usage ` for +them up in your `jrnl.yaml` (see `advanced usage ` for details). Generally, after writing an entry, you will have to save and close the file to save the changes to jrnl. ### Sublime Text To use Sublime Text, install the command line tools for Sublime Text and -configure your `.jrnl_config` like this: +configure your `jrnl.yaml` like this: -``` json -{ - "editor": "subl -w" -} +```yaml +editor: "subl -w" ``` Note the `-w` flag to make sure jrnl waits for Sublime Text to close the @@ -133,22 +130,20 @@ Similar to Sublime Text, MacVim must be started with a flag that tells the the process to wait until the file is closed before passing control back to journal. In the case of MacVim, this is `-f`: -``` json -{ - "editor": "mvim -f" -} +<<<<<<< HEAD + +```yaml +editor: "mvim -f" ``` ### iA Writer On OS X, you can use the fabulous [iA Writer](http://www.iawriter.com/mac) to write entries. Configure your -`.jrnl_config` like this: +`jrnl.yaml` like this: -``` json -{ - "editor": "open -b pro.writer.mac -Wn" -} +```yaml +editor: "open -b pro.writer.mac -Wn" ``` What does this do? `open -b ...` opens a file using the application @@ -160,19 +155,17 @@ If the `pro.writer.mac` bundle identifier is not found on your system, you can find the right string to use by inspecting iA Writer's `Info.plist` file in your shell: -``` sh +```sh grep -A 1 CFBundleIdentifier /Applications/iA\ Writer.app/Contents/Info.plist ``` ### Notepad++ on Windows To set [Notepad++](http://notepad-plus-plus.org/) as your editor, edit -the jrnl config file (`.jrnl_config`) like this: +the jrnl config file (`jrnl.yaml`) like this: -``` json -{ - "editor": "C:\\Program Files (x86)\\Notepad++\\notepad++.exe -multiInst -nosession", -} +```yaml +editor: "C:\\Program Files (x86)\\Notepad++\\notepad++.exe -multiInst -nosession" ``` The double backslashes are needed so jrnl can read the file path @@ -181,12 +174,10 @@ its own Notepad++ window. ### Visual Studio Code -To set [Visual Studo Code](https://code.visualstudio.com) as your editor on Linux, edit `.jrnl_config` like this: +To set [Visual Studo Code](https://code.visualstudio.com) as your editor on Linux, edit `jrnl.yaml` like this: -```json -{ - "editor": "/usr/bin/code --wait", -} +```yaml +editor: "/usr/bin/code --wait" ``` The `--wait` argument tells VS Code to wait for files to be written out before handing back control to jrnl. @@ -196,14 +187,13 @@ On MacOS you will need to add VS Code to your PATH. You can do that by adding: ```sh export PATH="\$PATH:/Applications/Visual Studio Code.app/Contents/Resources/app/bin" ``` + to your `.bash_profile`, or by running the **Install 'code' command in PATH** command from the command pallet in VS Code. Then you can add: -```javascript -{ - "editor": "code --wait", -} +```yaml +editor: "code --wait" ``` -to ``.jrnl_config``. See also the [Visual Studio Code documentation](https://code.visualstudio.com/docs/setup/mac) +to `jrnl.yaml`. See also the [Visual Studio Code documentation](https://code.visualstudio.com/docs/setup/mac) diff --git a/docs/theme/index.html b/docs/theme/index.html index 0c179210..a0f620fb 100755 --- a/docs/theme/index.html +++ b/docs/theme/index.html @@ -82,11 +82,6 @@

Accessible anywhere.

Sync your journals with Dropbox and capture your thoughts where ever you are

-
- -

DayOne compatible.

-

Read, write and search your DayOne journal from the command line.

-

Free & Open Source.

diff --git a/docs/usage.md b/docs/usage.md index fa5050a0..269bdce9 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,15 +1,15 @@ # Basic Usage `jrnl` has two modes: **composing** and **viewing**. Basically, whenever -you *don't* supply any arguments that start +you _don't_ supply any arguments that start with a dash or double-dash, you're in composing mode, meaning you can write your entry on the command line or an editor of your choice. We intentionally break a convention on command line arguments: all -arguments starting with a *single dash* -will *filter* your journal before viewing +arguments starting with a _single dash_ +will _filter_ your journal before viewing it, and can be combined arbitrarily. Arguments with a -*double dash* will control how your journal +_double dash_ will control how your journal is displayed or exported and are mutually exclusive (ie. you can only specify one way to display or export your journal at a time). @@ -17,7 +17,7 @@ specify one way to display or export your journal at a time). You can list the journals accessible by jrnl -``` sh +```sh jrnl -ls ``` @@ -30,21 +30,21 @@ Composing mode is entered by either starting `jrnl` without any arguments -- which will prompt you to write an entry or launch your editor -- or by just writing an entry on the prompt, such as -``` sh +```sh jrnl today at 3am: I just met Steve Buscemi in a bar! He looked funny. ``` !!! note - Most shell contains a certain number of reserved characters, such as `#` - and `*`. Unbalanced quotes, parenthesis, and so on will also get into - the way of your editing. - For writing longer entries, just enter `jrnl` - and hit `return`. Only then enter the text of your journal entry. - Alternatively, `use an external editor `). +Most shell contains a certain number of reserved characters, such as `#` +and `*`. Unbalanced quotes, parenthesis, and so on will also get into +the way of your editing. +For writing longer entries, just enter `jrnl` +and hit `return`. Only then enter the text of your journal entry. +Alternatively, `use an external editor `). You can also import an entry directly from a file -``` sh +```sh jrnl < my_entry.txt ``` @@ -52,51 +52,51 @@ jrnl < my_entry.txt Timestamps that work: - - at 6am - - yesterday - - last monday - - sunday at noon - - 2 march 2012 - - 7 apr - - 5/20/1998 at 23:42 +- at 6am +- yesterday +- last monday +- sunday at noon +- 2 march 2012 +- 7 apr +- 5/20/1998 at 23:42 ### Starring entries To mark an entry as a favourite, simply "star" it -``` sh +```sh jrnl last sunday *: Best day of my life. ``` If you don't want to add a date (ie. your entry will be dated as now), The following options are equivalent: - - `jrnl *: Best day of my life.` - - `jrnl *Best day of my life.` - - `jrnl Best day of my life.*` +- `jrnl *: Best day of my life.` +- `jrnl *Best day of my life.` +- `jrnl Best day of my life.*` !!! note - Just make sure that the asterisk sign is **not** surrounded by - whitespaces, e.g. `jrnl Best day of my life! *` will **not** work (the - reason being that the `*` sign has a special meaning on most shells). +Just make sure that the asterisk sign is **not** surrounded by +whitespaces, e.g. `jrnl Best day of my life! *` will **not** work (the +reason being that the `*` sign has a special meaning on most shells). ## Viewing -``` sh +```sh jrnl -n 10 ``` will list you the ten latest entries (if you're lazy, `jrnl -10` will do the same), -``` sh +```sh jrnl -from "last year" -until march ``` everything that happened from the start of last year to the start of last march. To only see your favourite entries, use -``` sh +```sh jrnl -starred ``` @@ -105,20 +105,20 @@ jrnl -starred Keep track of people, projects or locations, by tagging them with an `@` in your entries -``` sh +```sh jrnl Had a wonderful day on the @beach with @Tom and @Anna. ``` You can filter your journal entries just like this: -``` sh +```sh jrnl @pinkie @WorldDomination ``` Will print all entries in which either `@pinkie` or `@WorldDomination` occurred. -``` sh +```sh jrnl -n 5 -and @pineapple @lubricant ``` @@ -127,18 +127,18 @@ You can change which symbols you'd like to use for tagging in the configuration. !!! note - `jrnl @pinkie @WorldDomination` will switch to viewing mode because - although **no** command line arguments are given, all the input strings - look like tags - *jrnl* will assume you want to filter by tag. +`jrnl @pinkie @WorldDomination` will switch to viewing mode because +although **no** command line arguments are given, all the input strings +look like tags - _jrnl_ will assume you want to filter by tag. ## Editing older entries You can edit selected entries after you wrote them. This is particularly -useful when your journal file is encrypted or if you're using a DayOne -journal. To use this feature, you need to have an editor configured in -your journal configuration file (see `advanced usage `) +useful when your journal file is encrypted. To use this feature, you need +to have an editor configured in your journal configuration file (see +`advanced usage `) -``` sh +```sh jrnl -until 1950 @texas -and @history --edit ``` @@ -153,30 +153,8 @@ encrypt) your edited journal after you save and exit the editor. You can also use this feature for deleting entries from your journal -``` sh +```sh jrnl @girlfriend -until 'june 2012' --edit ``` Just select all text, press delete, and everything is gone... - -### Editing DayOne Journals - -DayOne journals can be edited exactly the same way, however the output -looks a little bit different because of the way DayOne stores its -entries: - -```md -# af8dbd0d43fb55458f11aad586ea2abf -2013-05-02 15:30 I told everyone I built my @robot wife for sex. -But late at night when we're alone we mostly play Battleship. - -# 2391048fe24111e1983ed49a20be6f9e -2013-08-10 03:22 I had all kinds of plans in case of a @zombie attack. -I just figured I'd be on the other side. -``` - -The long strings starting with hash symbol are the so-called UUIDs, -unique identifiers for each entry. Don't touch them. If you do, then the -old entry would get deleted and a new one written, which means that you -could lose DayOne data that jrnl can't handle (such as as the entry's -geolocation). From f30e11d46fac179933d5a4844584ec71ca9a2f1d Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Sat, 26 Oct 2019 23:00:06 +0200 Subject: [PATCH 23/93] Prepare travis for upcoming poetry 1.0 release The poetry version command will change in poetry 1.0 (see sdispater/poetry#1191). Without any argument, it won't bump the version anymore but instead just print the current version. This will break the current travis before_install. Let's pin poetry to ~0.12.17 for now and change it once poetry v1.0 releases. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1715093a..11e6586c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ python: "3.7" git: depth: false before_install: - - pip install poetry + - pip install poetry~=0.12.17 install: # we run `poetry version` here to appease poetry about '0.0.0-source' - poetry version From e682d2799415d581e4728ff0d992303382d8fdc0 Mon Sep 17 00:00:00 2001 From: Jims Date: Tue, 12 Nov 2019 11:53:12 -0500 Subject: [PATCH 24/93] added case-insensitive searching of entry title and body using a -S argument --- jrnl/Journal.py | 4 +++- jrnl/cli.py | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/jrnl/Journal.py b/jrnl/Journal.py index f6813222..ab52cd00 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -187,7 +187,7 @@ class Journal: tag_counts = {(tags.count(tag), tag) for tag in tags} return [Tag(tag, count=count) for count, tag in sorted(tag_counts)] - def filter(self, tags=[], start_date=None, end_date=None, starred=False, strict=False, short=False, exclude=[]): + def filter(self, tags=[], start_date=None, end_date=None, starred=False, strict=False, short=False, search_plain=None, exclude=[]): """Removes all entries from the journal that don't match the filter. tags is a list of tags, each being a string that starts with one of the @@ -209,6 +209,7 @@ class Journal: # If strict mode is on, all tags have to be present in entry tagged = self.search_tags.issubset if strict else self.search_tags.intersection excluded = lambda tags: len([tag for tag in tags if tag in excluded_tags]) > 0 + result = [ entry for entry in self.entries if (not tags or tagged(entry.tags)) @@ -216,6 +217,7 @@ class Journal: and (not start_date or entry.date >= start_date) and (not end_date or entry.date <= end_date) and (not exclude or not excluded(entry.tags)) + and (not search_plain or (search_plain.lower() in entry.title.lower() or search_plain.lower() in entry.body.lower())) ] self.entries = result diff --git a/jrnl/cli.py b/jrnl/cli.py index 3cf67030..ea8de891 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -32,6 +32,7 @@ def parse_args(args=None): reading = parser.add_argument_group('Reading', 'Specifying either of these parameters will display posts of your journal') reading.add_argument('-from', dest='start_date', metavar="DATE", help='View entries after this date') reading.add_argument('-until', '-to', dest='end_date', metavar="DATE", help='View entries before this date') + reading.add_argument('-S', '-search', dest='search_plain', help='View entries containing a specific string') reading.add_argument('-on', dest='on_date', metavar="DATE", help='View entries on this date') reading.add_argument('-and', dest='strict', action="store_true", help='Filter by tags using AND (default: OR)') reading.add_argument('-starred', dest='starred', action="store_true", help='Show only starred entries') @@ -64,7 +65,7 @@ def guess_mode(args, config): elif args.decrypt is not False or args.encrypt is not False or args.export is not False or any((args.short, args.tags, args.edit)): compose = False export = True - elif any((args.start_date, args.end_date, args.on_date, args.limit, args.strict, args.starred)): + elif any((args.start_date, args.end_date, args.on_date, args.limit, args.strict, args.starred, args.search_plain)): # Any sign of displaying stuff? compose = False elif args.text and all(word[0] in config['tagsymbols'] for word in " ".join(args.text).split()): @@ -236,7 +237,8 @@ def run(manual_args=None): strict=args.strict, short=args.short, starred=args.starred, - exclude=args.excluded) + exclude=args.excluded, + search_plain=args.search_plain) journal.limit(args.limit) # Reading mode From b5c05cd8467b927f1d245dfdd87ff80022fecc6c Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Tue, 12 Nov 2019 18:50:51 +0100 Subject: [PATCH 25/93] Clarify currently supported Python versions The Python 3.8 build currently fails on travis, this should be dealt with in a future PR along with updating the travis config and pyproject.toml --- .travis.yml | 4 +++- poetry.lock | 2 +- pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 11e6586c..e1f35e61 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ dist: xenial # required for Python >= 3.7 language: python -python: "3.7" +python: + - 3.6 + - 3.7 git: depth: false before_install: diff --git a/poetry.lock b/poetry.lock index e772b2d2..ea35de41 100644 --- a/poetry.lock +++ b/poetry.lock @@ -385,7 +385,7 @@ pytz = "*" [metadata] content-hash = "9896cf59c7552b6ad95219ee5555c7445a3fab39c2e4f4c6f3d991a36635e44b" -python-versions = "^3.7" +python-versions = ">=3.6.0, <3.8.0" [metadata.hashes] appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] diff --git a/pyproject.toml b/pyproject.toml index 7a284da8..70a44402 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ homepage = "https://jrnl.sh" repository = "https://github.com/jrnl-org/jrnl" [tool.poetry.dependencies] -python = "^3.7" +python = ">=3.6.0, <3.8.0" pyxdg = "^0.26.0" cryptography = "^2.7" passlib = "^1.7" From ea594e599884e463a7ae00c262f922915b04a230 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Wed, 13 Nov 2019 18:10:57 -0700 Subject: [PATCH 26/93] [Dayone] re-add tests c.f. commit 7cbca9f60f1fb5cc9b8d075f9b101d29d9656936 c.f. commit 2a401823b5ae37d5891548852cecfdec99a8e1c7 --- features/dayone.feature | 63 +++++++++++++++++++++++++++++ features/dayone_regressions.feature | 27 +++++++++++++ features/environment.py | 14 +++++++ 3 files changed, 104 insertions(+) create mode 100644 features/dayone.feature create mode 100644 features/dayone_regressions.feature diff --git a/features/dayone.feature b/features/dayone.feature new file mode 100644 index 00000000..b8a8a7cd --- /dev/null +++ b/features/dayone.feature @@ -0,0 +1,63 @@ +Feature: Dayone specific implementation details. + + Scenario: Loading a DayOne Journal + Given we use the config "dayone.yaml" + When we run "jrnl -from 'feb 2013'" + Then we should get no error + and the output should be + """ + 2013-05-17 11:39 This entry has tags! + + 2013-06-17 20:38 This entry has a location. + + 2013-07-17 11:38 This entry is starred! + """ + + Scenario: Entries without timezone information will be interpreted as in the current timezone + Given we use the config "dayone.yaml" + When we run "jrnl -until 'feb 2013'" + Then we should get no error + and the output should contain "2013-01-17T18:37Z" in the local time + + @skip + Scenario: Writing into Dayone + Given we use the config "dayone.yaml" + When we run "jrnl 01 may 1979: Being born hurts." + and we run "jrnl -until 1980" + Then the output should be + """ + 1979-05-01 09:00 Being born hurts. + """ + + Scenario: Loading tags from a DayOne Journal + Given we use the config "dayone.yaml" + When we run "jrnl --tags" + Then the output should be + """ + @work : 1 + @play : 1 + """ + + Scenario: Saving tags from a DayOne Journal + Given we use the config "dayone.yaml" + When we run "jrnl A hard day at @work" + and we run "jrnl --tags" + Then the output should be + """ + @work : 2 + @play : 1 + """ + + Scenario: Filtering by tags from a DayOne Journal + Given we use the config "dayone.yaml" + When we run "jrnl @work" + Then the output should be + """ + 2013-05-17 11:39 This entry has tags! + """ + Scenario: Exporting dayone to json + Given we use the config "dayone.yaml" + When we run "jrnl --export json" + Then we should get no error + and the output should be parsable as json + and the json output should contain entries.0.uuid = "4BB1F46946AD439996C9B59DE7C4DDC1" diff --git a/features/dayone_regressions.feature b/features/dayone_regressions.feature new file mode 100644 index 00000000..a707a5fb --- /dev/null +++ b/features/dayone_regressions.feature @@ -0,0 +1,27 @@ +Feature: Zapped Dayone bugs stay dead! + + Scenario: DayOne tag searching should work with tags containing a mixture of upper and lower case. + # https://github.com/jrnl-org/jrnl/issues/354 + Given we use the config "dayone.yaml" + When we run "jrnl @plAy" + Then the output should contain + """ + 2013-05-17 11:39 This entry has tags! + """ + + Scenario: Title with an embedded period on DayOne journal + Given we use the config "dayone.yaml" + When we run "jrnl 04-24-2014: "Ran 6.2 miles today in 1:02:03. I'm feeling sore because I forgot to stretch."" + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should be + """ + 2014-04-24 09:00 Ran 6.2 miles today in 1:02:03. + | I'm feeling sore because I forgot to stretch. + """ + + Scenario: Opening an folder that's not a DayOne folder gives a nice error message + Given we use the config "empty_folder.yaml" + When we run "jrnl Herro" + Then we should get an error + Then we should see the message "is a directory, but doesn't seem to be a DayOne journal either" diff --git a/features/environment.py b/features/environment.py index 7a918feb..8ba781ac 100644 --- a/features/environment.py +++ b/features/environment.py @@ -2,6 +2,14 @@ import shutil import os +def before_feature(context, feature): + # add "skip" tag + # https://stackoverflow.com/a/42721605/4276230 + if "skip" in feature.tags: + feature.skip("Marked with @skip") + return + + def before_scenario(context, scenario): """Before each scenario, backup all config and journal test data.""" # Clean up in case something went wrong @@ -22,6 +30,12 @@ def before_scenario(context, scenario): else: shutil.copy2(source, working_dir) + # add "skip" tag + # https://stackoverflow.com/a/42721605/4276230 + if "skip" in scenario.effective_tags: + scenario.skip("Marked with @skip") + return + def after_scenario(context, scenario): """After each scenario, restore all test data and remove working_dirs.""" From ad5d3a7856163dfea4a2d44a6ec91bfd716fc781 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Wed, 13 Nov 2019 18:26:13 -0700 Subject: [PATCH 27/93] [Dayone] Skip failing tests on Travis Travis sets the timezone to UTC, which causes many tests to fail --- features/dayone.feature | 13 +++++++++++++ features/dayone_regressions.feature | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/features/dayone.feature b/features/dayone.feature index b8a8a7cd..51aa2033 100644 --- a/features/dayone.feature +++ b/features/dayone.feature @@ -1,5 +1,7 @@ Feature: Dayone specific implementation details. + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Loading a DayOne Journal Given we use the config "dayone.yaml" When we run "jrnl -from 'feb 2013'" @@ -13,6 +15,8 @@ Feature: Dayone specific implementation details. 2013-07-17 11:38 This entry is starred! """ + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Entries without timezone information will be interpreted as in the current timezone Given we use the config "dayone.yaml" When we run "jrnl -until 'feb 2013'" @@ -29,6 +33,8 @@ Feature: Dayone specific implementation details. 1979-05-01 09:00 Being born hurts. """ + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Loading tags from a DayOne Journal Given we use the config "dayone.yaml" When we run "jrnl --tags" @@ -38,6 +44,8 @@ Feature: Dayone specific implementation details. @play : 1 """ + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Saving tags from a DayOne Journal Given we use the config "dayone.yaml" When we run "jrnl A hard day at @work" @@ -48,6 +56,8 @@ Feature: Dayone specific implementation details. @play : 1 """ + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Filtering by tags from a DayOne Journal Given we use the config "dayone.yaml" When we run "jrnl @work" @@ -55,6 +65,9 @@ Feature: Dayone specific implementation details. """ 2013-05-17 11:39 This entry has tags! """ + + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Exporting dayone to json Given we use the config "dayone.yaml" When we run "jrnl --export json" diff --git a/features/dayone_regressions.feature b/features/dayone_regressions.feature index a707a5fb..c3b700b9 100644 --- a/features/dayone_regressions.feature +++ b/features/dayone_regressions.feature @@ -1,5 +1,7 @@ Feature: Zapped Dayone bugs stay dead! + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: DayOne tag searching should work with tags containing a mixture of upper and lower case. # https://github.com/jrnl-org/jrnl/issues/354 Given we use the config "dayone.yaml" @@ -9,6 +11,8 @@ Feature: Zapped Dayone bugs stay dead! 2013-05-17 11:39 This entry has tags! """ + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Title with an embedded period on DayOne journal Given we use the config "dayone.yaml" When we run "jrnl 04-24-2014: "Ran 6.2 miles today in 1:02:03. I'm feeling sore because I forgot to stretch."" From 2890e33cac1acf9d86013b6d0627424d0ea88926 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Thu, 14 Nov 2019 12:00:23 +0100 Subject: [PATCH 28/93] Exit jrnl if no text entered into editor Fix #589 --- jrnl/cli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jrnl/cli.py b/jrnl/cli.py index 65a53516..192e3233 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -200,6 +200,8 @@ def run(manual_args=None): util.prompt("[Could not read template at '']".format(config['template'])) sys.exit(1) raw = util.get_text_from_editor(config, template) + if not raw: + sys.exit() else: try: raw = util.py23_read("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n") From fd5a08a4b27b83de3fa1c0067e9cf79a782d605d Mon Sep 17 00:00:00 2001 From: Jims Date: Thu, 14 Nov 2019 18:39:39 -0500 Subject: [PATCH 29/93] added search test --- features/searching.feature | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 features/searching.feature diff --git a/features/searching.feature b/features/searching.feature new file mode 100644 index 00000000..52e1ba54 --- /dev/null +++ b/features/searching.feature @@ -0,0 +1,11 @@ +Feature: Searching + + Scenario: Searching for a string + Given we use the config "basic.yaml" + When we run "jrnl -S life" + Then we should get no error + and the output should be + """ + 2013-06-10 15:40 Life is good. + | But I'm better. + """ From 7a90f4076d0b55c290884e468f46430a3bcba99a Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Fri, 15 Nov 2019 09:27:01 +0100 Subject: [PATCH 30/93] Add test for aborting jrnl entry from editor --- features/core.feature | 5 +++++ features/data/configs/editor.yaml | 12 ++++++++++++ features/steps/core.py | 13 +++++++++++++ poetry.lock | 14 +++++++++++++- pyproject.toml | 1 + 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 features/data/configs/editor.yaml diff --git a/features/core.feature b/features/core.feature index ab86da42..95ec9351 100644 --- a/features/core.feature +++ b/features/core.feature @@ -20,6 +20,11 @@ Feature: Basic reading and writing to a journal When we run "jrnl -n 1" Then the output should contain "2013-07-23 09:00 A cold and stormy day." + Scenario: Writing an empty entry from the editor + Given we use the config "editor.yaml" + When we open the editor and exit + Then the output should be empty + Scenario: Filtering for dates Given we use the config "basic.yaml" When we run "jrnl -on 2013-06-10 --short" diff --git a/features/data/configs/editor.yaml b/features/data/configs/editor.yaml new file mode 100644 index 00000000..8a06f916 --- /dev/null +++ b/features/data/configs/editor.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "vim" +encrypt: false +highlight: true +journals: + default: features/journals/simple.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/features/steps/core.py b/features/steps/core.py index 83981d13..fead2855 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -6,6 +6,7 @@ from jrnl import cli, install, Journal, util, plugins from jrnl import __version__ from dateutil import parser as date_parser from collections import defaultdict +import mock import os import json import yaml @@ -27,6 +28,7 @@ class TestKeyring(keyring.backend.KeyringBackend): def delete_password(self, servicename, username, password): self.keys[servicename][username] = None + # set the keyring for keyring lib keyring.set_keyring(TestKeyring()) @@ -73,6 +75,12 @@ def set_config(context, config_file): cf.write("version: {}".format(__version__)) +@when('we open the editor and exit') +@mock.patch('jrnl.util.get_text_from_editor', return_value="") +def open_editor_and_exit_without_entering_text(context, dead_param_to_satisfy_behave): + run(context, "jrnl") + + @when('we run "{command}" and enter') @when('we run "{command}" and enter "{inputs}"') def run_with_input(context, command, inputs=None): @@ -166,6 +174,11 @@ def check_json_output_path(context, path, value): assert struct == value, struct +@then('the output should be empty') +def check_empty_output(context, text=None): + assert (text or context.text) is None + + @then('the output should be') @then('the output should be "{text}"') def check_output(context, text=None): diff --git a/poetry.lock b/poetry.lock index e772b2d2..d4c35642 100644 --- a/poetry.lock +++ b/poetry.lock @@ -220,6 +220,17 @@ click = ">=3.3" livereload = ">=2.5.1" tornado = ">=5.0" +[[package]] +category = "main" +description = "Rolling backport of unittest.mock for all Pythons" +name = "mock" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.0.5" + +[package.dependencies] +six = "*" + [[package]] category = "main" description = "NumPy is the fundamental package for array computing with Python." @@ -384,7 +395,7 @@ version = "1.5.1" pytz = "*" [metadata] -content-hash = "9896cf59c7552b6ad95219ee5555c7445a3fab39c2e4f4c6f3d991a36635e44b" +content-hash = "ab4bffce011d607d66bdb9a3cadc5b7c67870ef4682c765a55af4d75ea3e0f73" python-versions = "^3.7" [metadata.hashes] @@ -409,6 +420,7 @@ markdown = ["2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a", markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] mkdocs = ["17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", "8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"] +mock = ["83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3", "d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"] numpy = ["0778076e764e146d3078b17c24c4d89e0ecd4ac5401beff8e1c87879043a0633", "141c7102f20abe6cf0d54c4ced8d565b86df4d3077ba2343b61a6db996cefec7", "14270a1ee8917d11e7753fb54fc7ffd1934f4d529235beec0b275e2ccf00333b", "27e11c7a8ec9d5838bc59f809bfa86efc8a4fd02e58960fa9c49d998e14332d5", "2a04dda79606f3d2f760384c38ccd3d5b9bb79d4c8126b67aff5eb09a253763e", "3c26010c1b51e1224a3ca6b8df807de6e95128b0908c7e34f190e7775455b0ca", "52c40f1a4262c896420c6ea1c6fda62cf67070e3947e3307f5562bd783a90336", "6e4f8d9e8aa79321657079b9ac03f3cf3fd067bf31c1cca4f56d49543f4356a5", "7242be12a58fec245ee9734e625964b97cf7e3f2f7d016603f9e56660ce479c7", "7dc253b542bfd4b4eb88d9dbae4ca079e7bf2e2afd819ee18891a43db66c60c7", "94f5bd885f67bbb25c82d80184abbf7ce4f6c3c3a41fbaa4182f034bba803e69", "a89e188daa119ffa0d03ce5123dee3f8ffd5115c896c2a9d4f0dbb3d8b95bfa3", "ad3399da9b0ca36e2f24de72f67ab2854a62e623274607e37e0ce5f5d5fa9166", "b0348be89275fd1d4c44ffa39530c41a21062f52299b1e3ee7d1c61f060044b8", "b5554368e4ede1856121b0dfa35ce71768102e4aa55e526cb8de7f374ff78722", "cbddc56b2502d3f87fda4f98d948eb5b11f36ff3902e17cb6cc44727f2200525", "d79f18f41751725c56eceab2a886f021d70fd70a6188fd386e29a045945ffc10", "dc2ca26a19ab32dc475dbad9dfe723d3a64c835f4c23f625c2b6566ca32b9f29", "dd9bcd4f294eb0633bb33d1a74febdd2b9018b8b8ed325f861fffcd2c7660bb8", "e8baab1bc7c9152715844f1faca6744f2416929de10d7639ed49555a85549f52", "ec31fe12668af687b99acf1567399632a7c47b0e17cfb9ae47c098644ef36797", "f12b4f7e2d8f9da3141564e6737d79016fe5336cc92de6814eba579744f65b0a", "f58ac38d5ca045a377b3b377c84df8175ab992c970a53332fa8ac2373df44ff7"] parse = ["1b68657434d371e5156048ca4a0c5aea5afc6ca59a2fea4dd1a575354f617142"] parse-type = ["6e906a66f340252e4c324914a60d417d33a4bea01292ea9bbf68b4fc123be8c9", "f596bdc75d3dd93036fbfe3d04127da9f6df0c26c36e01e76da85adef4336b3c"] diff --git a/pyproject.toml b/pyproject.toml index 1b84acde..19eafee4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ asteval = "^0.9.14" colorama = {version = "^0.4.1",platform = "win32"} python-dateutil = "^2.8" pyyaml = "^5.1" +mock = "^3.0" [tool.poetry.dev-dependencies] behave = "^1.2" From f0e5fe965371ecce4108a71fb6b15a4f3cfd7547 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Fri, 15 Nov 2019 09:33:47 +0100 Subject: [PATCH 31/93] Use native mocking --- features/steps/core.py | 8 ++++---- poetry.lock | 14 +------------- pyproject.toml | 1 - 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index fead2855..7070e10e 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -1,12 +1,12 @@ from __future__ import unicode_literals from __future__ import absolute_import +from unittest.mock import patch from behave import given, when, then from jrnl import cli, install, Journal, util, plugins from jrnl import __version__ from dateutil import parser as date_parser from collections import defaultdict -import mock import os import json import yaml @@ -76,9 +76,9 @@ def set_config(context, config_file): @when('we open the editor and exit') -@mock.patch('jrnl.util.get_text_from_editor', return_value="") -def open_editor_and_exit_without_entering_text(context, dead_param_to_satisfy_behave): - run(context, "jrnl") +def open_editor_and_exit_without_entering_text(context): + with patch('jrnl.util.get_text_from_editor', return_value=""): + run(context, "jrnl") @when('we run "{command}" and enter') diff --git a/poetry.lock b/poetry.lock index d4c35642..e772b2d2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -220,17 +220,6 @@ click = ">=3.3" livereload = ">=2.5.1" tornado = ">=5.0" -[[package]] -category = "main" -description = "Rolling backport of unittest.mock for all Pythons" -name = "mock" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.0.5" - -[package.dependencies] -six = "*" - [[package]] category = "main" description = "NumPy is the fundamental package for array computing with Python." @@ -395,7 +384,7 @@ version = "1.5.1" pytz = "*" [metadata] -content-hash = "ab4bffce011d607d66bdb9a3cadc5b7c67870ef4682c765a55af4d75ea3e0f73" +content-hash = "9896cf59c7552b6ad95219ee5555c7445a3fab39c2e4f4c6f3d991a36635e44b" python-versions = "^3.7" [metadata.hashes] @@ -420,7 +409,6 @@ markdown = ["2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a", markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] mkdocs = ["17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", "8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"] -mock = ["83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3", "d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"] numpy = ["0778076e764e146d3078b17c24c4d89e0ecd4ac5401beff8e1c87879043a0633", "141c7102f20abe6cf0d54c4ced8d565b86df4d3077ba2343b61a6db996cefec7", "14270a1ee8917d11e7753fb54fc7ffd1934f4d529235beec0b275e2ccf00333b", "27e11c7a8ec9d5838bc59f809bfa86efc8a4fd02e58960fa9c49d998e14332d5", "2a04dda79606f3d2f760384c38ccd3d5b9bb79d4c8126b67aff5eb09a253763e", "3c26010c1b51e1224a3ca6b8df807de6e95128b0908c7e34f190e7775455b0ca", "52c40f1a4262c896420c6ea1c6fda62cf67070e3947e3307f5562bd783a90336", "6e4f8d9e8aa79321657079b9ac03f3cf3fd067bf31c1cca4f56d49543f4356a5", "7242be12a58fec245ee9734e625964b97cf7e3f2f7d016603f9e56660ce479c7", "7dc253b542bfd4b4eb88d9dbae4ca079e7bf2e2afd819ee18891a43db66c60c7", "94f5bd885f67bbb25c82d80184abbf7ce4f6c3c3a41fbaa4182f034bba803e69", "a89e188daa119ffa0d03ce5123dee3f8ffd5115c896c2a9d4f0dbb3d8b95bfa3", "ad3399da9b0ca36e2f24de72f67ab2854a62e623274607e37e0ce5f5d5fa9166", "b0348be89275fd1d4c44ffa39530c41a21062f52299b1e3ee7d1c61f060044b8", "b5554368e4ede1856121b0dfa35ce71768102e4aa55e526cb8de7f374ff78722", "cbddc56b2502d3f87fda4f98d948eb5b11f36ff3902e17cb6cc44727f2200525", "d79f18f41751725c56eceab2a886f021d70fd70a6188fd386e29a045945ffc10", "dc2ca26a19ab32dc475dbad9dfe723d3a64c835f4c23f625c2b6566ca32b9f29", "dd9bcd4f294eb0633bb33d1a74febdd2b9018b8b8ed325f861fffcd2c7660bb8", "e8baab1bc7c9152715844f1faca6744f2416929de10d7639ed49555a85549f52", "ec31fe12668af687b99acf1567399632a7c47b0e17cfb9ae47c098644ef36797", "f12b4f7e2d8f9da3141564e6737d79016fe5336cc92de6814eba579744f65b0a", "f58ac38d5ca045a377b3b377c84df8175ab992c970a53332fa8ac2373df44ff7"] parse = ["1b68657434d371e5156048ca4a0c5aea5afc6ca59a2fea4dd1a575354f617142"] parse-type = ["6e906a66f340252e4c324914a60d417d33a4bea01292ea9bbf68b4fc123be8c9", "f596bdc75d3dd93036fbfe3d04127da9f6df0c26c36e01e76da85adef4336b3c"] diff --git a/pyproject.toml b/pyproject.toml index 19eafee4..1b84acde 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,6 @@ asteval = "^0.9.14" colorama = {version = "^0.4.1",platform = "win32"} python-dateutil = "^2.8" pyyaml = "^5.1" -mock = "^3.0" [tool.poetry.dev-dependencies] behave = "^1.2" From d378e2522df11eb485265776cefad2d4b89c36cd Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Fri, 15 Nov 2019 09:38:09 +0100 Subject: [PATCH 32/93] Add comment explaining discrepancy between expected and asserted output --- features/core.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/core.feature b/features/core.feature index 95ec9351..2f62c19b 100644 --- a/features/core.feature +++ b/features/core.feature @@ -20,6 +20,7 @@ Feature: Basic reading and writing to a journal When we run "jrnl -n 1" Then the output should contain "2013-07-23 09:00 A cold and stormy day." + # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" When we open the editor and exit From c0a1c171f140df0b35c38cff232ce8b91e4fc7d1 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 13:06:04 +0100 Subject: [PATCH 33/93] Fix check_empty_output method --- features/steps/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index ef3532d6..8bc73313 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -190,8 +190,8 @@ def check_json_output_path(context, path, value): @then('the output should be empty') -def check_empty_output(context, text=None): - assert (text or context.text) is None +def check_empty_output(context): + assert context.stdout_capture is None @then('the output should be') From ccb55392dd6dc7d539adbbd7142429965e694e35 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 13:18:14 +0100 Subject: [PATCH 34/93] Check message on stderr and patch subprocess.call --- features/core.feature | 4 ++-- features/steps/core.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/features/core.feature b/features/core.feature index 2f62c19b..854fa53f 100644 --- a/features/core.feature +++ b/features/core.feature @@ -23,8 +23,8 @@ Feature: Basic reading and writing to a journal # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" - When we open the editor and exit - Then the output should be empty + When we open the editor and enter "" + Then we should see the message "[Nothing saved to file]" Scenario: Filtering for dates Given we use the config "basic.yaml" diff --git a/features/steps/core.py b/features/steps/core.py index 8bc73313..d4bf1b47 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -67,9 +67,11 @@ def set_config(context, config_file): cf.write("version: {}".format(__version__)) -@when('we open the editor and exit') -def open_editor_and_exit_without_entering_text(context): - with patch('jrnl.util.get_text_from_editor', return_value=""): +@when('we open the editor and enter') +@when('we open the editor and enter {text}') +def open_editor_and_enter(context, text=""): + text = (text or context.text) + with patch('subprocess.call', return_value=text): run(context, "jrnl") From 9b0ebb7d847535603f0e631154e79391cd6d847f Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 13:42:30 +0100 Subject: [PATCH 35/93] Add _mock_editor_function --- features/steps/core.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index d4bf1b47..66fa1e11 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -71,7 +71,15 @@ def set_config(context, config_file): @when('we open the editor and enter {text}') def open_editor_and_enter(context, text=""): text = (text or context.text) - with patch('subprocess.call', return_value=text): + print("open_editor_and_enter called") + def _mock_editor_function(command): + print("_mock_editor_function called") + tmpfile = command.split()[-1] + print("TMPFILE:", tmpfile) + with open(tmpfile, "w+") as f: + f.write(text) + + with patch('subprocess.call', side_effect=_mock_editor_function): run(context, "jrnl") From 68772d3afe948137a11e5534c70bd68e739e0dee Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 13:51:55 +0100 Subject: [PATCH 36/93] Update features/steps/core.py Co-Authored-By: pspeter --- features/steps/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index 66fa1e11..7ab7d20d 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -74,7 +74,7 @@ def open_editor_and_enter(context, text=""): print("open_editor_and_enter called") def _mock_editor_function(command): print("_mock_editor_function called") - tmpfile = command.split()[-1] + tmpfile = command[-1] print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: f.write(text) From e63ae25433dcd6dfc92569333fd2a85fde0ddb59 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:07:39 +0100 Subject: [PATCH 37/93] Add return from mock function --- features/steps/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/features/steps/core.py b/features/steps/core.py index 7ab7d20d..354bb2bb 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -78,6 +78,7 @@ def open_editor_and_enter(context, text=""): print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: f.write(text) + return tmpfile with patch('subprocess.call', side_effect=_mock_editor_function): run(context, "jrnl") From 7dcc91431e947943be9d8a4b1415968ca77c5852 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:16:36 +0100 Subject: [PATCH 38/93] Add debug statements --- features/steps/core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/steps/core.py b/features/steps/core.py index 354bb2bb..84d4ccc9 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -78,6 +78,8 @@ def open_editor_and_enter(context, text=""): print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: f.write(text) + + print("File contents:", open(tmpfile, "r").read()) return tmpfile with patch('subprocess.call', side_effect=_mock_editor_function): From 87571fa55f5533af92a11bfd21bf936c849563ee Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:31:05 +0100 Subject: [PATCH 39/93] Debug --- jrnl/util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jrnl/util.py b/jrnl/util.py index 959d63a1..3b35c366 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -118,6 +118,8 @@ def get_text_from_editor(config, template=""): os.remove(tmpfile) if not raw: print('[Nothing saved to file]', file=sys.stderr) + else: + print("RAW: '" + raw + "'") return raw From 8d22283b93393316f8d293402e485c77b898d335 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:35:50 +0100 Subject: [PATCH 40/93] Update features/steps/core.py Co-Authored-By: pspeter --- features/steps/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index 84d4ccc9..2175bd28 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -68,7 +68,7 @@ def set_config(context, config_file): @when('we open the editor and enter') -@when('we open the editor and enter {text}') +@when('we open the editor and enter "{text}"') def open_editor_and_enter(context, text=""): text = (text or context.text) print("open_editor_and_enter called") From 4cfff00d0a3f1b7e7458f72815be5128f0d18999 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:40:31 +0100 Subject: [PATCH 41/93] Move sys.exit() down --- jrnl/cli.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/jrnl/cli.py b/jrnl/cli.py index 3ba6ec94..32cde8dc 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -196,8 +196,6 @@ def run(manual_args=None): print(f"[Could not read template at '{config['template']}']", file=sys.stderr) sys.exit(1) raw = util.get_text_from_editor(config, template) - if not raw: - sys.exit() else: try: print("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n", file=sys.stderr) @@ -208,7 +206,7 @@ def run(manual_args=None): if raw: args.text = [raw] else: - mode_compose = False + sys.exit() # This is where we finally open the journal! try: From c23efa7f801550c808e1adb4ec654bc11be70305 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:42:39 +0100 Subject: [PATCH 42/93] Fix test? --- features/core.feature | 2 +- features/steps/core.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/core.feature b/features/core.feature index 854fa53f..bff53e2f 100644 --- a/features/core.feature +++ b/features/core.feature @@ -23,7 +23,7 @@ Feature: Basic reading and writing to a journal # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" - When we open the editor and enter "" + When we open the editor and enter nothing Then we should see the message "[Nothing saved to file]" Scenario: Filtering for dates diff --git a/features/steps/core.py b/features/steps/core.py index 2175bd28..95e39af8 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -67,7 +67,7 @@ def set_config(context, config_file): cf.write("version: {}".format(__version__)) -@when('we open the editor and enter') +@when('we open the editor and enter nothing') @when('we open the editor and enter "{text}"') def open_editor_and_enter(context, text=""): text = (text or context.text) From bca12b4b16a6f0586b24f0cd3f53c9636fa1213a Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:46:34 +0100 Subject: [PATCH 43/93] Fix test? --- features/steps/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index 95e39af8..58d50e85 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -67,7 +67,7 @@ def set_config(context, config_file): cf.write("version: {}".format(__version__)) -@when('we open the editor and enter nothing') +@when('we open the editor and enter ""') @when('we open the editor and enter "{text}"') def open_editor_and_enter(context, text=""): text = (text or context.text) @@ -77,7 +77,10 @@ def open_editor_and_enter(context, text=""): tmpfile = command[-1] print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: - f.write(text) + if text is not None: + f.write(text) + else: + f.write("") print("File contents:", open(tmpfile, "r").read()) return tmpfile From 5d75bc25c7297a086b81ac8b0963b0289ab9b0b0 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:49:07 +0100 Subject: [PATCH 44/93] Fix test? --- features/core.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/core.feature b/features/core.feature index bff53e2f..854fa53f 100644 --- a/features/core.feature +++ b/features/core.feature @@ -23,7 +23,7 @@ Feature: Basic reading and writing to a journal # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" - When we open the editor and enter nothing + When we open the editor and enter "" Then we should see the message "[Nothing saved to file]" Scenario: Filtering for dates From b1c7deb9b9fc8694372126f5cfe0840c53448cb9 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:52:19 +0100 Subject: [PATCH 45/93] Clean up debug statements --- features/steps/core.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index 58d50e85..b48562e7 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -73,16 +73,13 @@ def open_editor_and_enter(context, text=""): text = (text or context.text) print("open_editor_and_enter called") def _mock_editor_function(command): - print("_mock_editor_function called") tmpfile = command[-1] - print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: if text is not None: f.write(text) else: f.write("") - print("File contents:", open(tmpfile, "r").read()) return tmpfile with patch('subprocess.call', side_effect=_mock_editor_function): From 7b84935470cbbbb0436c84d03d69dd48a7643be9 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:53:07 +0100 Subject: [PATCH 46/93] Clean up debug statements --- jrnl/util.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jrnl/util.py b/jrnl/util.py index 3b35c366..959d63a1 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -118,8 +118,6 @@ def get_text_from_editor(config, template=""): os.remove(tmpfile) if not raw: print('[Nothing saved to file]', file=sys.stderr) - else: - print("RAW: '" + raw + "'") return raw From 59a6aa3cfdf50cdc12c7e428926c6bf93d9b550f Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:54:03 +0100 Subject: [PATCH 47/93] Remove extraneous code --- features/steps/core.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index b48562e7..847cbeaf 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -202,11 +202,6 @@ def check_json_output_path(context, path, value): assert struct == value, struct -@then('the output should be empty') -def check_empty_output(context): - assert context.stdout_capture is None - - @then('the output should be') @then('the output should be "{text}"') def check_output(context, text=None): From 91dcb812d753dc07e12c24f23c64d5b3215c31a2 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:49:07 +0100 Subject: [PATCH 48/93] Remove extra space --- jrnl/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/cli.py b/jrnl/cli.py index 32cde8dc..738087e4 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -176,7 +176,7 @@ def run(manual_args=None): log.debug('Using journal "%s"', journal_name) mode_compose, mode_export, mode_import = guess_mode(args, config) - + # How to quit writing? if "win32" in sys.platform: _exit_multiline_code = "on a blank line, press Ctrl+Z and then Enter" From 455261c55e125433daa32e7c8e259ad7bb810683 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:49:46 +0100 Subject: [PATCH 49/93] Add test for empty stdin input --- features/core.feature | 10 +++++++++- features/steps/core.py | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/features/core.feature b/features/core.feature index 854fa53f..598b99f8 100644 --- a/features/core.feature +++ b/features/core.feature @@ -20,12 +20,20 @@ Feature: Basic reading and writing to a journal When we run "jrnl -n 1" Then the output should contain "2013-07-23 09:00 A cold and stormy day." - # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" When we open the editor and enter "" Then we should see the message "[Nothing saved to file]" + Scenario: Writing an empty entry from the command line + Given we use the config "basic.yaml" + When we run "jrnl ''" + Then we should get no error + And the unstripped output should be + """ + + """ + Scenario: Filtering for dates Given we use the config "basic.yaml" When we run "jrnl -on 2013-06-10 --short" diff --git a/features/steps/core.py b/features/steps/core.py index 847cbeaf..40b44f92 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -211,6 +211,15 @@ def check_output(context, text=None): for line_text, line_out in zip(text, out): assert line_text.strip() == line_out.strip(), [line_text.strip(), line_out.strip()] +@then('the unstripped output should be') +@then('the unstripped output should be "{text}"') +def check_output(context, text=None): + text = (text or context.text).splitlines() + out = context.stdout_capture.getvalue().splitlines() + assert len(text) == len(out), "Output has {} lines (expected: {})".format(len(out), len(text)) + for line_text, line_out in zip(text, out): + assert line_text == line_out, [line_text, line_out] + @then('the output should contain "{text}" in the local time') def check_output_time_inline(context, text): From fce60a364fdd16b17c404400b383282444a62d91 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:49:58 +0100 Subject: [PATCH 50/93] Remove extra debug line --- features/steps/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index 40b44f92..b9d6c02c 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -71,7 +71,6 @@ def set_config(context, config_file): @when('we open the editor and enter "{text}"') def open_editor_and_enter(context, text=""): text = (text or context.text) - print("open_editor_and_enter called") def _mock_editor_function(command): tmpfile = command[-1] with open(tmpfile, "w+") as f: From 0aee900ffc66437061fc873f003ea96a3c720e58 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:54:49 +0100 Subject: [PATCH 51/93] Fix test? --- features/core.feature | 3 +-- features/steps/core.py | 9 --------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/features/core.feature b/features/core.feature index 598b99f8..23058e12 100644 --- a/features/core.feature +++ b/features/core.feature @@ -29,9 +29,8 @@ Feature: Basic reading and writing to a journal Given we use the config "basic.yaml" When we run "jrnl ''" Then we should get no error - And the unstripped output should be + And the output should be """ - """ Scenario: Filtering for dates diff --git a/features/steps/core.py b/features/steps/core.py index b9d6c02c..2ec9e416 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -210,15 +210,6 @@ def check_output(context, text=None): for line_text, line_out in zip(text, out): assert line_text.strip() == line_out.strip(), [line_text.strip(), line_out.strip()] -@then('the unstripped output should be') -@then('the unstripped output should be "{text}"') -def check_output(context, text=None): - text = (text or context.text).splitlines() - out = context.stdout_capture.getvalue().splitlines() - assert len(text) == len(out), "Output has {} lines (expected: {})".format(len(out), len(text)) - for line_text, line_out in zip(text, out): - assert line_text == line_out, [line_text, line_out] - @then('the output should contain "{text}" in the local time') def check_output_time_inline(context, text): From b793ce5d6c25ae604cbb1762dbdc5bbb23777007 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:57:19 +0100 Subject: [PATCH 52/93] Fix test? --- features/core.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/core.feature b/features/core.feature index 23058e12..f37e151b 100644 --- a/features/core.feature +++ b/features/core.feature @@ -31,6 +31,7 @@ Feature: Basic reading and writing to a journal Then we should get no error And the output should be """ + """ Scenario: Filtering for dates From e4012426ca8f3750a14d7c0bbf3159b61f28f6a2 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 02:07:06 +0100 Subject: [PATCH 53/93] Update features/core.feature Co-Authored-By: pspeter --- features/core.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/core.feature b/features/core.feature index f37e151b..50f40984 100644 --- a/features/core.feature +++ b/features/core.feature @@ -27,7 +27,7 @@ Feature: Basic reading and writing to a journal Scenario: Writing an empty entry from the command line Given we use the config "basic.yaml" - When we run "jrnl ''" + When we run "jrnl" and enter "" Then we should get no error And the output should be """ From 9701401946e6d75b953e580e782f7eb79e103253 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Mon, 18 Nov 2019 14:51:41 +0100 Subject: [PATCH 54/93] Fix test? --- features/steps/core.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index 2ec9e416..0e4e2a75 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -101,12 +101,18 @@ def _mock_input(inputs): @when('we run "{command}" and enter') +@when('we run "{command}" and enter ""') @when('we run "{command}" and enter "{inputs1}"') @when('we run "{command}" and enter "{inputs1}" and "{inputs2}"') def run_with_input(context, command, inputs1="", inputs2=""): # create an iterator through all inputs. These inputs will be fed one by one # to the mocked calls for 'input()', 'util.getpass()' and 'sys.stdin.read()' - text = iter((inputs1, inputs2)) if inputs1 else iter(context.text.split("\n")) + if inputs1: + text = iter((inputs1, inputs2)) + elif context.text: + text = iter(context.text.split("\n")) + else: + text = "" args = ushlex(command)[1:] with patch("builtins.input", side_effect=_mock_input(text)) as mock_input: with patch("jrnl.util.getpass", side_effect=_mock_getpass(text)) as mock_getpass: From 7fbb5db5e71c14d47d94d966b681eb8b221f78df Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Mon, 18 Nov 2019 15:34:31 +0100 Subject: [PATCH 55/93] Fix no stdin input test --- features/core.feature | 3 +-- features/steps/core.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/features/core.feature b/features/core.feature index 50f40984..36601fde 100644 --- a/features/core.feature +++ b/features/core.feature @@ -28,8 +28,7 @@ Feature: Basic reading and writing to a journal Scenario: Writing an empty entry from the command line Given we use the config "basic.yaml" When we run "jrnl" and enter "" - Then we should get no error - And the output should be + Then the output should be """ """ diff --git a/features/steps/core.py b/features/steps/core.py index 0e4e2a75..af4d19a3 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -112,7 +112,7 @@ def run_with_input(context, command, inputs1="", inputs2=""): elif context.text: text = iter(context.text.split("\n")) else: - text = "" + text = iter(("", "")) args = ushlex(command)[1:] with patch("builtins.input", side_effect=_mock_input(text)) as mock_input: with patch("jrnl.util.getpass", side_effect=_mock_getpass(text)) as mock_getpass: From 4244ffb2ff1b6c0ffe4a57131d736e3092528a85 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 18 Nov 2019 18:59:57 -0800 Subject: [PATCH 56/93] Adding :new: label to github issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- .github/ISSUE_TEMPLATE/support_request.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 0019514d..288142cc 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: '' -labels: bug +labels: bug, :new: assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index a2970a2f..450aa48c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for jrnl title: '' -labels: enhancement +labels: enhancement, :new: assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md index d3ce69b0..70c8832c 100644 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -2,7 +2,7 @@ name: Support Request about: Get help with jrnl title: '' -labels: support +labels: support, :new: assignees: '' --- From 712c0d1e20117d22ac7b48ee75e966db7339db03 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 18 Nov 2019 19:06:22 -0800 Subject: [PATCH 57/93] Escaping colon label with quotes in new issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- .github/ISSUE_TEMPLATE/support_request.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 288142cc..3c17a017 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: '' -labels: bug, :new: +labels: bug, ":new:" assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 450aa48c..3740a47b 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for jrnl title: '' -labels: enhancement, :new: +labels: enhancement, ":new:" assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md index 70c8832c..2b6a86ef 100644 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -2,7 +2,7 @@ name: Support Request about: Get help with jrnl title: '' -labels: support, :new: +labels: support, ":new:" assignees: '' --- From 77f3a96648260e7a9bf3a738b7cfd4658c2851a7 Mon Sep 17 00:00:00 2001 From: micahellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 18 Nov 2019 19:18:26 -0800 Subject: [PATCH 58/93] Adding "new" labels to new issues --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- .github/ISSUE_TEMPLATE/support_request.md | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3c17a017..02c0c251 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: '' -labels: bug, ":new:" +labels: ":new:, bug" assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 3740a47b..8690ad71 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for jrnl title: '' -labels: enhancement, ":new:" +labels: ":new:, enhancement" assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md index 2b6a86ef..d9608885 100644 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -2,7 +2,7 @@ name: Support Request about: Get help with jrnl title: '' -labels: support, ":new:" +labels: ":new:, support" assignees: '' --- @@ -24,4 +24,3 @@ If you would fill out the below points, that would make our process a whole lot * **What have you tried?** * **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc) - From 3aae43e138ce7ef9f4787fb5fccbccf16086a5e1 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 18 Nov 2019 20:32:40 -0800 Subject: [PATCH 59/93] [GH-738] preventing readline usage on Windows - removes autocomplete on the install step for Windows users --- jrnl/install.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/jrnl/install.py b/jrnl/install.py index c104b46b..8eb73c76 100644 --- a/jrnl/install.py +++ b/jrnl/install.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import readline import glob import getpass import os @@ -14,6 +13,9 @@ from .util import UserAbort import yaml import logging import sys +if "win32" not in sys.platform: + # readline is not included in Windows Active Python + import readline DEFAULT_CONFIG_NAME = 'jrnl.yaml' DEFAULT_JOURNAL_NAME = 'journal.txt' @@ -108,14 +110,10 @@ def load_or_install_jrnl(): def install(): - def autocomplete(text, state): - expansions = glob.glob(os.path.expanduser(os.path.expandvars(text)) + '*') - expansions = [e + "/" if os.path.isdir(e) else e for e in expansions] - expansions.append(None) - return expansions[state] - readline.set_completer_delims(' \t\n;') - readline.parse_and_bind("tab: complete") - readline.set_completer(autocomplete) + if "win32" not in sys.platform: + readline.set_completer_delims(' \t\n;') + readline.parse_and_bind("tab: complete") + readline.set_completer(autocomplete) # Where to create the journal? path_query = f'Path to your journal file (leave blank for {JOURNAL_FILE_PATH}): ' @@ -146,3 +144,9 @@ def install(): if password: config['password'] = password return config + +def autocomplete(text, state): + expansions = glob.glob(os.path.expanduser(os.path.expandvars(text)) + '*') + expansions = [e + "/" if os.path.isdir(e) else e for e in expansions] + expansions.append(None) + return expansions[state] From d947b19bcd34027132514a17c5b461dcd0211c44 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 18 Nov 2019 21:39:28 -0800 Subject: [PATCH 60/93] [GH-693] work so far on updating developer documentation --- CONTRIBUTING.md | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0de5982e..8b20e4bd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,24 +1,28 @@ -Contributing -============ +# Contributing If you use jrnl, you can totally make our day by just saying "thanks for the code." It's your chance to make a programmer happy today! If you have a moment, let us know what you use jrnl for and how; it'll help us to make it even better! -Docs & Typos ------------- +# Table of Contents + * [Docs and Typos](#docs-and-typos) + * [Bugs](#bugs) + * [Feature requests and ideas](#feature-requests-and-ideas) + * [New programmers and programmers new to python](#new-programmers-and-programmers-new-to-python) + * [Developing jrnl](#developing-jrnl) + + +## Docs and Typos If you find a typo or a mistake in the docs, please fix it right away and send a pull request. The Right Way™ to fix the docs is to edit the `docs/*.md` files on the **master** branch. You can see the result if you run `make html` inside the project's root directory, which will open a browser that hot-reloads as you change the docs. This requires [mkdocs](https://www.mkdocs.org) to be installed. The `gh-pages` branch is automatically maintained and updates from `master`; you should never have to edit that. -Bugs ----- +## Bugs -Unfortunately, bugs happen. If you found one, please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new) and describe it as well as possible. If you're a programmer with some time, go ahead and send us a pull request! We'll review as quickly as we can. +Unfortunately, bugs happen. If you found one, please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe it as well as possible. If you're a programmer with some time, go ahead and send us a pull request that references the issue! We'll review as quickly as we can. -Feature requests and ideas --------------------------- +## Feature requests and ideas -So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. +So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. When discussing new features, please keep in mind our design goals. jrnl strives to do one thing well. To us, that means: @@ -27,9 +31,16 @@ When discussing new features, please keep in mind our design goals. jrnl strives * avoid duplicating functionality -A short note for new programmers and programmers new to python --------------------------------------------------------------- +## New programmers and programmers new to python -Although jrnl has grown quite a bit since its inception. The overall complexity (for an end-user program) is fairly low, and we hope you'll find the code easy enough to understand. +Although jrnl has grown quite a bit since its inception, the overall complexity (for an end-user program) is fairly low, and we hope you'll find the code easy enough to understand. If you have a question, please don't hesitate to ask! Python is known for its welcoming community and openness to novice programmers, so feel free to fork the code and play around with it! If you create something you want to share with us, please create a pull request. We never expect pull requests to be perfect, idiomatic, instantly mergeable code. We can work through it together! + +## Developing jrnl + +The jrnl source uses [poetry](https://poetry.eustace.io/) for dependency management. + + * To run the source: `python3 -m jrnl` + * To run tests: `make test` or `poetry run behave` if on Windows + * To build and install: `poetry install` then `poetry shell` From e925fe22f9f7c5c4051eb73b8be708b8c93841d7 Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Thu, 31 Oct 2019 21:22:50 +0100 Subject: [PATCH 61/93] Add password confirmation dialog --- features/encryption.feature | 42 ++++++++++++++++++++++++------ features/multiple_journals.feature | 7 ++++- features/steps/core.py | 28 +++++++++++--------- jrnl/EncryptedJournal.py | 2 +- jrnl/cli.py | 2 +- jrnl/util.py | 10 +++++-- 6 files changed, 66 insertions(+), 25 deletions(-) diff --git a/features/encryption.feature b/features/encryption.feature index d30c48b3..787fa850 100644 --- a/features/encryption.feature +++ b/features/encryption.feature @@ -3,29 +3,55 @@ Given we use the config "encrypted.yaml" When we run "jrnl -n 1" and enter "bad doggie no biscuit" Then the output should contain "Password" - and the output should contain "2013-06-10 15:40 Life is good" + And the output should contain "2013-06-10 15:40 Life is good" Scenario: Decrypting a journal Given we use the config "encrypted.yaml" When we run "jrnl --decrypt" and enter "bad doggie no biscuit" Then the config for journal "default" should have "encrypt" set to "bool:False" Then we should see the message "Journal decrypted" - and the journal should have 2 entries + And the journal should have 2 entries Scenario: Encrypting a journal Given we use the config "basic.yaml" - When we run "jrnl --encrypt" and enter "swordfish" and "n" + When we run "jrnl --encrypt" and enter + """ + swordfish + swordfish + n + """ Then we should see the message "Journal encrypted" - and the config for journal "default" should have "encrypt" set to "bool:True" + And the config for journal "default" should have "encrypt" set to "bool:True" When we run "jrnl -n 1" and enter "swordfish" Then the output should contain "Password" - and the output should contain "2013-06-10 15:40 Life is good" + And the output should contain "2013-06-10 15:40 Life is good" + + Scenario: Mistyping your password + Given we use the config "basic.yaml" + When we run "jrnl --encrypt" and enter + """ + swordfish + sordfish + swordfish + swordfish + n + """ + Then we should see the message "Passwords did not match" + And we should see the message "Journal encrypted" + And the config for journal "default" should have "encrypt" set to "bool:True" + When we run "jrnl -n 1" and enter "swordfish" + Then the output should contain "Password" + And the output should contain "2013-06-10 15:40 Life is good" Scenario: Storing a password in Keychain Given we use the config "multiple.yaml" - When we run "jrnl simple --encrypt" and enter "sabertooth" and "y" + When we run "jrnl simple --encrypt" and enter + """ + sabertooth + sabertooth + y + """ When we set the keychain password of "simple" to "sabertooth" Then the config for journal "simple" should have "encrypt" set to "bool:True" When we run "jrnl simple -n 1" - Then we should not see the message "Password" - and the output should contain "2013-06-10 15:40 Life is good" + Then the output should contain "2013-06-10 15:40 Life is good" diff --git a/features/multiple_journals.feature b/features/multiple_journals.feature index 28587f96..56c4b3a7 100644 --- a/features/multiple_journals.feature +++ b/features/multiple_journals.feature @@ -42,5 +42,10 @@ Feature: Multiple journals Scenario: Don't crash if no file exists for a configured encrypted journal Given we use the config "multiple.yaml" - When we run "jrnl new_encrypted Adding first entry" and enter "these three eyes" and "y" + When we run "jrnl new_encrypted Adding first entry" and enter + """ + these three eyes + these three eyes + n + """ Then we should see the message "Journal 'new_encrypted' created" diff --git a/features/steps/core.py b/features/steps/core.py index af4d19a3..375c1f74 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -26,7 +26,7 @@ class TestKeyring(keyring.backend.KeyringBackend): def get_password(self, servicename, username): return self.keys[servicename].get(username) - def delete_password(self, servicename, username, password): + def delete_password(self, servicename, username): self.keys[servicename][username] = None @@ -102,29 +102,33 @@ def _mock_input(inputs): @when('we run "{command}" and enter') @when('we run "{command}" and enter ""') -@when('we run "{command}" and enter "{inputs1}"') -@when('we run "{command}" and enter "{inputs1}" and "{inputs2}"') -def run_with_input(context, command, inputs1="", inputs2=""): +@when('we run "{command}" and enter "{inputs}"') +def run_with_input(context, command, inputs=""): # create an iterator through all inputs. These inputs will be fed one by one # to the mocked calls for 'input()', 'util.getpass()' and 'sys.stdin.read()' - if inputs1: - text = iter((inputs1, inputs2)) - elif context.text: + if context.text: text = iter(context.text.split("\n")) else: - text = iter(("", "")) + text = iter([inputs]) + args = ushlex(command)[1:] - with patch("builtins.input", side_effect=_mock_input(text)) as mock_input: - with patch("jrnl.util.getpass", side_effect=_mock_getpass(text)) as mock_getpass: - with patch("sys.stdin.read", side_effect=text) as mock_read: + with patch("builtins.input", side_effect=_mock_input(text)) as mock_input,\ + patch("getpass.getpass", side_effect=_mock_getpass(text)) as mock_getpass,\ + patch("sys.stdin.read", side_effect=text) as mock_read: try: cli.run(args or []) context.exit_status = 0 except SystemExit as e: context.exit_status = e.code - # assert at least one of the mocked input methods got called + # at least one of the mocked input methods got called assert mock_input.called or mock_getpass.called or mock_read.called + # all inputs were used + try: + next(text) + assert False, "Not all inputs were consumed" + except StopIteration: + pass diff --git a/jrnl/EncryptedJournal.py b/jrnl/EncryptedJournal.py index 1cce66b8..f3ff63c6 100644 --- a/jrnl/EncryptedJournal.py +++ b/jrnl/EncryptedJournal.py @@ -38,7 +38,7 @@ class EncryptedJournal(Journal.Journal): filename = filename or self.config['journal'] if not os.path.exists(filename): - password = util.getpass("Enter password for new journal: ") + password = util.create_password() if password: if util.yesno("Do you want to store the password in your keychain?", default=True): util.set_keychain(self.name, password) diff --git a/jrnl/cli.py b/jrnl/cli.py index 738087e4..945a728f 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -78,7 +78,7 @@ def encrypt(journal, filename=None): """ Encrypt into new file. If filename is not set, we encrypt the journal file itself. """ from . import EncryptedJournal - journal.config['password'] = util.getpass("Enter new password: ") + journal.config['password'] = util.create_password() journal.config['encrypt'] = True new_journal = EncryptedJournal.EncryptedJournal(None, **journal.config) diff --git a/jrnl/util.py b/jrnl/util.py index fc917ae9..f70b2df9 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -37,12 +37,18 @@ class UserAbort(Exception): pass -getpass = gp.getpass +def create_password(): + while True: + pw = gp.getpass("Enter password for new journal: ") + if pw == gp.getpass("Enter password again: "): + return pw + + print("Passwords did not match, please try again", file=sys.stderr) def get_password(validator, keychain=None, max_attempts=3): pwd_from_keychain = keychain and get_keychain(keychain) - password = pwd_from_keychain or getpass() + password = pwd_from_keychain or gp.getpass() result = validator(password) # Password is bad: if result is None and pwd_from_keychain: From 29545c3a81792422a3860f5d2d03898bc89ae30d Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 25 Nov 2019 19:39:58 -0800 Subject: [PATCH 62/93] [GH-741] adding test for bug that displays all entries -on today --- features/regression.feature | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/features/regression.feature b/features/regression.feature index 3e644b19..4534761b 100644 --- a/features/regression.feature +++ b/features/regression.feature @@ -62,3 +62,10 @@ Feature: Zapped bugs should stay dead. Then the output should contain "I'm going to activate the machine." Then the output should contain "I've crossed so many timelines. Is there any going back?" + Scenario: Viewing today's entries does not print the entire journal + # https://github.com/jrnl-org/jrnl/issues/741 + Given we use the config "basic.yaml" + When we run "jrnl -on today" + Then the output should not contain "Life is good" + Then the output should not contain "But I'm better." + From 13bdd94dfb2bc4c4cedc2614212d833784f4d5a1 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 25 Nov 2019 20:22:44 -0800 Subject: [PATCH 63/93] [GH-693] cleaning up "Developing" section and adding a note about behave --- CONTRIBUTING.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b20e4bd..414a7091 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,6 @@ If you find a typo or a mistake in the docs, please fix it right away and send a Unfortunately, bugs happen. If you found one, please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe it as well as possible. If you're a programmer with some time, go ahead and send us a pull request that references the issue! We'll review as quickly as we can. - ## Feature requests and ideas So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. @@ -30,7 +29,6 @@ When discussing new features, please keep in mind our design goals. jrnl strives * have a simple interface * avoid duplicating functionality - ## New programmers and programmers new to python Although jrnl has grown quite a bit since its inception, the overall complexity (for an end-user program) is fairly low, and we hope you'll find the code easy enough to understand. @@ -39,8 +37,9 @@ If you have a question, please don't hesitate to ask! Python is known for its we ## Developing jrnl -The jrnl source uses [poetry](https://poetry.eustace.io/) for dependency management. +The jrnl source uses [poetry](https://poetry.eustace.io/) for dependency management. You will need to install it to develop journal. - * To run the source: `python3 -m jrnl` - * To run tests: `make test` or `poetry run behave` if on Windows - * To build and install: `poetry install` then `poetry shell` + * To run tests: `make test` (or `poetry run behave` if on Windows) + * To run the source: `poetry install` then `poetry shell` then run `jrnl` with or without arguments as necessary + +For testing, jrnl uses [behave](https://behave.readthedocs.io/). From a529b70614c54d8270252ca1d3f3b70c61718c87 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Mon, 25 Nov 2019 19:59:53 -0800 Subject: [PATCH 64/93] Squashed commit of the following: commit 75113187432939a51486422c3f70b3a9e2bcf0aa Merge: 74d1854 47e10fb Author: Jonathan Wren <9453067+wren@users.noreply.github.com> Date: Thu Oct 24 17:02:10 2019 -0700 Merge pull request #665 from notbalanced/issue_662 Fixes Issue #662 - Day names not treated consistently for new entry commit 74d1854a4bba468221b4eee254bdee2bb40f5d5a Merge: 1bbf074 6a5726a Author: Jonathan Wren <9453067+wren@users.noreply.github.com> Date: Sat Oct 5 15:30:57 2019 -0700 Merge pull request #418 from philipsd6/2.0-fancy_exporter Add exporter to output entries inside unicode box character boxes commit 47e10fbee746dba14f2550610b06301e7925a24f Author: Craig Moyer Date: Sun Sep 29 19:06:53 2019 -0400 Fix issue #662 to properly handle day names as new entry dates and command line (-on, -from, -to). commit 9588913100979b2a33ab87f60b5233f79c5bdf7d Author: Craig Moyer Date: Sun Sep 29 08:27:27 2019 -0400 Syncing with jrnl-org/master commit 4c68eb193d10db13fe9ec1321f75c89e6baf76f9 Merge: 81dfebb 1bbf074 Author: Craig Moyer Date: Sun Sep 29 07:52:02 2019 -0400 Merge remote-tracking branch 'upstream/master' into 2.0-rc1-maebert commit 81dfebb2c0ef4d4ebf16e8c29fdec5f4a9d9afb3 Author: Manuel Ebert Date: Mon Apr 29 20:34:18 2019 +0200 export changes commit 6a5726acd2102a715c2b1ec92190982ce66eaf07 Author: Philip Douglass Date: Fri Dec 22 20:56:36 2017 -0500 Enable FancyExporter plugin commit 3d1b22687131f5adf8c69769e4b6c79232337be8 Author: Philip Douglass Date: Fri Jan 29 11:17:41 2016 -0500 Add exporter to output entries inside unicode box character boxes --- features/regression.feature | 47 ++++++++++++++++++++++++++++ features/steps/core.py | 13 ++++++-- jrnl/Journal.py | 3 +- jrnl/plugins/__init__.py | 3 +- jrnl/plugins/fancy_exporter.py | 56 ++++++++++++++++++++++++++++++++++ jrnl/time.py | 14 ++++++--- 6 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 jrnl/plugins/fancy_exporter.py diff --git a/features/regression.feature b/features/regression.feature index 3e644b19..fb4f0060 100644 --- a/features/regression.feature +++ b/features/regression.feature @@ -62,3 +62,50 @@ Feature: Zapped bugs should stay dead. Then the output should contain "I'm going to activate the machine." Then the output should contain "I've crossed so many timelines. Is there any going back?" + Scenario: Create entry using day of the week as entry date. + Given we use the config "basic.yaml" + When we run "jrnl monday: This is an entry on a Monday." + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain "monday at 9am" in the local time + Then the output should contain "This is an entry on a Monday." + + Scenario: Create entry using day of the week abbreviations as entry date. + Given we use the config "basic.yaml" + When we run "jrnl fri: This is an entry on a Friday." + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain "friday at 9am" in the local time + + Scenario: Displaying entries using -on today should display entries created today. + Given we use the config "basic.yaml" + When we run "jrnl today: Adding an entry right now." + Then we should see the message "Entry added" + When we run "jrnl -on today" + Then the output should contain "Adding an entry right now." + + Scenario: Displaying entries using -from day should display correct entries + Given we use the config "basic.yaml" + When we run "jrnl yesterday: This thing happened yesterday" + Then we should see the message "Entry added" + When we run "jrnl today at 11:59pm: Adding an entry right now." + Then we should see the message "Entry added" + When we run "jrnl tomorrow: A future entry." + Then we should see the message "Entry added" + When we run "jrnl -from today" + Then the output should contain "Adding an entry right now." + Then the output should contain "A future entry." + Then the output should not contain "This thing happened yesterday" + + Scenario: Displaying entries using -from and -to day should display correct entries + Given we use the config "basic.yaml" + When we run "jrnl yesterday: This thing happened yesterday" + Then we should see the message "Entry added" + When we run "jrnl today at 11:59pm: Adding an entry right now." + Then we should see the message "Entry added" + When we run "jrnl tomorrow: A future entry." + Then we should see the message "Entry added" + When we run "jrnl -from yesterday -to today" + Then the output should contain "This thing happened yesterday" + Then the output should contain "Adding an entry right now." + Then the output should not contain "A future entry." diff --git a/features/steps/core.py b/features/steps/core.py index af4d19a3..314a5167 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -5,6 +5,9 @@ from jrnl import cli, install, Journal, util, plugins from jrnl import __version__ from dateutil import parser as date_parser from collections import defaultdict +try: import parsedatetime.parsedatetime_consts as pdt +except ImportError: import parsedatetime as pdt +import time import os import json import yaml @@ -13,6 +16,10 @@ import tzlocal import shlex import sys +consts = pdt.Constants(usePyICU=False) +consts.DOWParseStyle = -1 # Prefers past weekdays +CALENDAR = pdt.Calendar(consts) + class TestKeyring(keyring.backend.KeyringBackend): """A test keyring that just stores its values in a hash""" @@ -221,9 +228,9 @@ def check_output(context, text=None): def check_output_time_inline(context, text): out = context.stdout_capture.getvalue() local_tz = tzlocal.get_localzone() - utc_time = date_parser.parse(text) - local_date = utc_time.astimezone(local_tz).strftime("%Y-%m-%d %H:%M") - assert local_date in out, local_date + date, flag = CALENDAR.parse(text) + output_date = time.strftime("%Y-%m-%d %H:%M",date) + assert output_date in out, output_date @then('the output should contain') diff --git a/jrnl/Journal.py b/jrnl/Journal.py index f6813222..0fd41c22 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -122,7 +122,8 @@ class Journal: try: new_date = datetime.strptime(date_blob, self.config["timeformat"]) except ValueError: - new_date = time.parse(date_blob) + # Passing in a date that had brackets around it + new_date = time.parse(date_blob, bracketed=True) if new_date: if entries: diff --git a/jrnl/plugins/__init__.py b/jrnl/plugins/__init__.py index 8d59b556..53b595e3 100644 --- a/jrnl/plugins/__init__.py +++ b/jrnl/plugins/__init__.py @@ -9,8 +9,9 @@ from .tag_exporter import TagExporter from .xml_exporter import XMLExporter from .yaml_exporter import YAMLExporter from .template_exporter import __all__ as template_exporters +from .fancy_exporter import FancyExporter -__exporters =[JSONExporter, MarkdownExporter, TagExporter, TextExporter, XMLExporter, YAMLExporter] + template_exporters +__exporters =[JSONExporter, MarkdownExporter, TagExporter, TextExporter, XMLExporter, YAMLExporter, FancyExporter] + template_exporters __importers =[JRNLImporter] __exporter_types = {name: plugin for plugin in __exporters for name in plugin.names} diff --git a/jrnl/plugins/fancy_exporter.py b/jrnl/plugins/fancy_exporter.py new file mode 100644 index 00000000..74d4555f --- /dev/null +++ b/jrnl/plugins/fancy_exporter.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from __future__ import absolute_import, unicode_literals, print_function +from .text_exporter import TextExporter +from textwrap import TextWrapper + + +class FancyExporter(TextExporter): + """This Exporter can convert entries and journals into text with unicode box drawing characters.""" + names = ["fancy", "boxed"] + extension = "txt" + + border_a="┎" + border_b="─" + border_c="╮" + border_d="╘" + border_e="═" + border_f="╕" + border_g="┃" + border_h="│" + border_i="┠" + border_j="╌" + border_k="┤" + border_l="┖" + border_m="┘" + + @classmethod + def export_entry(cls, entry): + """Returns a fancy unicode representation of a single entry.""" + date_str = entry.date.strftime(entry.journal.config['timeformat']) + linewrap = entry.journal.config['linewrap'] or 78 + initial_linewrap = linewrap - len(date_str) - 2 + body_linewrap = linewrap - 2 + card = [cls.border_a + cls.border_b*(initial_linewrap) + cls.border_c + date_str] + w = TextWrapper(width=initial_linewrap, initial_indent=cls.border_g+' ', subsequent_indent=cls.border_g+' ') + title_lines = w.wrap(entry.title) + card.append(title_lines[0].ljust(initial_linewrap+1) + cls.border_d + cls.border_e*(len(date_str)-1) + cls.border_f) + w.width = body_linewrap + if len(title_lines) > 1: + for line in w.wrap(' '.join([title_line[len(w.subsequent_indent):] + for title_line in title_lines[1:]])): + card.append(line.ljust(body_linewrap+1) + cls.border_h) + if entry.body: + card.append(cls.border_i + cls.border_j*body_linewrap + cls.border_k) + for line in entry.body.splitlines(): + body_lines = w.wrap(line) or [cls.border_g] + for body_line in body_lines: + card.append(body_line.ljust(body_linewrap+1) + cls.border_h) + card.append(cls.border_l + cls.border_b*body_linewrap + cls.border_m) + return "\n".join(card) + + @classmethod + def export_journal(cls, journal): + """Returns a unicode representation of an entire journal.""" + return "\n".join(cls.export_entry(entry) for entry in journal) diff --git a/jrnl/time.py b/jrnl/time.py index eee3fc20..66d3f4f8 100644 --- a/jrnl/time.py +++ b/jrnl/time.py @@ -12,15 +12,16 @@ consts.DOWParseStyle = -1 # "Monday" will be either today or the last Monday CALENDAR = pdt.Calendar(consts) -def parse(date_str, inclusive=False, default_hour=None, default_minute=None): +def parse(date_str, inclusive=False, default_hour=None, default_minute=None, bracketed=False): """Parses a string containing a fuzzy date and returns a datetime.datetime object""" if not date_str: return None elif isinstance(date_str, datetime): return date_str - # Don't try to parse anything with 6 or less characters. It's probably a markdown footnote - if len(date_str) <= 6: + # Don't try to parse anything with 6 or less characters and was parsed from the existing journal. + # It's probably a markdown footnote + if len(date_str) <= 6 and bracketed: return None default_date = DEFAULT_FUTURE if inclusive else DEFAULT_PAST @@ -51,8 +52,11 @@ def parse(date_str, inclusive=False, default_hour=None, default_minute=None): except TypeError: return None - if flag == 1: # Date found, but no time. Use the default time. - date = datetime(*date[:3], hour=default_hour or 0, minute=default_minute or 0) + if flag is 1: # Date found, but no time. Use the default time. + date = datetime(*date[:3], + hour=23 if inclusive else default_hour or 0, + minute=59 if inclusive else default_minute or 0, + second=59 if inclusive else 0) else: date = datetime(*date[:6]) From 4ecaf197f598ab1dc010e66f3434402aa52819bb Mon Sep 17 00:00:00 2001 From: Jrnl Bot Date: Tue, 26 Nov 2019 05:00:15 +0000 Subject: [PATCH 65/93] Incrementing version to v2.1.1-beta --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 70a44402..cf6111fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "jrnl" -version = "v2.1.post2" +version = "v2.1.1-beta" description = "Collect your thoughts and notes without leaving the command line." authors = [ "Manuel Ebert ", From cab23ddc97c0adb083ce2d66808cad581ed63005 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 28 Nov 2019 07:43:05 -0700 Subject: [PATCH 66/93] [Travis] add testing on Windows and Mac See #739 (Python 3.8), #619 (Windows) --- .travis.yml | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index e1f35e61..7efc4fd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,33 @@ dist: xenial # required for Python >= 3.7 language: python -python: - - 3.6 - - 3.7 +jobs: + include: + - name: "Python 3.6 on Linux" + python: 3.6 + - name: "Python 3.7 on Linux" + python: 3.7 + - name: "Python 3.8 on Linux" + python: 3.8 + - name: "Python, developmental version, on Linux" + python: nightly + - name: "Python 3.7.4 on MacOS" + os: osx + osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4 + language: shell # 'language: python' is an error on Travis CI macOS + - name: "Python 3.8.0 on Windows" + os: windows + langage: shell # 'language: python' is an error on Travis CI Windows + before_install: + - choco install python --version 3.8.0 + - python -m pip install --upgrade pip + allow_failures: + - python: 3.8 + - python: nightly git: depth: false -before_install: - - pip install poetry~=0.12.17 +cache: pip install: + - pip install poetry~=0.12.17 # we run `poetry version` here to appease poetry about '0.0.0-source' - poetry version - poetry install From b0f07eeb5981253cbdaefba7eb55fbb2d9d3e232 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 28 Nov 2019 07:49:33 -0700 Subject: [PATCH 67/93] Appease the Travis gods --- .travis.yml | 9 ++++++--- pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7efc4fd3..eac6f3f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ dist: xenial # required for Python >= 3.7 -language: python jobs: include: - name: "Python 3.6 on Linux" @@ -8,26 +7,30 @@ jobs: python: 3.7 - name: "Python 3.8 on Linux" python: 3.8 - - name: "Python, developmental version, on Linux" + - name: "Python dev on Linux" python: nightly - name: "Python 3.7.4 on MacOS" os: osx osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4 language: shell # 'language: python' is an error on Travis CI macOS + before_install: + - pip3 install poetry~=0.12.17 - name: "Python 3.8.0 on Windows" os: windows langage: shell # 'language: python' is an error on Travis CI Windows before_install: - choco install python --version 3.8.0 - python -m pip install --upgrade pip + - pip install poetry~=0.12.17 allow_failures: - python: 3.8 - python: nightly git: depth: false cache: pip -install: +before_install: - pip install poetry~=0.12.17 +install: # we run `poetry version` here to appease poetry about '0.0.0-source' - poetry version - poetry install diff --git a/pyproject.toml b/pyproject.toml index 70a44402..136aeb97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ homepage = "https://jrnl.sh" repository = "https://github.com/jrnl-org/jrnl" [tool.poetry.dependencies] -python = ">=3.6.0, <3.8.0" +python = ">=3.6.0, <3.9.0" pyxdg = "^0.26.0" cryptography = "^2.7" passlib = "^1.7" From 3c2ced098915ad31a2acd2af3315017b9575fb12 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 28 Nov 2019 07:59:06 -0700 Subject: [PATCH 68/93] [Travis] no auto-ruby --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index eac6f3f2..22241a4d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,16 @@ dist: xenial # required for Python >= 3.7 jobs: include: - name: "Python 3.6 on Linux" + language: python python: 3.6 - name: "Python 3.7 on Linux" + language: python python: 3.7 - name: "Python 3.8 on Linux" + language: python python: 3.8 - name: "Python dev on Linux" + language: python python: nightly - name: "Python 3.7.4 on MacOS" os: osx From cdad3230f023ed6df6c29f21c7b5d92d2a8cb9f1 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 28 Nov 2019 08:21:01 -0700 Subject: [PATCH 69/93] [Travis] include a test in not UTC see #742 --- .travis.yml | 17 ++++++++++------- features/steps/core.py | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 22241a4d..19fa4084 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,31 @@ dist: xenial # required for Python >= 3.7 +language: python jobs: include: - name: "Python 3.6 on Linux" - language: python python: 3.6 - name: "Python 3.7 on Linux" - language: python python: 3.7 + - name: "Python 3.7 on Linux, not UTC" + python: 3.7 + env: + - TZ=America/Edmonton - name: "Python 3.8 on Linux" - language: python python: 3.8 - name: "Python dev on Linux" - language: python python: nightly - name: "Python 3.7.4 on MacOS" os: osx osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4 language: shell # 'language: python' is an error on Travis CI macOS before_install: - - pip3 install poetry~=0.12.17 + - pip3 install poetry~=0.12.17 # 'pip' points to Python 2 on MacOS - name: "Python 3.8.0 on Windows" os: windows - langage: shell # 'language: python' is an error on Travis CI Windows + language: shell # 'language: python' is an error on Travis CI Windows before_install: - choco install python --version 3.8.0 + - refreshenv # add Python to PATH - python -m pip install --upgrade pip - pip install poetry~=0.12.17 allow_failures: @@ -33,13 +35,14 @@ git: depth: false cache: pip before_install: + - date - pip install poetry~=0.12.17 install: # we run `poetry version` here to appease poetry about '0.0.0-source' - poetry version - poetry install -script: - poetry run python --version +script: - poetry run behave before_deploy: - poetry config http-basic.pypi $PYPI_USER $PYPI_PASS diff --git a/features/steps/core.py b/features/steps/core.py index 314a5167..5e8e3ea3 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -164,7 +164,7 @@ def has_error(context): @then('we should get no error') def no_error(context): - assert context.exit_status is 0, context.exit_status + assert context.exit_status == 0, context.exit_status @then('the output should be parsable as json') From 9cad7ddb5110b3debbd7f237335664ca0d2ebcee Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 28 Nov 2019 08:39:22 -0700 Subject: [PATCH 70/93] [Travis] Add Python to PATH on Windows --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 19fa4084..e8278da2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,9 +25,10 @@ jobs: language: shell # 'language: python' is an error on Travis CI Windows before_install: - choco install python --version 3.8.0 - - refreshenv # add Python to PATH - python -m pip install --upgrade pip - pip install poetry~=0.12.17 + env: + - PATH=/c/Python38:/c/Python38/Scripts:$PATH allow_failures: - python: 3.8 - python: nightly From 34f7d9830f3aff457336b686f174e2685585fc94 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 28 Nov 2019 08:50:06 -0700 Subject: [PATCH 71/93] [Travis] Windows: try Python 3.7 for cryptography wheels? --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e8278da2..5f020fc1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,15 +20,16 @@ jobs: language: shell # 'language: python' is an error on Travis CI macOS before_install: - pip3 install poetry~=0.12.17 # 'pip' points to Python 2 on MacOS - - name: "Python 3.8.0 on Windows" + - name: "Python 3.7.5 on Windows" os: windows language: shell # 'language: python' is an error on Travis CI Windows before_install: - - choco install python --version 3.8.0 + - choco install python --version 3.7.5 - python -m pip install --upgrade pip + - pip --version - pip install poetry~=0.12.17 env: - - PATH=/c/Python38:/c/Python38/Scripts:$PATH + - PATH=/c/Python37:/c/Python37/Scripts:$PATH allow_failures: - python: 3.8 - python: nightly From ae505dcbbda63dc07891d116d447230265451248 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 28 Nov 2019 09:14:50 -0700 Subject: [PATCH 72/93] [Travis] Windows tests time out --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5f020fc1..297ce340 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ jobs: allow_failures: - python: 3.8 - python: nightly + - os: windows git: depth: false cache: pip From 19fb2207dd15242fe1a6dcb0451c00bec32d94fc Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 11:35:30 -0800 Subject: [PATCH 73/93] [#757] Move deploy to it's own stage on CI so it doesn't run multiple times --- .travis.yml | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 297ce340..b10cb9b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,25 @@ jobs: - pip install poetry~=0.12.17 env: - PATH=/c/Python37:/c/Python37/Scripts:$PATH + - stage: "Deploy" + before_deploy: + - poetry config http-basic.pypi $PYPI_USER $PYPI_PASS + - poetry version $TRAVIS_TAG + - poetry build + deploy: + - provider: script + script: poetry publish + skip_cleanup: true + on: + branch: master + tags: true + after_deploy: + - git config --global user.email "jrnl.bot@gmail.com" + - git config --global user.name "Jrnl Bot" + - git checkout master + - git add pyproject.toml + - git commit -m "Incrementing version to ${TRAVIS_TAG}" + - git push https://${GITHUB_TOKEN}@github.com/jrnl-org/jrnl.git master allow_failures: - python: 3.8 - python: nightly @@ -47,21 +66,4 @@ install: - poetry run python --version script: - poetry run behave -before_deploy: - - poetry config http-basic.pypi $PYPI_USER $PYPI_PASS - - poetry version $TRAVIS_TAG - - poetry build -deploy: - - provider: script - script: poetry publish - skip_cleanup: true - on: - branch: master - tags: true -after_deploy: - - git config --global user.email "jrnl.bot@gmail.com" - - git config --global user.name "Jrnl Bot" - - git checkout master - - git add pyproject.toml - - git commit -m "Incrementing version to ${TRAVIS_TAG}" - - git push https://${GITHUB_TOKEN}@github.com/jrnl-org/jrnl.git master + From 44531bb47f7c53608809e6d12eb0726d4a649366 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 11:36:31 -0800 Subject: [PATCH 74/93] [#757] Take out old code (no longer needed) --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b10cb9b2..ad07de69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,8 +60,6 @@ before_install: - date - pip install poetry~=0.12.17 install: - # we run `poetry version` here to appease poetry about '0.0.0-source' - - poetry version - poetry install - poetry run python --version script: From 7fe1281c38e7b5706b8e9cc5768a6d23ca5c1d42 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 11:37:01 -0800 Subject: [PATCH 75/93] [#757] Add quotes around environment variables to be a little safer --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad07de69..a948fbac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,8 +32,8 @@ jobs: - PATH=/c/Python37:/c/Python37/Scripts:$PATH - stage: "Deploy" before_deploy: - - poetry config http-basic.pypi $PYPI_USER $PYPI_PASS - - poetry version $TRAVIS_TAG + - poetry config http-basic.pypi "$PYPI_USER" "$PYPI_PASS" + - poetry version "$TRAVIS_TAG" - poetry build deploy: - provider: script From f64f7428394c7be7994fd54bd21ac94015dde8fb Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 11:37:51 -0800 Subject: [PATCH 76/93] [#757] add stage name to test stage to be explicit --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a948fbac..ececef6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,8 @@ dist: xenial # required for Python >= 3.7 language: python jobs: include: - - name: "Python 3.6 on Linux" + - stage: "Test" + name: "Python 3.6 on Linux" python: 3.6 - name: "Python 3.7 on Linux" python: 3.7 From 7f83a1ef263816a8f5ca1a40499a674c198def43 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 11:38:14 -0800 Subject: [PATCH 77/93] [#757] add explicit os at root per travis config linter --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ececef6e..c65ac960 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ dist: xenial # required for Python >= 3.7 +os: linux language: python jobs: include: From 427f1321b7bea1143f241b38dc68fdf3d2fda53b Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 12:11:17 -0800 Subject: [PATCH 78/93] [#757] update CI logic to new format --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c65ac960..2872d30c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ jobs: env: - PATH=/c/Python37:/c/Python37/Scripts:$PATH - stage: "Deploy" + if: branch = master AND tag IS present before_deploy: - poetry config http-basic.pypi "$PYPI_USER" "$PYPI_PASS" - poetry version "$TRAVIS_TAG" @@ -40,10 +41,6 @@ jobs: deploy: - provider: script script: poetry publish - skip_cleanup: true - on: - branch: master - tags: true after_deploy: - git config --global user.email "jrnl.bot@gmail.com" - git config --global user.name "Jrnl Bot" From b1307674d879ca5a3314dfda754fe4e4eff67ff5 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Fri, 29 Nov 2019 12:51:25 -0800 Subject: [PATCH 79/93] Display header in mobile docs --- docs/assets/theme.css | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/assets/theme.css b/docs/assets/theme.css index a3de87a9..687b3347 100644 --- a/docs/assets/theme.css +++ b/docs/assets/theme.css @@ -45,9 +45,17 @@ p { /* No-one likes lines that are 400 characters long. */ div.rst-content {max-width: 54em;} -.wy-side-nav-search, .wy-nav-top, .wy-menu-vertical li.current { +.wy-side-nav-search, .wy-menu-vertical li.current { background-color: transparent; } +.wy-nav-top { + background-image: linear-gradient(-211deg, #95699C 0%, #604385 100%); +} + +.wy-nav-top .fa-bars { + line-height: 50px; +} + .wy-side-nav-search a.icon-home { width: 100%; max-width: 250px; From c6eab97f64e00bed82e0189f0c7049a68e3932cf Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 15:05:15 -0800 Subject: [PATCH 80/93] [#757] Take out unneeded quotes around strings in yaml --- .travis.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2872d30c..501cbd5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,26 +3,26 @@ os: linux language: python jobs: include: - - stage: "Test" - name: "Python 3.6 on Linux" + - stage: Test + - name: Python 3.6 on Linux python: 3.6 - - name: "Python 3.7 on Linux" + - name: Python 3.7 on Linux python: 3.7 - - name: "Python 3.7 on Linux, not UTC" + - name: Python 3.7 on Linux, not UTC python: 3.7 env: - TZ=America/Edmonton - - name: "Python 3.8 on Linux" + - name: Python 3.8 on Linux python: 3.8 - - name: "Python dev on Linux" + - name: Python nightly on Linux python: nightly - - name: "Python 3.7.4 on MacOS" + - name: Python 3.7.4 on MacOS os: osx osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4 language: shell # 'language: python' is an error on Travis CI macOS before_install: - pip3 install poetry~=0.12.17 # 'pip' points to Python 2 on MacOS - - name: "Python 3.7.5 on Windows" + - name: Python 3.7 on Windows os: windows language: shell # 'language: python' is an error on Travis CI Windows before_install: @@ -32,7 +32,7 @@ jobs: - pip install poetry~=0.12.17 env: - PATH=/c/Python37:/c/Python37/Scripts:$PATH - - stage: "Deploy" + - stage: Deploy if: branch = master AND tag IS present before_deploy: - poetry config http-basic.pypi "$PYPI_USER" "$PYPI_PASS" From a01fb67c87e11f942adacff036be73d39ecca587 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 15:06:13 -0800 Subject: [PATCH 81/93] [#757] Clean up travis yaml file for readability --- .travis.yml | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 501cbd5c..22b69409 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,28 @@ dist: xenial # required for Python >= 3.7 os: linux language: python +cache: pip + +git: + depth: false + +before_install: + - date + - pip install poetry~=0.12.17 + +install: + - poetry install + - poetry run python --version + +script: + - poetry run behave + jobs: + allow_failures: + - python: 3.8 + - python: nightly + - os: windows + include: - stage: Test - name: Python 3.6 on Linux @@ -32,6 +53,7 @@ jobs: - pip install poetry~=0.12.17 env: - PATH=/c/Python37:/c/Python37/Scripts:$PATH + - stage: Deploy if: branch = master AND tag IS present before_deploy: @@ -48,19 +70,4 @@ jobs: - git add pyproject.toml - git commit -m "Incrementing version to ${TRAVIS_TAG}" - git push https://${GITHUB_TOKEN}@github.com/jrnl-org/jrnl.git master - allow_failures: - - python: 3.8 - - python: nightly - - os: windows -git: - depth: false -cache: pip -before_install: - - date - - pip install poetry~=0.12.17 -install: - - poetry install - - poetry run python --version -script: - - poetry run behave From 7b27e6d0c08ab4f575329ded79cb2d2799a83509 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 15:07:44 -0800 Subject: [PATCH 82/93] [#757] Add more testing for Windows across multiple Python versions --- .travis.yml | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 22b69409..67b3cf2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,15 +8,24 @@ git: before_install: - date - - pip install poetry~=0.12.17 install: + - pip install poetry~=0.12.17 - poetry install - poetry run python --version script: - poetry run behave +aliases: + test_windows: &test_windows + os: windows + language: shell + before_install: + - choco install python --version $JRNL_PYTHON_VERSION + - python -m pip install --upgrade pip + - pip --version + jobs: allow_failures: - python: 3.8 @@ -41,18 +50,21 @@ jobs: os: osx osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4 language: shell # 'language: python' is an error on Travis CI macOS - before_install: - - pip3 install poetry~=0.12.17 # 'pip' points to Python 2 on MacOS - - name: Python 3.7 on Windows - os: windows - language: shell # 'language: python' is an error on Travis CI Windows - before_install: - - choco install python --version 3.7.5 - - python -m pip install --upgrade pip - - pip --version - - pip install poetry~=0.12.17 + - <<: *test_windows + name: Python 3.6 on Windows env: + - JRNL_PYTHON_VERSION=3.6.8 + - PATH=/c/Python36:/c/Python36/Scripts:$PATH + - <<: *test_windows + name: Python 3.7 on Windows + env: + - JRNL_PYTHON_VERSION=3.7.5 - PATH=/c/Python37:/c/Python37/Scripts:$PATH + - <<: *test_windows + name: Python 3.8 on Windows + env: + - JRNL_PYTHON_VERSION=3.8.0 + - PATH=/c/Python38:/c/Python38/Scripts:$PATH - stage: Deploy if: branch = master AND tag IS present From cbd0014a52bd72cb4811fb05462049495fc68188 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 15:38:32 -0800 Subject: [PATCH 83/93] [#757] Add more Mac tests for each Python version --- .travis.yml | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 67b3cf2e..76214d83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,16 @@ script: - poetry run behave aliases: + test_mac: &test_mac + os: osx + language: shell + osx_image: xcode11.2 + before_install: + - eval "$(pyenv init -)" + - pyenv install -s $JRNL_PYTHON_VERSION + - pyenv global $JRNL_PYTHON_VERSION + - pip install --upgrade pip + - pip --version test_windows: &test_windows os: windows language: shell @@ -46,10 +56,20 @@ jobs: python: 3.8 - name: Python nightly on Linux python: nightly - - name: Python 3.7.4 on MacOS - os: osx - osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4 - language: shell # 'language: python' is an error on Travis CI macOS + + - <<: *test_mac + name: Python 3.6 on MacOS + env: + - JRNL_PYTHON_VERSION=3.6.8 + - <<: *test_mac + name: Python 3.7 on MacOS + env: + - JRNL_PYTHON_VERSION=3.7.4 + - <<: *test_mac + name: Python 3.8 on MacOS + env: + - JRNL_PYTHON_VERSION=3.8.0 + - <<: *test_windows name: Python 3.6 on Windows env: From f16e7860e043fff5b73036b1c16cf9e1b254e7ed Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 16:21:03 -0800 Subject: [PATCH 84/93] [#757] Group tests by python version --- .travis.yml | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 76214d83..c80e74f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,49 +43,55 @@ jobs: - os: windows include: - - stage: Test + # Python 3.6 Tests - name: Python 3.6 on Linux python: 3.6 - - name: Python 3.7 on Linux - python: 3.7 - - name: Python 3.7 on Linux, not UTC - python: 3.7 - env: - - TZ=America/Edmonton - - name: Python 3.8 on Linux - python: 3.8 - - name: Python nightly on Linux - python: nightly - - <<: *test_mac name: Python 3.6 on MacOS env: - JRNL_PYTHON_VERSION=3.6.8 - - <<: *test_mac - name: Python 3.7 on MacOS - env: - - JRNL_PYTHON_VERSION=3.7.4 - - <<: *test_mac - name: Python 3.8 on MacOS - env: - - JRNL_PYTHON_VERSION=3.8.0 - - <<: *test_windows name: Python 3.6 on Windows env: - JRNL_PYTHON_VERSION=3.6.8 - PATH=/c/Python36:/c/Python36/Scripts:$PATH + + # Python 3.7 Tests + - name: Python 3.7 on Linux + python: 3.7 + - <<: *test_mac + name: Python 3.7 on MacOS + env: + - JRNL_PYTHON_VERSION=3.7.4 - <<: *test_windows name: Python 3.7 on Windows env: - JRNL_PYTHON_VERSION=3.7.5 - PATH=/c/Python37:/c/Python37/Scripts:$PATH + + # Python 3.8 Tests + - name: Python 3.8 on Linux + python: 3.8 + - <<: *test_mac + name: Python 3.8 on MacOS + env: + - JRNL_PYTHON_VERSION=3.8.0 - <<: *test_windows name: Python 3.8 on Windows env: - JRNL_PYTHON_VERSION=3.8.0 - PATH=/c/Python38:/c/Python38/Scripts:$PATH + # ... and beyond! + - name: Python nightly on Linux + python: nightly + + # Specialty tests + - name: Python 3.7 on Linux, not UTC + python: 3.7 + env: + - TZ=America/Edmonton + - stage: Deploy if: branch = master AND tag IS present before_deploy: From 174632f8e38ba98b54ffc0c080b580d9976c094a Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 16:23:52 -0800 Subject: [PATCH 85/93] [#757] Be explicit about python versio so travis doesn't get confused --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index c80e74f2..9b010ffc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,10 +48,12 @@ jobs: python: 3.6 - <<: *test_mac name: Python 3.6 on MacOS + python: 3.6 env: - JRNL_PYTHON_VERSION=3.6.8 - <<: *test_windows name: Python 3.6 on Windows + python: 3.6 env: - JRNL_PYTHON_VERSION=3.6.8 - PATH=/c/Python36:/c/Python36/Scripts:$PATH @@ -61,10 +63,12 @@ jobs: python: 3.7 - <<: *test_mac name: Python 3.7 on MacOS + python: 3.7 env: - JRNL_PYTHON_VERSION=3.7.4 - <<: *test_windows name: Python 3.7 on Windows + python: 3.7 env: - JRNL_PYTHON_VERSION=3.7.5 - PATH=/c/Python37:/c/Python37/Scripts:$PATH @@ -74,10 +78,12 @@ jobs: python: 3.8 - <<: *test_mac name: Python 3.8 on MacOS + python: 3.8 env: - JRNL_PYTHON_VERSION=3.8.0 - <<: *test_windows name: Python 3.8 on Windows + python: 3.8 env: - JRNL_PYTHON_VERSION=3.8.0 - PATH=/c/Python38:/c/Python38/Scripts:$PATH From eb6c5200dedcc4a2fe9c1e6a5396b4bd4ff471a4 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 22:02:57 -0800 Subject: [PATCH 86/93] [#757] update caching for ci --- .travis.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9b010ffc..7b4786fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,9 @@ dist: xenial # required for Python >= 3.7 os: linux language: python -cache: pip + +cache: + - pip git: depth: false @@ -22,6 +24,9 @@ aliases: os: osx language: shell osx_image: xcode11.2 + cache: + directories: + - $HOME/.pyenv/versions before_install: - eval "$(pyenv init -)" - pyenv install -s $JRNL_PYTHON_VERSION @@ -31,6 +36,11 @@ aliases: test_windows: &test_windows os: windows language: shell + cache: + directories: + - /c/Python36 + - /c/Python37 + - /c/Python38 before_install: - choco install python --version $JRNL_PYTHON_VERSION - python -m pip install --upgrade pip From e424e32a37f89cd72327a73bd4173a50a14450c4 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 22:03:40 -0800 Subject: [PATCH 87/93] [#757] Add fast finish option to travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7b4786fb..949391bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,6 +47,7 @@ aliases: - pip --version jobs: + fast_finish: true allow_failures: - python: 3.8 - python: nightly From a5b5f4732a0c967ed4a7ec5ef664a293700cd181 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Fri, 29 Nov 2019 22:04:02 -0800 Subject: [PATCH 88/93] [#757] update mac python version test to match others --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 949391bd..140349c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,7 @@ jobs: name: Python 3.7 on MacOS python: 3.7 env: - - JRNL_PYTHON_VERSION=3.7.4 + - JRNL_PYTHON_VERSION=3.7.5 - <<: *test_windows name: Python 3.7 on Windows python: 3.7 From 562454427e0ad86d21ccb931f15271d4ea1a39da Mon Sep 17 00:00:00 2001 From: Jrnl Bot Date: Sat, 30 Nov 2019 20:32:11 +0000 Subject: [PATCH 89/93] Incrementing version to v2.1.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index cf6111fd..9d540c95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "jrnl" -version = "v2.1.1-beta" +version = "v2.1.1" description = "Collect your thoughts and notes without leaving the command line." authors = [ "Manuel Ebert ", From 7200b1fa778e0f83ebc584e13edd336a3e61b4a3 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Sat, 30 Nov 2019 13:35:59 -0800 Subject: [PATCH 90/93] [#739][#747] update lock file --- poetry.lock | 132 +++++++++++++++++++++++++++------------------------- 1 file changed, 68 insertions(+), 64 deletions(-) diff --git a/poetry.lock b/poetry.lock index ea35de41..8a698d84 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,25 +6,13 @@ optional = false python-versions = "*" version = "1.4.3" -[[package]] -category = "main" -description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" -name = "asn1crypto" -optional = false -python-versions = "*" -version = "0.24.0" - [[package]] category = "main" description = "Safe, minimalistic evaluator of python expression using ast module" name = "asteval" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.9.14" - -[package.dependencies] -numpy = "*" -six = "*" +version = "0.9.17" [[package]] category = "dev" @@ -32,7 +20,7 @@ description = "Classes Without Boilerplate" name = "attrs" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.1.0" +version = "19.3.0" [[package]] category = "dev" @@ -67,7 +55,7 @@ description = "Foreign Function Interface for Python calling C code." name = "cffi" optional = false python-versions = "*" -version = "1.12.3" +version = "1.13.2" [package.dependencies] pycparser = "*" @@ -95,15 +83,14 @@ description = "cryptography is a package which provides cryptographic recipes an name = "cryptography" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "2.7" +version = "2.8" [package.dependencies] -asn1crypto = ">=0.21.0" cffi = ">=1.8,<1.11.3 || >1.11.3" six = ">=1.4.1" [[package]] -category = "main" +category = "dev" description = "Discover and load entry points from installed packages." name = "entrypoints" optional = false @@ -116,7 +103,7 @@ description = "the modular source code checker: pep8, pyflakes and co" name = "flake8" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.7.8" +version = "3.7.9" [package.dependencies] entrypoints = ">=0.3.0,<0.4.0" @@ -126,11 +113,15 @@ pyflakes = ">=2.1.0,<2.2.0" [[package]] category = "main" -description = "Clean single-source support for Python 3 and 2" -name = "future" +description = "Read metadata from Python packages" +marker = "python_version < \"3.8\"" +name = "importlib-metadata" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "0.17.1" +python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" +version = "1.0.0" + +[package.dependencies] +zipp = ">=0.5" [[package]] category = "main" @@ -139,15 +130,15 @@ marker = "sys_platform == \"linux\"" name = "jeepney" optional = false python-versions = ">=3.5" -version = "0.4" +version = "0.4.1" [[package]] category = "dev" -description = "A small but fast and easy to use stand-alone template engine written in pure python." +description = "A very fast and expressive template engine." name = "jinja2" optional = false python-versions = "*" -version = "2.10.1" +version = "2.10.3" [package.dependencies] MarkupSafe = ">=0.23" @@ -158,13 +149,16 @@ description = "Store and access your passwords safely." name = "keyring" optional = false python-versions = ">=3.5" -version = "19.0.2" +version = "19.3.0" [package.dependencies] -entrypoints = "*" pywin32-ctypes = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1" secretstorage = "*" +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + [[package]] category = "dev" description = "Python LiveReload is an awesome tool for web developers" @@ -222,11 +216,12 @@ tornado = ">=5.0" [[package]] category = "main" -description = "NumPy is the fundamental package for array computing with Python." -name = "numpy" +description = "More routines for operating on iterables, beyond itertools" +marker = "python_version < \"3.8\"" +name = "more-itertools" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.16.4" +python-versions = ">=3.5" +version = "8.0.0" [[package]] category = "dev" @@ -234,7 +229,7 @@ description = "parse() is the opposite of format()" name = "parse" optional = false python-versions = "*" -version = "1.12.0" +version = "1.12.1" [[package]] category = "dev" @@ -242,10 +237,10 @@ description = "Simplifies to build parse types based on the parse module" name = "parse-type" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "0.4.2" +version = "0.5.2" [package.dependencies] -parse = ">=1.8" +parse = ">=1.8.4" six = ">=1.11" [[package]] @@ -254,10 +249,7 @@ description = "Parse human-readable date/time text." name = "parsedatetime" optional = false python-versions = "*" -version = "2.4" - -[package.dependencies] -future = "*" +version = "2.5" [[package]] category = "main" @@ -265,7 +257,7 @@ description = "comprehensive password hashing framework supporting over 30 schem name = "passlib" optional = false python-versions = "*" -version = "1.7.1" +version = "1.7.2" [[package]] category = "dev" @@ -296,8 +288,8 @@ category = "main" description = "Extensions to the standard Python datetime module" name = "python-dateutil" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.8.0" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +version = "2.8.1" [package.dependencies] six = ">=1.5" @@ -308,7 +300,7 @@ description = "World timezone definitions, modern and historical" name = "pytz" optional = false python-versions = "*" -version = "2019.1" +version = "2019.3" [[package]] category = "main" @@ -354,7 +346,7 @@ description = "Python 2 and 3 compatibility utilities" name = "six" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "1.12.0" +version = "1.13.0" [[package]] category = "dev" @@ -383,47 +375,59 @@ version = "1.5.1" [package.dependencies] pytz = "*" +[[package]] +category = "main" +description = "Backport of pathlib-compatible object wrapper for zip files" +marker = "python_version < \"3.8\"" +name = "zipp" +optional = false +python-versions = ">=2.7" +version = "0.6.0" + +[package.dependencies] +more-itertools = "*" + [metadata] -content-hash = "9896cf59c7552b6ad95219ee5555c7445a3fab39c2e4f4c6f3d991a36635e44b" -python-versions = ">=3.6.0, <3.8.0" +content-hash = "f84f27e8b11b6f6a749ae558b693d013c09c086f64a122a1f239eb206370219b" +python-versions = ">=3.6.0, <3.9.0" [metadata.hashes] appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] -asn1crypto = ["2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87", "9d5c20441baf0cb60a4ac34cc447c6c189024b6b4c6cd7877034f4965c464e49"] -asteval = ["7c81fee6707a7a28e8beae891b858535a7e61f9ce275a0a4cf5f428fbc934cb8"] -attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"] +asteval = ["3e291b2cb71284bc55816fe7ff65ee15e47fa66df98490cdd95f5531fc37b81e"] +attrs = ["08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", "f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"] behave = ["b9662327aa53294c1351b0a9c369093ccec1d21026f050c3bd9b3e5cccf81a86", "ebda1a6c9e5bfe95c5f9f0a2794e01c7098b3dde86c10a95d8621c5907ff6f1c"] black = ["817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", "e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"] -cffi = ["041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774", "046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d", "066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90", "066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b", "2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63", "300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45", "34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25", "46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3", "4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b", "4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647", "4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016", "50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4", "55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb", "5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753", "59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7", "73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9", "a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f", "a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8", "a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f", "a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc", "ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42", "b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3", "d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909", "d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45", "dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d", "e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512", "e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff", "ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201"] +cffi = ["0b49274afc941c626b605fb59b59c3485c17dc776dc3cc7cc14aca74cc19cc42", "0e3ea92942cb1168e38c05c1d56b0527ce31f1a370f6117f1d490b8dcd6b3a04", "135f69aecbf4517d5b3d6429207b2dff49c876be724ac0c8bf8e1ea99df3d7e5", "19db0cdd6e516f13329cba4903368bff9bb5a9331d3410b1b448daaadc495e54", "2781e9ad0e9d47173c0093321bb5435a9dfae0ed6a762aabafa13108f5f7b2ba", "291f7c42e21d72144bb1c1b2e825ec60f46d0a7468f5346841860454c7aa8f57", "2c5e309ec482556397cb21ede0350c5e82f0eb2621de04b2633588d118da4396", "2e9c80a8c3344a92cb04661115898a9129c074f7ab82011ef4b612f645939f12", "32a262e2b90ffcfdd97c7a5e24a6012a43c61f1f5a57789ad80af1d26c6acd97", "3c9fff570f13480b201e9ab69453108f6d98244a7f495e91b6c654a47486ba43", "415bdc7ca8c1c634a6d7163d43fb0ea885a07e9618a64bda407e04b04333b7db", "42194f54c11abc8583417a7cf4eaff544ce0de8187abaf5d29029c91b1725ad3", "4424e42199e86b21fc4db83bd76909a6fc2a2aefb352cb5414833c030f6ed71b", "4a43c91840bda5f55249413037b7a9b79c90b1184ed504883b72c4df70778579", "599a1e8ff057ac530c9ad1778293c665cb81a791421f46922d80a86473c13346", "5c4fae4e9cdd18c82ba3a134be256e98dc0596af1e7285a3d2602c97dcfa5159", "5ecfa867dea6fabe2a58f03ac9186ea64da1386af2159196da51c4904e11d652", "62f2578358d3a92e4ab2d830cd1c2049c9c0d0e6d3c58322993cc341bdeac22e", "6471a82d5abea994e38d2c2abc77164b4f7fbaaf80261cb98394d5793f11b12a", "6d4f18483d040e18546108eb13b1dfa1000a089bcf8529e30346116ea6240506", "71a608532ab3bd26223c8d841dde43f3516aa5d2bf37b50ac410bb5e99053e8f", "74a1d8c85fb6ff0b30fbfa8ad0ac23cd601a138f7509dc617ebc65ef305bb98d", "7b93a885bb13073afb0aa73ad82059a4c41f4b7d8eb8368980448b52d4c7dc2c", "7d4751da932caaec419d514eaa4215eaf14b612cff66398dd51129ac22680b20", "7f627141a26b551bdebbc4855c1157feeef18241b4b8366ed22a5c7d672ef858", "8169cf44dd8f9071b2b9248c35fc35e8677451c52f795daa2bb4643f32a540bc", "aa00d66c0fab27373ae44ae26a66a9e43ff2a678bf63a9c7c1a9a4d61172827a", "ccb032fda0873254380aa2bfad2582aedc2959186cce61e3a17abc1a55ff89c3", "d754f39e0d1603b5b24a7f8484b22d2904fa551fe865fd0d4c3332f078d20d4e", "d75c461e20e29afc0aee7172a0950157c704ff0dd51613506bd7d82b718e7410", "dcd65317dd15bc0451f3e01c80da2216a31916bdcffd6221ca1202d96584aa25", "e570d3ab32e2c2861c4ebe6ffcad6a8abf9347432a37608fe1fbd157b3f0036b", "fd43a88e045cf992ed09fa724b5315b790525f2676883a6ea64e3263bae6549d"] click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] -cryptography = ["24b61e5fcb506424d3ec4e18bca995833839bf13c59fc43e530e488f28d46b8c", "25dd1581a183e9e7a806fe0543f485103232f940fcfc301db65e630512cce643", "3452bba7c21c69f2df772762be0066c7ed5dc65df494a1d53a58b683a83e1216", "41a0be220dd1ed9e998f5891948306eb8c812b512dc398e5a01846d855050799", "5751d8a11b956fbfa314f6553d186b94aa70fdb03d8a4d4f1c82dcacf0cbe28a", "5f61c7d749048fa6e3322258b4263463bfccefecb0dd731b6561cb617a1d9bb9", "72e24c521fa2106f19623a3851e9f89ddfdeb9ac63871c7643790f872a305dfc", "7b97ae6ef5cba2e3bb14256625423413d5ce8d1abb91d4f29b6d1a081da765f8", "961e886d8a3590fd2c723cf07be14e2a91cf53c25f02435c04d39e90780e3b53", "96d8473848e984184b6728e2c9d391482008646276c3ff084a1bd89e15ff53a1", "ae536da50c7ad1e002c3eee101871d93abdc90d9c5f651818450a0d3af718609", "b0db0cecf396033abb4a93c95d1602f268b3a68bb0a9cc06a7cff587bb9a7292", "cfee9164954c186b191b91d4193989ca994703b2fff406f71cf454a2d3c7327e", "e6347742ac8f35ded4a46ff835c60e68c22a536a8ae5c4422966d06946b6d4c6", "f27d93f0139a3c056172ebb5d4f9056e770fdf0206c2f422ff2ebbad142e09ed", "f57b76e46a58b63d1c6375017f4564a28f19a5ca912691fd2e4261b3414b618d"] +cryptography = ["02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c", "1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595", "369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad", "3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651", "44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2", "4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff", "58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d", "6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42", "7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d", "73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e", "7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912", "90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793", "971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13", "a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7", "b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0", "b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879", "d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f", "de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9", "df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2", "ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf", "fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"] entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] -flake8 = ["19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", "8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"] -future = ["67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"] -jeepney = ["6089412a5de162c04747f0220f6b2223b8ba660acd041e52a76426ca550e3c70", "f6f8b1428403b4afad04b6b82f9ab9fc426c253d7504c9031c41712a2c01dc74"] -jinja2 = ["065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", "14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"] -keyring = ["1b74595f7439e4581a11d4f9a12790ac34addce64ca389c86272ff465f5e0b90", "afbfe7bc9bdba69d25c551b0c738adde533d87e0b51ad6bbe332cbea19ad8476"] +flake8 = ["45681a117ecc81e870cbf1262835ae4af5e7a8b08e40b944a8a6e6b895914cfb", "49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca"] +importlib-metadata = ["a82ca8c109e194d7d6aee3f7531b0470dd4dd6b36ec14fd55087142a96bd55a7", "f4a7ba72e93bc97ff491b66d69063819ae2b75238bb653cd4c95e3f2847ce76e"] +jeepney = ["13806f91a96e9b2623fd2a81b950d763ee471454aafd9eb6d75dbe7afce428fb", "f6a3f93464a0cf052f4e87da3c8b3ed1e27696758fb9739c63d3a74d9a1b6774"] +jinja2 = ["74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", "9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"] +keyring = ["9b80469783d3f6106bce1d389c6b8b20c8d4d739943b1b8cd0ddc2a45d065f9d", "ee3d35b7f1ac3cb69e9a1e4323534649d3ab2fea402738a77e4250c152970fed"] livereload = ["78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b", "89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66"] markdown = ["2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a", "56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"] markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] mkdocs = ["17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", "8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"] -numpy = ["0778076e764e146d3078b17c24c4d89e0ecd4ac5401beff8e1c87879043a0633", "141c7102f20abe6cf0d54c4ced8d565b86df4d3077ba2343b61a6db996cefec7", "14270a1ee8917d11e7753fb54fc7ffd1934f4d529235beec0b275e2ccf00333b", "27e11c7a8ec9d5838bc59f809bfa86efc8a4fd02e58960fa9c49d998e14332d5", "2a04dda79606f3d2f760384c38ccd3d5b9bb79d4c8126b67aff5eb09a253763e", "3c26010c1b51e1224a3ca6b8df807de6e95128b0908c7e34f190e7775455b0ca", "52c40f1a4262c896420c6ea1c6fda62cf67070e3947e3307f5562bd783a90336", "6e4f8d9e8aa79321657079b9ac03f3cf3fd067bf31c1cca4f56d49543f4356a5", "7242be12a58fec245ee9734e625964b97cf7e3f2f7d016603f9e56660ce479c7", "7dc253b542bfd4b4eb88d9dbae4ca079e7bf2e2afd819ee18891a43db66c60c7", "94f5bd885f67bbb25c82d80184abbf7ce4f6c3c3a41fbaa4182f034bba803e69", "a89e188daa119ffa0d03ce5123dee3f8ffd5115c896c2a9d4f0dbb3d8b95bfa3", "ad3399da9b0ca36e2f24de72f67ab2854a62e623274607e37e0ce5f5d5fa9166", "b0348be89275fd1d4c44ffa39530c41a21062f52299b1e3ee7d1c61f060044b8", "b5554368e4ede1856121b0dfa35ce71768102e4aa55e526cb8de7f374ff78722", "cbddc56b2502d3f87fda4f98d948eb5b11f36ff3902e17cb6cc44727f2200525", "d79f18f41751725c56eceab2a886f021d70fd70a6188fd386e29a045945ffc10", "dc2ca26a19ab32dc475dbad9dfe723d3a64c835f4c23f625c2b6566ca32b9f29", "dd9bcd4f294eb0633bb33d1a74febdd2b9018b8b8ed325f861fffcd2c7660bb8", "e8baab1bc7c9152715844f1faca6744f2416929de10d7639ed49555a85549f52", "ec31fe12668af687b99acf1567399632a7c47b0e17cfb9ae47c098644ef36797", "f12b4f7e2d8f9da3141564e6737d79016fe5336cc92de6814eba579744f65b0a", "f58ac38d5ca045a377b3b377c84df8175ab992c970a53332fa8ac2373df44ff7"] -parse = ["1b68657434d371e5156048ca4a0c5aea5afc6ca59a2fea4dd1a575354f617142"] -parse-type = ["6e906a66f340252e4c324914a60d417d33a4bea01292ea9bbf68b4fc123be8c9", "f596bdc75d3dd93036fbfe3d04127da9f6df0c26c36e01e76da85adef4336b3c"] -parsedatetime = ["3d817c58fb9570d1eec1dd46fa9448cd644eeed4fb612684b02dfda3a79cb84b", "9ee3529454bf35c40a77115f5a596771e59e1aee8c53306f346c461b8e913094"] -passlib = ["3d948f64138c25633613f303bcc471126eae67c04d5e3f6b7b8ce6242f8653e0", "43526aea08fa32c6b6dbbbe9963c4c767285b78147b7437597f992812f69d280"] +more-itertools = ["53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", "a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45"] +parse = ["a5fca7000c6588d77bc65c28f3f21bfce03b5e44daa8f9f07c17fe364990d717"] +parse-type = ["089a471b06327103865dfec2dd844230c3c658a4a1b5b4c8b6c16c8f77577f9e", "7f690b18d35048c15438d6d0571f9045cffbec5907e0b1ccf006f889e3a38c0b"] +parsedatetime = ["3b835fc54e472c17ef447be37458b400e3fefdf14bb1ffdedb5d2c853acf4ba1", "d2e9ddb1e463de871d32088a3f3cea3dc8282b1b2800e081bd0ef86900451667"] +passlib = ["68c35c98a7968850e17f1b6892720764cc7eed0ef2b7cb3116a89a28e43fe177", "8d666cef936198bc2ab47ee9b0410c94adf2ba798e5a84bf220be079ae7ab6a8"] pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"] pycparser = ["a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"] pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"] -python-dateutil = ["7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", "c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"] -pytz = ["303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda", "d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141"] +python-dateutil = ["73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"] +pytz = ["1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", "b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"] pywin32-ctypes = ["24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942", "9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"] pyxdg = ["1948ff8e2db02156c0cccd2529b43c0cff56ebaa71f5f021bbd755bc1419190e", "fe2928d3f532ed32b39c32a482b54136fe766d19936afc96c8f00645f9da1a06"] pyyaml = ["0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", "01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", "5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", "5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", "7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", "7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", "87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", "9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", "a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", "b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", "b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", "bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", "f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"] secretstorage = ["20c797ae48a4419f66f8d28fc221623f11fc45b6828f96bdb1ad9990acb59f92", "7a119fb52a88e398dbb22a4b3eb39b779bfbace7e4153b7bc6e5954d86282a8a"] -six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] +six = ["1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", "30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"] toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] tornado = ["349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c", "398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60", "4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281", "559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5", "abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7", "c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9", "c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5"] tzlocal = ["4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e"] +zipp = ["3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", "f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"] From 3717c26559c56a904df94f2793a4462a930b4885 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Sat, 30 Nov 2019 13:36:50 -0800 Subject: [PATCH 91/93] [#739] Update tests so that 3.8 is not an allowed failure --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 140349c3..17251ba9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,7 +49,6 @@ aliases: jobs: fast_finish: true allow_failures: - - python: 3.8 - python: nightly - os: windows From 9b6b788af459fa826b70a66bf750f2228784e32b Mon Sep 17 00:00:00 2001 From: Jims Date: Sun, 1 Dec 2019 23:03:38 -0500 Subject: [PATCH 92/93] Renamed searching to contains. Made changes as per pull-request: https://github.com/jrnl-org/jrnl/pull/740 --- features/contains.feature | 29 +++++++++++++++++++++++++++++ features/searching.feature | 11 ----------- jrnl/Journal.py | 6 ++++-- jrnl/cli.py | 6 +++--- 4 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 features/contains.feature delete mode 100644 features/searching.feature diff --git a/features/contains.feature b/features/contains.feature new file mode 100644 index 00000000..5813be20 --- /dev/null +++ b/features/contains.feature @@ -0,0 +1,29 @@ +Feature: Contains + + Scenario: Searching for a string + Given we use the config "basic.yaml" + When we run "jrnl -contains life" + Then we should get no error + and the output should be + """ + 2013-06-10 15:40 Life is good. + | But I'm better. + """ + + Scenario: Searching for a string within tag results + Given we use the config "tags.yaml" + When we run "jrnl @idea -contains software" + Then we should get no error + and the output should contain "software" + + Scenario: Searching for a string within AND tag results + Given we use the config "tags.yaml" + When we run "jrnl -and @journal @idea -contains software" + Then we should get no error + and the output should contain "software" + + Scenario: Searching for a string within NOT tag results + Given we use the config "tags.yaml" + When we run "jrnl -not @dan -contains software" + Then we should get no error + and the output should contain "software" diff --git a/features/searching.feature b/features/searching.feature deleted file mode 100644 index 52e1ba54..00000000 --- a/features/searching.feature +++ /dev/null @@ -1,11 +0,0 @@ -Feature: Searching - - Scenario: Searching for a string - Given we use the config "basic.yaml" - When we run "jrnl -S life" - Then we should get no error - and the output should be - """ - 2013-06-10 15:40 Life is good. - | But I'm better. - """ diff --git a/jrnl/Journal.py b/jrnl/Journal.py index ab52cd00..7360b385 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -187,7 +187,7 @@ class Journal: tag_counts = {(tags.count(tag), tag) for tag in tags} return [Tag(tag, count=count) for count, tag in sorted(tag_counts)] - def filter(self, tags=[], start_date=None, end_date=None, starred=False, strict=False, short=False, search_plain=None, exclude=[]): + def filter(self, tags=[], start_date=None, end_date=None, starred=False, strict=False, short=False, contains=None, exclude=[]): """Removes all entries from the journal that don't match the filter. tags is a list of tags, each being a string that starts with one of the @@ -209,6 +209,8 @@ class Journal: # If strict mode is on, all tags have to be present in entry tagged = self.search_tags.issubset if strict else self.search_tags.intersection excluded = lambda tags: len([tag for tag in tags if tag in excluded_tags]) > 0 + if contains: + contains_lower = contains.casefold() result = [ entry for entry in self.entries @@ -217,7 +219,7 @@ class Journal: and (not start_date or entry.date >= start_date) and (not end_date or entry.date <= end_date) and (not exclude or not excluded(entry.tags)) - and (not search_plain or (search_plain.lower() in entry.title.lower() or search_plain.lower() in entry.body.lower())) + and (not contains or (contains_lower in entry.title.casefold() or contains_lower in entry.body.casefold())) ] self.entries = result diff --git a/jrnl/cli.py b/jrnl/cli.py index ea8de891..b1454679 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -32,7 +32,7 @@ def parse_args(args=None): reading = parser.add_argument_group('Reading', 'Specifying either of these parameters will display posts of your journal') reading.add_argument('-from', dest='start_date', metavar="DATE", help='View entries after this date') reading.add_argument('-until', '-to', dest='end_date', metavar="DATE", help='View entries before this date') - reading.add_argument('-S', '-search', dest='search_plain', help='View entries containing a specific string') + reading.add_argument('-contains', dest='contains', help='View entries containing a specific string') reading.add_argument('-on', dest='on_date', metavar="DATE", help='View entries on this date') reading.add_argument('-and', dest='strict', action="store_true", help='Filter by tags using AND (default: OR)') reading.add_argument('-starred', dest='starred', action="store_true", help='Show only starred entries') @@ -65,7 +65,7 @@ def guess_mode(args, config): elif args.decrypt is not False or args.encrypt is not False or args.export is not False or any((args.short, args.tags, args.edit)): compose = False export = True - elif any((args.start_date, args.end_date, args.on_date, args.limit, args.strict, args.starred, args.search_plain)): + elif any((args.start_date, args.end_date, args.on_date, args.limit, args.strict, args.starred, args.contains)): # Any sign of displaying stuff? compose = False elif args.text and all(word[0] in config['tagsymbols'] for word in " ".join(args.text).split()): @@ -238,7 +238,7 @@ def run(manual_args=None): short=args.short, starred=args.starred, exclude=args.excluded, - search_plain=args.search_plain) + contains=args.contains) journal.limit(args.limit) # Reading mode From 8407ec5fb007117884f9bd3fb9653634d65defd0 Mon Sep 17 00:00:00 2001 From: Jesper Kjeldgaard Date: Tue, 3 Dec 2019 11:59:05 +0100 Subject: [PATCH 93/93] Fix merge conflict left-over --- docs/recipes.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/recipes.md b/docs/recipes.md index 04604e6d..08e51711 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -130,8 +130,6 @@ Similar to Sublime Text, MacVim must be started with a flag that tells the the process to wait until the file is closed before passing control back to journal. In the case of MacVim, this is `-f`: -<<<<<<< HEAD - ```yaml editor: "mvim -f" ```