From 739ccb3d16045a000af72843e9247feb84fc706e Mon Sep 17 00:00:00 2001 From: Casper Weiss Bang Date: Mon, 28 Oct 2019 20:51:57 +0100 Subject: [PATCH 01/22] Create templates for issues and pull requests --- .github/ISSUE_TEMPLATE.md | 35 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/Bug_report.md | 20 +++++++++++++ .github/ISSUE_TEMPLATE/Feature_request.md | 9 ++++++ .github/ISSUE_TEMPLATE/Support_request.md | 19 ++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 19 ++++++++++++ 5 files changed, 102 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/Bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/Feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/Support_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..8ca44464 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,35 @@ +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 new file mode 100644 index 00000000..38cf527e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -0,0 +1,20 @@ +## 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:** + - 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) + diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md new file mode 100644 index 00000000..f9357f42 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -0,0 +1,9 @@ +#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! + +* **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/Support_request.md b/.github/ISSUE_TEMPLATE/Support_request.md new file mode 100644 index 00000000..814d1f70 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Support_request.md @@ -0,0 +1,19 @@ +## 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?** + +* **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) + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..986f10ee --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ + +# **TEMPLATE PLEASE EDIT** +*Thank you for wanting to contribute! Please fill out this description as well as look at the checklist!* + +*Short block of text containing: +- Relevant changes in text form +- related issues +- Motivation (if applicable) +- Example of usage (if applicable) +- Example of changes to config files (if applicable) +* +### Checklist +- [ ] The code change is tested and works locally. +- [ ] Tests pass. Your PR cannot be merged unless tests pass +- [ ] There is no commented out code in this PR. +- [ ] Have you followed the guidelines in our Contributing document? +- [ ] Have you checked to ensure there aren't other open [Pull Requests](../pulls) for the same update/change? +- [ ] Have you added an explanation of what your changes do and why you'd like us to include them? +- [ ] Have you written new tests for your core changes, as applicable? From 34f8f858f14707646c0897e5577361eb80781a6a Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Thu, 31 Oct 2019 21:12:55 +0100 Subject: [PATCH 02/22] 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 03/22] 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 04/22] 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 05/22] 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 06/22] 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 07/22] 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 08/22] 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 09/22] 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 10/22] 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 11/22] 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 12/22] [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 13/22] [#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 14/22] 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 15/22] 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 16/22] 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 17/22] 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 18/22] 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 19/22] 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 20/22] 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 21/22] [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 22/22] 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