mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-21 05:28:31 +02:00
Merge branch 'develop' into change-cwd-bug
This commit is contained in:
commit
15ad02dc85
15 changed files with 261 additions and 35 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -43,6 +43,7 @@ obj
|
||||||
env/
|
env/
|
||||||
env*/
|
env*/
|
||||||
venv*/
|
venv*/
|
||||||
|
.venv*/
|
||||||
|
|
||||||
# PyCharm Project files
|
# PyCharm Project files
|
||||||
.idea/
|
.idea/
|
||||||
|
|
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -1,5 +1,24 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [v2.4](https://pypi.org/project/jrnl/v2.4/) (2020-04-25)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.4-beta...v2.4)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Upgrade license to GPLv3 [\#918](https://github.com/jrnl-org/jrnl/pull/918) ([wren](https://github.com/wren))
|
||||||
|
|
||||||
|
**Build:**
|
||||||
|
|
||||||
|
- Update makefile to match pipeline better [\#919](https://github.com/jrnl-org/jrnl/pull/919) ([wren](https://github.com/wren))
|
||||||
|
|
||||||
|
**Updated documentation:**
|
||||||
|
|
||||||
|
- Clean up readme file [\#924](https://github.com/jrnl-org/jrnl/pull/924) ([wren](https://github.com/wren))
|
||||||
|
- Clarify that editing config isn't always destructive [\#923](https://github.com/jrnl-org/jrnl/pull/923) ([Epskampie](https://github.com/Epskampie))
|
||||||
|
|
||||||
|
# Changelog
|
||||||
|
|
||||||
## [Unreleased](https://github.com/jrnl-org/jrnl/)
|
## [Unreleased](https://github.com/jrnl-org/jrnl/)
|
||||||
|
|
||||||
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.4.2...HEAD)
|
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.4.2...HEAD)
|
||||||
|
|
|
@ -1,27 +1,41 @@
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
## What is jrnl?
|
## Features
|
||||||
|
|
||||||
`jrnl` is a simple journal application for
|
### Command-Line Interface
|
||||||
your command line. Journals are stored as human readable plain text
|
|
||||||
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.
|
|
||||||
|
|
||||||
Optionally, your journal can be encrypted using the [256-bit
|
`jrnl` is a simple but powerful plain text journal application for the command
|
||||||
AES](http://en.wikipedia.org/wiki/Advanced_Encryption_Standard).
|
line. Everything happens on the command line.
|
||||||
|
|
||||||
## Why keep a journal?
|
### Text-Based
|
||||||
|
|
||||||
Journals aren't just for people who have too much
|
`jrnl` stores your journals as human-readable, future-proof plain text files.
|
||||||
time on their summer vacation. A journal helps you to keep track of the
|
You can store them wherever you want, including in shared folders to keep them
|
||||||
things you get done and how you did them. Your imagination may be
|
synchronized between devices. And because journal files are stored as plain
|
||||||
limitless, but your memory isn't.
|
text, you can rest assured that your journals will be readable for centuries.
|
||||||
|
|
||||||
For personal use, make it a good habit to write at least 20 words a day.
|
### Support for Multiple Journals
|
||||||
Just to reflect what made this day special, why you haven't wasted it.
|
|
||||||
|
|
||||||
For professional use, consider a text-based journal to be the perfect
|
`jrnl` allows you to work with multiple journals, each of which is stored as a
|
||||||
complement to your GTD todo list - a documentation of what and how
|
single file using date and time tags to identify individual entries. `jrnl`
|
||||||
you've done it. Or use it as a quick way to keep a change log. Or use it
|
makes it easy to find the entries you want, and only the ones you want, so that
|
||||||
to keep a lab book.
|
you can read them or edit them.
|
||||||
|
|
||||||
|
### Support for External Editors
|
||||||
|
|
||||||
|
`jrnl` allows you to search for specific entries and edit them in your favorite
|
||||||
|
text editor.
|
||||||
|
|
||||||
|
### Encryption
|
||||||
|
|
||||||
|
`jrnl` includes support for [256-bit AES
|
||||||
|
encryption](http://en.wikipedia.org/wiki/Advanced_Encryption_Standard) using
|
||||||
|
[cryptography.io](https://cryptography.io).
|
||||||
|
|
||||||
|
### Multi-Platform Support
|
||||||
|
|
||||||
|
`jrnl` is compatible with most operating systems. Pre-compiled binaries are available through several distribution channels, and you can build from source. See the installation page for more information.
|
||||||
|
|
||||||
|
### Open-Source
|
||||||
|
|
||||||
|
`jrnl` is written in [Python](https://www.python.org) and maintained by a [friendly community](https://github.com/jrnl-org/jrnl) of open-source software enthusiasts.
|
||||||
|
|
|
@ -114,6 +114,12 @@ Feature: Basic reading and writing to a journal
|
||||||
"""
|
"""
|
||||||
And we should get no error
|
And we should get no error
|
||||||
|
|
||||||
|
Scenario: Journal directory does not exist
|
||||||
|
Given we use the config "missing_directory.yaml"
|
||||||
|
When we run "jrnl Life is good"
|
||||||
|
and we run "jrnl -n 1"
|
||||||
|
Then the output should contain "Life is good"
|
||||||
|
|
||||||
Scenario: Installation with relative journal and referencing from another folder
|
Scenario: Installation with relative journal and referencing from another folder
|
||||||
Given we use the config "missingconfig"
|
Given we use the config "missingconfig"
|
||||||
When we run "jrnl hello world" and enter
|
When we run "jrnl hello world" and enter
|
||||||
|
|
|
@ -63,3 +63,15 @@ Feature: Dayone specific implementation details.
|
||||||
Then we should get no error
|
Then we should get no error
|
||||||
and the output should be parsable as json
|
and the output should be parsable as json
|
||||||
and the json output should contain entries.0.uuid = "4BB1F46946AD439996C9B59DE7C4DDC1"
|
and the json output should contain entries.0.uuid = "4BB1F46946AD439996C9B59DE7C4DDC1"
|
||||||
|
|
||||||
|
Scenario: Writing into Dayone adds extended metadata
|
||||||
|
Given we use the config "dayone.yaml"
|
||||||
|
When we run "jrnl 01 may 1979: Being born hurts."
|
||||||
|
and we run "jrnl --export json"
|
||||||
|
Then "entries" in the json output should have 5 elements
|
||||||
|
and the json output should contain entries.0.creator.software_agent
|
||||||
|
and the json output should contain entries.0.creator.os_agent
|
||||||
|
and the json output should contain entries.0.creator.host_name
|
||||||
|
and the json output should contain entries.0.creator.generation_date
|
||||||
|
and the json output should contain entries.0.creator.device_agent
|
||||||
|
and "entries.0.creator.software_agent" in the json output should contain "jrnl"
|
||||||
|
|
|
@ -55,3 +55,27 @@
|
||||||
Then the config for journal "simple" should have "encrypt" set to "bool:True"
|
Then the config for journal "simple" should have "encrypt" set to "bool:True"
|
||||||
When we run "jrnl simple -n 1"
|
When we run "jrnl simple -n 1"
|
||||||
Then the output should contain "2013-06-10 15:40 Life is good"
|
Then the output should contain "2013-06-10 15:40 Life is good"
|
||||||
|
|
||||||
|
Scenario: Encrypt journal with no keyring backend and do not store in keyring
|
||||||
|
Given we use the config "basic.yaml"
|
||||||
|
When we disable the keychain
|
||||||
|
and we run "jrnl test entry"
|
||||||
|
and we run "jrnl --encrypt" and enter
|
||||||
|
"""
|
||||||
|
password
|
||||||
|
password
|
||||||
|
n
|
||||||
|
"""
|
||||||
|
Then we should get no error
|
||||||
|
|
||||||
|
Scenario: Encrypt journal with no keyring backend and do store in keyring
|
||||||
|
Given we use the config "basic.yaml"
|
||||||
|
When we disable the keychain
|
||||||
|
and we run "jrnl test entry"
|
||||||
|
and we run "jrnl --encrypt" and enter
|
||||||
|
"""
|
||||||
|
password
|
||||||
|
password
|
||||||
|
y
|
||||||
|
"""
|
||||||
|
Then we should get no error
|
||||||
|
|
|
@ -41,6 +41,22 @@ class TestKeyring(keyring.backend.KeyringBackend):
|
||||||
self.keys[servicename][username] = None
|
self.keys[servicename][username] = None
|
||||||
|
|
||||||
|
|
||||||
|
class NoKeyring(keyring.backend.KeyringBackend):
|
||||||
|
"""A keyring that simulated an environment with no keyring backend."""
|
||||||
|
|
||||||
|
priority = 2
|
||||||
|
keys = defaultdict(dict)
|
||||||
|
|
||||||
|
def set_password(self, servicename, username, password):
|
||||||
|
raise keyring.errors.NoKeyringError
|
||||||
|
|
||||||
|
def get_password(self, servicename, username):
|
||||||
|
raise keyring.errors.NoKeyringError
|
||||||
|
|
||||||
|
def delete_password(self, servicename, username):
|
||||||
|
raise keyring.errors.NoKeyringError
|
||||||
|
|
||||||
|
|
||||||
# set the keyring for keyring lib
|
# set the keyring for keyring lib
|
||||||
keyring.set_keyring(TestKeyring())
|
keyring.set_keyring(TestKeyring())
|
||||||
|
|
||||||
|
@ -213,6 +229,11 @@ def set_keychain(context, journal, password):
|
||||||
keyring.set_password("jrnl", journal, password)
|
keyring.set_password("jrnl", journal, password)
|
||||||
|
|
||||||
|
|
||||||
|
@when("we disable the keychain")
|
||||||
|
def disable_keychain(context):
|
||||||
|
keyring.core.set_keyring(NoKeyring())
|
||||||
|
|
||||||
|
|
||||||
@then("we should get an error")
|
@then("we should get an error")
|
||||||
def has_error(context):
|
def has_error(context):
|
||||||
assert context.exit_status != 0, context.exit_status
|
assert context.exit_status != 0, context.exit_status
|
||||||
|
|
|
@ -32,13 +32,21 @@ def check_output_field_not_key(context, field, key):
|
||||||
@then('"{field}" in the json output should contain "{key}"')
|
@then('"{field}" in the json output should contain "{key}"')
|
||||||
def check_output_field_key(context, field, key):
|
def check_output_field_key(context, field, key):
|
||||||
out = context.stdout_capture.getvalue()
|
out = context.stdout_capture.getvalue()
|
||||||
out_json = json.loads(out)
|
struct = json.loads(out)
|
||||||
assert field in out_json
|
|
||||||
assert key in out_json[field]
|
for node in field.split("."):
|
||||||
|
try:
|
||||||
|
struct = struct[int(node)]
|
||||||
|
except ValueError:
|
||||||
|
assert node in struct
|
||||||
|
struct = struct[node]
|
||||||
|
|
||||||
|
assert key in struct
|
||||||
|
|
||||||
|
|
||||||
|
@then("the json output should contain {path}")
|
||||||
@then('the json output should contain {path} = "{value}"')
|
@then('the json output should contain {path} = "{value}"')
|
||||||
def check_json_output_path(context, path, value):
|
def check_json_output_path(context, path, value=None):
|
||||||
""" E.g.
|
""" E.g.
|
||||||
the json output should contain entries.0.title = "hello"
|
the json output should contain entries.0.title = "hello"
|
||||||
"""
|
"""
|
||||||
|
@ -50,7 +58,11 @@ def check_json_output_path(context, path, value):
|
||||||
struct = struct[int(node)]
|
struct = struct[int(node)]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
struct = struct[node]
|
struct = struct[node]
|
||||||
assert struct == value, struct
|
|
||||||
|
if value is not None:
|
||||||
|
assert struct == value, struct
|
||||||
|
else:
|
||||||
|
assert struct is not None
|
||||||
|
|
||||||
|
|
||||||
@then("the output should be a valid XML string")
|
@then("the output should be a valid XML string")
|
||||||
|
|
|
@ -9,11 +9,13 @@ import re
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from xml.parsers.expat import ExpatError
|
from xml.parsers.expat import ExpatError
|
||||||
|
import socket
|
||||||
|
import platform
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
import tzlocal
|
import tzlocal
|
||||||
|
|
||||||
from . import Entry, Journal
|
from . import __title__, __version__, Entry, Journal
|
||||||
from . import time as jrnl_time
|
from . import time as jrnl_time
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,6 +73,41 @@ class DayOne(Journal.Journal):
|
||||||
for tag in dict_entry.get("Tags", [])
|
for tag in dict_entry.get("Tags", [])
|
||||||
]
|
]
|
||||||
|
|
||||||
|
"""Extended DayOne attributes"""
|
||||||
|
try:
|
||||||
|
entry.creator_device_agent = dict_entry["Creator"][
|
||||||
|
"Device Agent"
|
||||||
|
]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
entry.creator_generation_date = dict_entry["Creator"][
|
||||||
|
"Generation Date"
|
||||||
|
]
|
||||||
|
except:
|
||||||
|
entry.creator_generation_date = date
|
||||||
|
try:
|
||||||
|
entry.creator_host_name = dict_entry["Creator"]["Host Name"]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
entry.creator_os_agent = dict_entry["Creator"]["OS Agent"]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
entry.creator_software_agent = dict_entry["Creator"][
|
||||||
|
"Software Agent"
|
||||||
|
]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
entry.location = dict_entry["Location"]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
entry.weather = dict_entry["Weather"]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
self.entries.append(entry)
|
self.entries.append(entry)
|
||||||
self.sort()
|
self.sort()
|
||||||
return self
|
return self
|
||||||
|
@ -85,6 +122,20 @@ class DayOne(Journal.Journal):
|
||||||
|
|
||||||
if not hasattr(entry, "uuid"):
|
if not hasattr(entry, "uuid"):
|
||||||
entry.uuid = uuid.uuid1().hex
|
entry.uuid = uuid.uuid1().hex
|
||||||
|
if not hasattr(entry, "creator_device_agent"):
|
||||||
|
entry.creator_device_agent = "" # iPhone/iPhone5,3
|
||||||
|
if not hasattr(entry, "creator_generation_date"):
|
||||||
|
entry.creator_generation_date = utc_time
|
||||||
|
if not hasattr(entry, "creator_host_name"):
|
||||||
|
entry.creator_host_name = socket.gethostname()
|
||||||
|
if not hasattr(entry, "creator_os_agent"):
|
||||||
|
entry.creator_os_agent = "{}/{}".format(
|
||||||
|
platform.system(), platform.release()
|
||||||
|
)
|
||||||
|
if not hasattr(entry, "creator_software_agent"):
|
||||||
|
entry.creator_software_agent = "{}/{}".format(
|
||||||
|
__title__, __version__
|
||||||
|
)
|
||||||
|
|
||||||
fn = (
|
fn = (
|
||||||
Path(self.config["journal"])
|
Path(self.config["journal"])
|
||||||
|
@ -102,10 +153,23 @@ class DayOne(Journal.Journal):
|
||||||
tag.strip(self.config["tagsymbols"]).replace("_", " ")
|
tag.strip(self.config["tagsymbols"]).replace("_", " ")
|
||||||
for tag in entry.tags
|
for tag in entry.tags
|
||||||
],
|
],
|
||||||
|
"Creator": {
|
||||||
|
"Device Agent": entry.creator_device_agent,
|
||||||
|
"Generation Date": entry.creator_generation_date,
|
||||||
|
"Host Name": entry.creator_host_name,
|
||||||
|
"OS Agent": entry.creator_os_agent,
|
||||||
|
"Software Agent": entry.creator_software_agent,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
if hasattr(entry, "location"):
|
||||||
|
entry_plist["Location"] = entry.location
|
||||||
|
if hasattr(entry, "weather"):
|
||||||
|
entry_plist["Weather"] = entry.weather
|
||||||
|
|
||||||
# plistlib expects a binary object
|
# plistlib expects a binary object
|
||||||
with fn.open(mode="wb") as f:
|
with fn.open(mode="wb") as f:
|
||||||
plistlib.dump(entry_plist, f, fmt=plistlib.FMT_XML, sort_keys=False)
|
plistlib.dump(entry_plist, f, fmt=plistlib.FMT_XML, sort_keys=False)
|
||||||
|
|
||||||
for entry in self._deleted_entries:
|
for entry in self._deleted_entries:
|
||||||
filename = os.path.join(
|
filename = os.path.join(
|
||||||
self.config["journal"], "entries", entry.uuid + ".doentry"
|
self.config["journal"], "entries", entry.uuid + ".doentry"
|
||||||
|
@ -147,7 +211,7 @@ class DayOne(Journal.Journal):
|
||||||
if line.endswith("*"):
|
if line.endswith("*"):
|
||||||
current_entry.starred = True
|
current_entry.starred = True
|
||||||
line = line[:-1]
|
line = line[:-1]
|
||||||
current_entry.title = line[len(date_blob) - 1 :]
|
current_entry.title = line[len(date_blob) - 1 :].strip()
|
||||||
current_entry.date = new_date
|
current_entry.date = new_date
|
||||||
elif current_entry:
|
elif current_entry:
|
||||||
current_entry.body += line + "\n"
|
current_entry.body += line + "\n"
|
||||||
|
@ -159,10 +223,33 @@ class DayOne(Journal.Journal):
|
||||||
# Now, update our current entries if they changed
|
# Now, update our current entries if they changed
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
entry._parse_text()
|
entry._parse_text()
|
||||||
matched_entries = [e for e in self.entries if e.uuid.lower() == entry.uuid]
|
matched_entries = [
|
||||||
|
e for e in self.entries if e.uuid.lower() == entry.uuid.lower()
|
||||||
|
]
|
||||||
|
# tags in entry body
|
||||||
if matched_entries:
|
if matched_entries:
|
||||||
# This entry is an existing entry
|
# This entry is an existing entry
|
||||||
match = matched_entries[0]
|
match = matched_entries[0]
|
||||||
|
|
||||||
|
# merge existing tags with tags pulled from the entry body
|
||||||
|
entry.tags = list(set(entry.tags + match.tags))
|
||||||
|
|
||||||
|
# extended Dayone metadata
|
||||||
|
if hasattr(match, "creator_device_agent"):
|
||||||
|
entry.creator_device_agent = match.creator_device_agent
|
||||||
|
if hasattr(match, "creator_generation_date"):
|
||||||
|
entry.creator_generation_date = match.creator_generation_date
|
||||||
|
if hasattr(match, "creator_host_name"):
|
||||||
|
entry.creator_host_name = match.creator_host_name
|
||||||
|
if hasattr(match, "creator_os_agent"):
|
||||||
|
entry.creator_os_agent = match.creator_os_agent
|
||||||
|
if hasattr(match, "creator_software_agent"):
|
||||||
|
entry.creator_software_agent = match.creator_software_agent
|
||||||
|
if hasattr(match, "location"):
|
||||||
|
entry.location = match.location
|
||||||
|
if hasattr(match, "weather"):
|
||||||
|
entry.weather = match.weather
|
||||||
|
|
||||||
if match != entry:
|
if match != entry:
|
||||||
self.entries.remove(match)
|
self.entries.remove(match)
|
||||||
entry.modified = True
|
entry.modified = True
|
||||||
|
|
|
@ -40,8 +40,11 @@ class EncryptedJournal(Journal):
|
||||||
"""Opens the journal file defined in the config and parses it into a list of Entries.
|
"""Opens the journal file defined in the config and parses it into a list of Entries.
|
||||||
Entries have the form (date, title, body)."""
|
Entries have the form (date, title, body)."""
|
||||||
filename = filename or self.config["journal"]
|
filename = filename or self.config["journal"]
|
||||||
|
dirname = os.path.dirname(filename)
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
|
if not os.path.isdir(dirname):
|
||||||
|
os.makedirs(dirname)
|
||||||
|
print(f"[Directory {dirname} created]", file=sys.stderr)
|
||||||
self.create_file(filename)
|
self.create_file(filename)
|
||||||
self.password = util.create_password(self.name)
|
self.password = util.create_password(self.name)
|
||||||
print(
|
print(
|
||||||
|
|
|
@ -74,8 +74,11 @@ class Journal:
|
||||||
"""Opens the journal file defined in the config and parses it into a list of Entries.
|
"""Opens the journal file defined in the config and parses it into a list of Entries.
|
||||||
Entries have the form (date, title, body)."""
|
Entries have the form (date, title, body)."""
|
||||||
filename = filename or self.config["journal"]
|
filename = filename or self.config["journal"]
|
||||||
|
dirname = os.path.dirname(filename)
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
|
if not os.path.isdir(dirname):
|
||||||
|
os.makedirs(dirname)
|
||||||
|
print(f"[Directory {dirname} created]", file=sys.stderr)
|
||||||
self.create_file(filename)
|
self.create_file(filename)
|
||||||
print(f"[Journal '{self.name}' created at {filename}]", file=sys.stderr)
|
print(f"[Journal '{self.name}' created at {filename}]", file=sys.stderr)
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "v2.4.2"
|
__version__ = "v2.4.3-beta"
|
||||||
|
|
|
@ -24,6 +24,27 @@ class JSONExporter(TextExporter):
|
||||||
}
|
}
|
||||||
if hasattr(entry, "uuid"):
|
if hasattr(entry, "uuid"):
|
||||||
entry_dict["uuid"] = entry.uuid
|
entry_dict["uuid"] = entry.uuid
|
||||||
|
if (
|
||||||
|
hasattr(entry, "creator_device_agent")
|
||||||
|
or hasattr(entry, "creator_generation_date")
|
||||||
|
or hasattr(entry, "creator_host_name")
|
||||||
|
or hasattr(entry, "creator_os_agent")
|
||||||
|
or hasattr(entry, "creator_software_agent")
|
||||||
|
):
|
||||||
|
entry_dict["creator"] = {}
|
||||||
|
if hasattr(entry, "creator_device_agent"):
|
||||||
|
entry_dict["creator"]["device_agent"] = entry.creator_device_agent
|
||||||
|
if hasattr(entry, "creator_generation_date"):
|
||||||
|
entry_dict["creator"]["generation_date"] = str(
|
||||||
|
entry.creator_generation_date
|
||||||
|
)
|
||||||
|
if hasattr(entry, "creator_host_name"):
|
||||||
|
entry_dict["creator"]["host_name"] = entry.creator_host_name
|
||||||
|
if hasattr(entry, "creator_os_agent"):
|
||||||
|
entry_dict["creator"]["os_agent"] = entry.creator_os_agent
|
||||||
|
if hasattr(entry, "creator_software_agent"):
|
||||||
|
entry_dict["creator"]["software_agent"] = entry.creator_software_agent
|
||||||
|
|
||||||
return entry_dict
|
return entry_dict
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
11
jrnl/util.py
11
jrnl/util.py
|
@ -59,9 +59,6 @@ def create_password(
|
||||||
|
|
||||||
if yesno("Do you want to store the password in your keychain?", default=True):
|
if yesno("Do you want to store the password in your keychain?", default=True):
|
||||||
set_keychain(journal_name, pw)
|
set_keychain(journal_name, pw)
|
||||||
else:
|
|
||||||
set_keychain(journal_name, None)
|
|
||||||
|
|
||||||
return pw
|
return pw
|
||||||
|
|
||||||
|
|
||||||
|
@ -107,7 +104,13 @@ def set_keychain(journal_name, password):
|
||||||
except keyring.errors.PasswordDeleteError:
|
except keyring.errors.PasswordDeleteError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
keyring.set_password("jrnl", journal_name, password)
|
try:
|
||||||
|
keyring.set_password("jrnl", journal_name, password)
|
||||||
|
except keyring.errors.NoKeyringError:
|
||||||
|
print(
|
||||||
|
"Keyring backend not found. Please install one of the supported backends by visiting: https://pypi.org/project/keyring/",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def yesno(prompt, default=True):
|
def yesno(prompt, default=True):
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "jrnl"
|
name = "jrnl"
|
||||||
version = "v2.4.2"
|
version = "v2.4.3-beta"
|
||||||
description = "Collect your thoughts and notes without leaving the command line."
|
description = "Collect your thoughts and notes without leaving the command line."
|
||||||
authors = [
|
authors = [
|
||||||
"Manuel Ebert <manuel@1450.me>",
|
"Manuel Ebert <manuel@1450.me>",
|
||||||
|
|
Loading…
Add table
Reference in a new issue