diff --git a/docs/usage.md b/docs/usage.md
index 18b35d68..deb612be 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -119,10 +119,10 @@ Will print all entries in which either `@pinkie` or `@WorldDomination`
occurred.
```sh
-jrnl -n 5 -and @pineapple @lubricant
+jrnl -n 5 -and @pinkie @WorldDomination
```
-the last five entries containing both `@pineapple` **and** `@lubricant`.
+the last five entries containing both `@pinkie` **and** `@worldDomination`.
You can change which symbols you'd like to use for tagging in the
configuration.
@@ -154,7 +154,7 @@ encrypt) your edited journal after you save and exit the editor.
You can also use this feature for deleting entries from your journal
```sh
-jrnl @girlfriend -until 'june 2012' --edit
+jrnl @texas -until 'june 2012' --edit
```
Just select all text, press delete, and everything is gone...
diff --git a/features/contains.feature b/features/contains.feature
index 5813be20..eac3a464 100644
--- a/features/contains.feature
+++ b/features/contains.feature
@@ -14,7 +14,7 @@ Feature: Contains
Given we use the config "tags.yaml"
When we run "jrnl @idea -contains software"
Then we should get no error
- and the output should contain "software"
+ And the output should contain "software"
Scenario: Searching for a string within AND tag results
Given we use the config "tags.yaml"
diff --git a/features/core.feature b/features/core.feature
index c023cd4c..df214494 100644
--- a/features/core.feature
+++ b/features/core.feature
@@ -13,6 +13,19 @@ Feature: Basic reading and writing to a journal
| But I'm better.
"""
+ Scenario: Printing a journal that has multiline entries
+ Given we use the config "multiline.yaml"
+ When we run "jrnl -n 1"
+ Then we should get no error
+ and the output should be
+ """
+ 2013-06-09 15:39 Multiple line entry.
+ | This is the first line.
+ | This line doesn't have any ending punctuation
+ |
+ | There is a blank line above this.
+ """
+
Scenario: Writing an entry from command line
Given we use the config "basic.yaml"
When we run "jrnl 23 july 2013: A cold and stormy day. I ate crisps on the sofa."
@@ -23,12 +36,12 @@ Feature: Basic reading and writing to a journal
@skip_win
Scenario: Writing an empty entry from the editor
Given we use the config "editor.yaml"
- When we open the editor and enter ""
+ When we open the editor and enter nothing
Then we should see the message "[Nothing saved to file]"
Scenario: Writing an empty entry from the command line
Given we use the config "basic.yaml"
- When we run "jrnl" and enter ""
+ When we run "jrnl" and enter nothing
Then the output should be
"""
@@ -41,6 +54,14 @@ Feature: Basic reading and writing to a journal
When we run "jrnl -on 'june 6 2013' --short"
Then the output should be "2013-06-10 15:40 Life is good."
+ Scenario: Emoji support
+ Given we use the config "basic.yaml"
+ When we run "jrnl 23 july 2013: 🌞 sunny day. Saw an 🐘"
+ Then we should see the message "Entry added"
+ When we run "jrnl -n 1"
+ Then the output should contain "🌞"
+ and the output should contain "🐘"
+
Scenario: Writing an entry at the prompt
Given we use the config "basic.yaml"
When we run "jrnl" and enter "25 jul 2013: I saw Elvis. He's alive."
@@ -64,3 +85,11 @@ Feature: Basic reading and writing to a journal
When we run "jrnl -on 2013-06-10 -s"
Then the output should be "2013-06-10 15:40 Life is good."
+ Scenario: Invalid color configuration
+ Given we use the config "invalid_color.yaml"
+ When we run "jrnl -on 2013-06-10 -s"
+ Then the output should be
+ """
+ 2013-06-10 15:40 Life is good.
+ """
+ And we should get no error
diff --git a/features/data/configs/basic.yaml b/features/data/configs/basic.yaml
index 9111b561..020bab18 100644
--- a/features/data/configs/basic.yaml
+++ b/features/data/configs/basic.yaml
@@ -10,3 +10,8 @@ tagsymbols: "@"
template: false
timeformat: "%Y-%m-%d %H:%M"
indent_character: "|"
+colors:
+ date: none
+ title: none
+ body: none
+ tags: none
diff --git a/features/data/configs/bug153.yaml b/features/data/configs/bug153.yaml
index 5bbcbf4a..ff645ab6 100644
--- a/features/data/configs/bug153.yaml
+++ b/features/data/configs/bug153.yaml
@@ -10,3 +10,8 @@ tagsymbols: '@'
template: false
timeformat: '%Y-%m-%d %H:%M'
indent_character: "|"
+colors:
+ date: none
+ title: none
+ body: none
+ tags: none
diff --git a/features/data/configs/bug780.yaml b/features/data/configs/bug780.yaml
new file mode 100644
index 00000000..e1d830c2
--- /dev/null
+++ b/features/data/configs/bug780.yaml
@@ -0,0 +1,12 @@
+default_hour: 9
+default_minute: 0
+editor: ''
+encrypt: false
+highlight: true
+journals:
+ default: features/journals/bug780.dayone
+linewrap: 80
+tagsymbols: '@'
+template: false
+timeformat: '%Y-%m-%d %H:%M'
+indent_character: "|"
diff --git a/features/data/configs/invalid_color.yaml b/features/data/configs/invalid_color.yaml
new file mode 100644
index 00000000..25c0e58d
--- /dev/null
+++ b/features/data/configs/invalid_color.yaml
@@ -0,0 +1,17 @@
+default_hour: 9
+default_minute: 0
+editor: ""
+encrypt: false
+highlight: true
+journals:
+ default: features/journals/simple.journal
+linewrap: 80
+tagsymbols: "@"
+template: false
+timeformat: "%Y-%m-%d %H:%M"
+indent_character: "|"
+colors:
+ date: not-a-color
+ title: also-not-a-color
+ body: still-no-color
+ tags: me-too
diff --git a/features/data/configs/markdown-headings-335.yaml b/features/data/configs/markdown-headings-335.yaml
index 5c33c53e..4368f641 100644
--- a/features/data/configs/markdown-headings-335.yaml
+++ b/features/data/configs/markdown-headings-335.yaml
@@ -10,3 +10,8 @@ linewrap: 80
tagsymbols: '@'
timeformat: '%Y-%m-%d %H:%M'
indent_character: "|"
+colors:
+ date: none
+ title: none
+ body: none
+ tags: none
diff --git a/features/data/configs/multiline-tags.yaml b/features/data/configs/multiline-tags.yaml
new file mode 100644
index 00000000..033aaa27
--- /dev/null
+++ b/features/data/configs/multiline-tags.yaml
@@ -0,0 +1,17 @@
+default_hour: 9
+default_minute: 0
+editor: ""
+encrypt: false
+highlight: true
+journals:
+ default: features/journals/multiline-tags.journal
+linewrap: 80
+tagsymbols: "@"
+template: false
+timeformat: "%Y-%m-%d %H:%M"
+indent_character: "|"
+colors:
+ date: none
+ title: none
+ body: none
+ tags: none
diff --git a/features/data/configs/multiline.yaml b/features/data/configs/multiline.yaml
new file mode 100644
index 00000000..aa35b3f5
--- /dev/null
+++ b/features/data/configs/multiline.yaml
@@ -0,0 +1,17 @@
+default_hour: 9
+default_minute: 0
+editor: ""
+encrypt: false
+highlight: true
+journals:
+ default: features/journals/multiline.journal
+linewrap: 80
+tagsymbols: "@"
+template: false
+timeformat: "%Y-%m-%d %H:%M"
+indent_character: "|"
+colors:
+ date: none
+ title: none
+ body: none
+ tags: none
diff --git a/features/data/configs/no_colors.yaml b/features/data/configs/no_colors.yaml
new file mode 100644
index 00000000..9111b561
--- /dev/null
+++ b/features/data/configs/no_colors.yaml
@@ -0,0 +1,12 @@
+default_hour: 9
+default_minute: 0
+editor: ""
+encrypt: false
+highlight: true
+journals:
+ default: features/journals/simple.journal
+linewrap: 80
+tagsymbols: "@"
+template: false
+timeformat: "%Y-%m-%d %H:%M"
+indent_character: "|"
diff --git a/features/data/configs/tags-216.yaml b/features/data/configs/tags-216.yaml
index b71abea5..81b3865f 100644
--- a/features/data/configs/tags-216.yaml
+++ b/features/data/configs/tags-216.yaml
@@ -10,3 +10,8 @@ linewrap: 80
tagsymbols: '@'
timeformat: '%Y-%m-%d %H:%M'
indent_character: "|"
+colors:
+ date: none
+ title: none
+ body: none
+ tags: none
diff --git a/features/data/configs/tags-237.yaml b/features/data/configs/tags-237.yaml
index a5a70d99..5aecd61e 100644
--- a/features/data/configs/tags-237.yaml
+++ b/features/data/configs/tags-237.yaml
@@ -10,3 +10,8 @@ linewrap: 80
tagsymbols: '@'
timeformat: '%Y-%m-%d %H:%M'
indent_character: "|"
+colors:
+ date: none
+ title: none
+ body: none
+ tags: none
diff --git a/features/data/configs/tags.yaml b/features/data/configs/tags.yaml
index 28ef5f69..4b55952c 100644
--- a/features/data/configs/tags.yaml
+++ b/features/data/configs/tags.yaml
@@ -10,3 +10,8 @@ linewrap: 80
tagsymbols: '@'
timeformat: '%Y-%m-%d %H:%M'
indent_character: "|"
+colors:
+ date: none
+ title: none
+ body: none
+ tags: none
diff --git a/features/data/configs/unreadabledates.yaml b/features/data/configs/unreadabledates.yaml
index 474e7aae..99304e5a 100644
--- a/features/data/configs/unreadabledates.yaml
+++ b/features/data/configs/unreadabledates.yaml
@@ -10,3 +10,8 @@ tagsymbols: "@"
template: false
timeformat: "%Y-%m-%d %H:%M"
indent_character: "|"
+colors:
+ date: none
+ title: none
+ body: none
+ tags: none
diff --git a/features/data/journals/bug780.dayone/entries/48A25033B34047C591160A4480197D8B.doentry b/features/data/journals/bug780.dayone/entries/48A25033B34047C591160A4480197D8B.doentry
new file mode 100644
index 00000000..426f1ea8
--- /dev/null
+++ b/features/data/journals/bug780.dayone/entries/48A25033B34047C591160A4480197D8B.doentry
@@ -0,0 +1,33 @@
+
+
+
+
+ Activity
+ Stationary
+ Creation Date
+ 2019-12-30T21:28:54Z
+ Entry Text
+
+ Starred
+
+ UUID
+ 48A25033B34047C591160A4480197D8B
+ Creator
+
+ Device Agent
+ PC
+ Generation Date
+ 2019-12-30T21:28:54Z
+ Host Name
+ LE-TREPORT
+ OS Agent
+ Microsoft Windows/10 Home
+ Software Agent
+ Journaley/2.1
+
+ Tags
+
+ i_have_no_body
+
+
+
diff --git a/features/data/journals/empty_folder/empty.txt b/features/data/journals/empty_folder/empty
similarity index 100%
rename from features/data/journals/empty_folder/empty.txt
rename to features/data/journals/empty_folder/empty
diff --git a/features/data/journals/multiline-tags.journal b/features/data/journals/multiline-tags.journal
new file mode 100644
index 00000000..1fb8706f
--- /dev/null
+++ b/features/data/journals/multiline-tags.journal
@@ -0,0 +1,7 @@
+[2013-06-09 15:39] Multiple @line entry with @tags.
+Tag with @punctuation. afterwards
+@TagOnLineAloneWithOutPunctuation
+@TagOnLineAloneWithPunctuation.
+Text before @tag. And After.
+@hi. Hello
+hi Hello
\ No newline at end of file
diff --git a/features/data/journals/multiline.journal b/features/data/journals/multiline.journal
new file mode 100644
index 00000000..294ed141
--- /dev/null
+++ b/features/data/journals/multiline.journal
@@ -0,0 +1,5 @@
+[2013-06-09 15:39] Multiple line entry.
+This is the first line.
+This line doesn't have any ending punctuation
+
+There is a blank line above this.
diff --git a/features/dayone.feature b/features/dayone.feature
index 51aa2033..8e50b42b 100644
--- a/features/dayone.feature
+++ b/features/dayone.feature
@@ -1,7 +1,5 @@
Feature: Dayone specific implementation details.
- # fails when system time is UTC (as on Travis-CI)
- @skip
Scenario: Loading a DayOne Journal
Given we use the config "dayone.yaml"
When we run "jrnl -from 'feb 2013'"
@@ -15,7 +13,7 @@ Feature: Dayone specific implementation details.
2013-07-17 11:38 This entry is starred!
"""
- # fails when system time is UTC (as on Travis-CI)
+ # broken still
@skip
Scenario: Entries without timezone information will be interpreted as in the current timezone
Given we use the config "dayone.yaml"
@@ -23,7 +21,6 @@ Feature: Dayone specific implementation details.
Then we should get no error
and the output should contain "2013-01-17T18:37Z" in the local time
- @skip
Scenario: Writing into Dayone
Given we use the config "dayone.yaml"
When we run "jrnl 01 may 1979: Being born hurts."
@@ -33,8 +30,6 @@ Feature: Dayone specific implementation details.
1979-05-01 09:00 Being born hurts.
"""
- # fails when system time is UTC (as on Travis-CI)
- @skip
Scenario: Loading tags from a DayOne Journal
Given we use the config "dayone.yaml"
When we run "jrnl --tags"
@@ -44,8 +39,6 @@ Feature: Dayone specific implementation details.
@play : 1
"""
- # fails when system time is UTC (as on Travis-CI)
- @skip
Scenario: Saving tags from a DayOne Journal
Given we use the config "dayone.yaml"
When we run "jrnl A hard day at @work"
@@ -56,8 +49,6 @@ Feature: Dayone specific implementation details.
@play : 1
"""
- # fails when system time is UTC (as on Travis-CI)
- @skip
Scenario: Filtering by tags from a DayOne Journal
Given we use the config "dayone.yaml"
When we run "jrnl @work"
@@ -66,8 +57,6 @@ Feature: Dayone specific implementation details.
2013-05-17 11:39 This entry has tags!
"""
- # fails when system time is UTC (as on Travis-CI)
- @skip
Scenario: Exporting dayone to json
Given we use the config "dayone.yaml"
When we run "jrnl --export json"
diff --git a/features/dayone_regressions.feature b/features/dayone_regressions.feature
index 62c8cc24..3e98f9e9 100644
--- a/features/dayone_regressions.feature
+++ b/features/dayone_regressions.feature
@@ -23,9 +23,3 @@ Feature: Zapped Dayone bugs stay dead!
2014-04-24 09:00 Ran 6.2 miles today in 1:02:03.
| I'm feeling sore because I forgot to stretch.
"""
-
- Scenario: Opening an folder that's not a DayOne folder gives a nice error message
- Given we use the config "empty_folder.yaml"
- When we run "jrnl Herro"
- Then we should get an error
- Then we should see the message "is a directory, but doesn't seem to be a DayOne journal either"
diff --git a/features/exporting.feature b/features/exporting.feature
index db2ef5b3..5705fda1 100644
--- a/features/exporting.feature
+++ b/features/exporting.feature
@@ -4,21 +4,20 @@ Feature: Exporting a Journal
Given we use the config "tags.yaml"
When we run "jrnl --export json"
Then we should get no error
- and the output should be parsable as json
- and "entries" in the json output should have 2 elements
- and "tags" in the json output should contain "@idea"
- and "tags" in the json output should contain "@journal"
- and "tags" in the json output should contain "@dan"
+ And the output should be parsable as json
+ And "entries" in the json output should have 2 elements
+ And "tags" in the json output should contain "@idea"
+ And "tags" in the json output should contain "@journal"
+ And "tags" in the json output should contain "@dan"
Scenario: Exporting using filters should only export parts of the journal
Given we use the config "tags.yaml"
When we run "jrnl -until 'may 2013' --export json"
- # Then we should get no error
Then the output should be parsable as json
- and "entries" in the json output should have 1 element
- and "tags" in the json output should contain "@idea"
- and "tags" in the json output should contain "@journal"
- and "tags" in the json output should not contain "@dan"
+ And "entries" in the json output should have 1 element
+ And "tags" in the json output should contain "@idea"
+ And "tags" in the json output should contain "@journal"
+ And "tags" in the json output should not contain "@dan"
Scenario: Exporting using custom templates
Given we use the config "basic.yaml"
@@ -83,3 +82,57 @@ Feature: Exporting a Journal
More stuff
more stuff again
"""
+
+ Scenario: Exporting to XML
+ Given we use the config "tags.yaml"
+ When we run "jrnl --export xml"
+ Then the output should be a valid XML string
+ And "entries" node in the xml output should have 2 elements
+ And "tags" in the xml output should contain ["@idea", "@journal", "@dan"]
+
+ Scenario: Exporting tags
+ Given we use the config "tags.yaml"
+ When we run "jrnl --export tags"
+ Then the output should be
+ """
+ @idea : 2
+ @journal : 1
+ @dan : 1
+ """
+
+ Scenario: Exporting fancy
+ Given we use the config "tags.yaml"
+ When we run "jrnl --export fancy"
+ Then the output should be
+ """
+ ┎──────────────────────────────────────────────────────────────╮2013-04-09 15:39
+ ┃ I have an @idea: ╘═══════════════╕
+ ┠╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
+ ┃ (1) write a command line @journal software │
+ ┃ (2) ??? │
+ ┃ (3) PROFIT! │
+ ┖──────────────────────────────────────────────────────────────────────────────┘
+ ┎──────────────────────────────────────────────────────────────╮2013-06-10 15:40
+ ┃ I met with @dan. ╘═══════════════╕
+ ┠╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
+ ┃ As alway's he shared his latest @idea on how to rule the world with me. │
+ ┃ inst │
+ ┖──────────────────────────────────────────────────────────────────────────────┘
+ """
+
+ Scenario: Export to yaml
+ Given we use the config "tags.yaml"
+ And we created a directory named "exported_journal"
+ When we run "jrnl --export yaml -o exported_journal"
+ Then "exported_journal" should contain the files ["2013-04-09_i-have-an-idea.md", "2013-06-10_i-met-with-dan.md"]
+ And the content of exported yaml "exported_journal/2013-04-09_i-have-an-idea.md" should be
+ """
+ title: I have an @idea:
+ date: 2013-04-09 15:39
+ stared: False
+ tags: idea, journal
+
+ (1) write a command line @journal software
+ (2) ???
+ (3) PROFIT!
+ """
diff --git a/features/folder.feature b/features/folder.feature
new file mode 100644
index 00000000..650ac53e
--- /dev/null
+++ b/features/folder.feature
@@ -0,0 +1,42 @@
+Feature: Testing a journal with a root directory and multiple files in the format of yyyy/mm/dd.txt
+
+ Scenario: Opening an folder that's not a DayOne folder should treat as folder journal
+ Given we use the config "empty_folder.yaml"
+ When we run "jrnl 23 july 2013: Testing folder journal."
+ Then we should see the message "Entry added"
+ When we run "jrnl -1"
+ Then the output should be
+ """
+ 2013-07-23 09:00 Testing folder journal.
+ """
+
+ Scenario: Adding entries to a Folder journal should generate date files
+ Given we use the config "empty_folder.yaml"
+ When we run "jrnl 23 July 2013: Testing folder journal."
+ Then we should see the message "Entry added"
+ When the journal directory is listed
+ Then the output should contain "2013/07/23.txt" or "2013\07\23.txt"
+
+
+ Scenario: Adding multiple entries to a Folder journal should generate multiple date files
+ Given we use the config "empty_folder.yaml"
+ When we run "jrnl 23 July 2013: Testing folder journal."
+ And we run "jrnl 3/7/2014: Second entry of journal."
+ Then we should see the message "Entry added"
+ When the journal directory is listed
+ Then the output should contain "2013/07/23.txt" or "2013\07\23.txt"
+ Then the output should contain "2014/03/07.txt" or "2014\03\07.txt"
+
+ Scenario: Out of order entries to a Folder journal should be listed in date order
+ Given we use the config "empty_folder.yaml"
+ When we run "jrnl 3/7/2014 4:37pm: Second entry of journal."
+ Then we should see the message "Entry added"
+ When we run "jrnl 23 July 2013: Testing folder journal."
+ Then we should see the message "Entry added"
+ When we run "jrnl -2"
+ Then the output should be
+ """
+ 2013-07-23 09:00 Testing folder journal.
+
+ 2014-03-07 16:37 Second entry of journal.
+ """
diff --git a/features/regression.feature b/features/regression.feature
index bedc1295..5aa1db06 100644
--- a/features/regression.feature
+++ b/features/regression.feature
@@ -1,7 +1,7 @@
Feature: Zapped bugs should stay dead.
Scenario: Writing an entry does not print the entire journal
- # https://github.com/maebert/jrnl/issues/87
+ # https://github.com/jrnl-org/jrnl/issues/87
Given we use the config "basic.yaml"
When we run "jrnl 23 july 2013: A cold and stormy day. I ate crisps on the sofa."
Then we should see the message "Entry added"
@@ -9,21 +9,14 @@ Feature: Zapped bugs should stay dead.
Then the output should not contain "Life is good"
Scenario: Date with time should be parsed correctly
- # https://github.com/maebert/jrnl/issues/117
+ # https://github.com/jrnl-org/jrnl/issues/117
Given we use the config "basic.yaml"
When we run "jrnl 2013-11-30 15:42: Project Started."
Then we should see the message "Entry added"
and the journal should contain "[2013-11-30 15:42] Project Started."
- Scenario: Date in the future should be parsed correctly
- # https://github.com/maebert/jrnl/issues/185
- Given we use the config "basic.yaml"
- When we run "jrnl 26/06/2019: Planet? Earth. Year? 2019."
- Then we should see the message "Entry added"
- and the journal should contain "[2019-06-26 09:00] Planet?"
-
Scenario: Loading entry with ambiguous time stamp
- #https://github.com/maebert/jrnl/issues/153
+ #https://github.com/jrnl-org/jrnl/issues/153
Given we use the config "bug153.yaml"
When we run "jrnl -1"
Then we should get no error
@@ -32,6 +25,19 @@ Feature: Zapped bugs should stay dead.
2013-10-27 03:27 Some text.
"""
+ Scenario: Date in the future should be parsed correctly
+ # https://github.com/jrnl-org/jrnl/issues/185
+ Given we use the config "basic.yaml"
+ When we run "jrnl 26/06/2019: Planet? Earth. Year? 2019."
+ Then we should see the message "Entry added"
+ and the journal should contain "[2019-06-26 09:00] Planet?"
+
+ Scenario: Empty DayOne entry bodies should not error
+ # https://github.com/jrnl-org/jrnl/issues/780
+ Given we use the config "bug780.yaml"
+ When we run "jrnl --short"
+ Then we should get no error
+
Scenario: Title with an embedded period.
Given we use the config "basic.yaml"
When we run "jrnl 04-24-2014: Created a new website - empty.com. Hope to get a lot of traffic."
diff --git a/features/steps/core.py b/features/steps/core.py
index af0a657e..26398fc4 100644
--- a/features/steps/core.py
+++ b/features/steps/core.py
@@ -3,7 +3,6 @@ from unittest.mock import patch
from behave import given, when, then
from jrnl import cli, install, Journal, util, plugins
from jrnl import __version__
-from dateutil import parser as date_parser
from collections import defaultdict
try:
@@ -11,7 +10,9 @@ try:
except ImportError:
import parsedatetime as pdt
import time
+from codecs import encode, decode
import os
+import ast
import json
import yaml
import keyring
@@ -81,18 +82,15 @@ def set_config(context, config_file):
cf.write("version: {}".format(__version__))
-@when('we open the editor and enter ""')
@when('we open the editor and enter "{text}"')
+@when("we open the editor and enter nothing")
def open_editor_and_enter(context, text=""):
- text = text or context.text
+ text = text or context.text or ""
def _mock_editor_function(command):
tmpfile = command[-1]
with open(tmpfile, "w+") as f:
- if text is not None:
- f.write(text)
- else:
- f.write("")
+ f.write(text)
return tmpfile
@@ -118,7 +116,7 @@ def _mock_input(inputs):
@when('we run "{command}" and enter')
-@when('we run "{command}" and enter ""')
+@when('we run "{command}" and enter nothing')
@when('we run "{command}" and enter "{inputs}"')
def run_with_input(context, command, inputs=""):
# create an iterator through all inputs. These inputs will be fed one by one
@@ -185,53 +183,6 @@ def no_error(context):
assert context.exit_status == 0, context.exit_status
-@then("the output should be parsable as json")
-def check_output_json(context):
- out = context.stdout_capture.getvalue()
- assert json.loads(out), out
-
-
-@then('"{field}" in the json output should have {number:d} elements')
-@then('"{field}" in the json output should have 1 element')
-def check_output_field(context, field, number=1):
- out = context.stdout_capture.getvalue()
- out_json = json.loads(out)
- assert field in out_json, [field, out_json]
- assert len(out_json[field]) == number, len(out_json[field])
-
-
-@then('"{field}" in the json output should not contain "{key}"')
-def check_output_field_not_key(context, field, key):
- out = context.stdout_capture.getvalue()
- out_json = json.loads(out)
- assert field in out_json
- assert key not in out_json[field]
-
-
-@then('"{field}" in the json output should contain "{key}"')
-def check_output_field_key(context, field, key):
- out = context.stdout_capture.getvalue()
- out_json = json.loads(out)
- assert field in out_json
- assert key in out_json[field]
-
-
-@then('the json output should contain {path} = "{value}"')
-def check_json_output_path(context, path, value):
- """ E.g.
- the json output should contain entries.0.title = "hello"
- """
- out = context.stdout_capture.getvalue()
- struct = json.loads(out)
-
- for node in path.split("."):
- try:
- struct = struct[int(node)]
- except ValueError:
- struct = struct[node]
- assert struct == value, struct
-
-
@then("the output should be")
@then('the output should be "{text}"')
def check_output(context, text=None):
@@ -258,10 +209,11 @@ def check_output_time_inline(context, text):
@then("the output should contain")
@then('the output should contain "{text}"')
-def check_output_inline(context, text=None):
+@then('the output should contain "{text}" or "{text2}"')
+def check_output_inline(context, text=None, text2=None):
text = text or context.text
out = context.stdout_capture.getvalue()
- assert text in out, text
+ assert text in out or text2 in out, text or text2
@then('the output should not contain "{text}"')
@@ -300,8 +252,15 @@ def journal_doesnt_exist(context, journal_name="default"):
@then('the config should have "{key}" set to "{value}"')
@then('the config for journal "{journal}" should have "{key}" set to "{value}"')
def config_var(context, key, value, journal=None):
- t, value = value.split(":")
- value = {"bool": lambda v: v.lower() == "true", "int": int, "str": str}[t](value)
+ if not value[0] == "{":
+ t, value = value.split(":")
+ value = {"bool": lambda v: v.lower() == "true", "int": int, "str": str}[t](
+ value
+ )
+ else:
+ # Handle value being a dictionary
+ value = ast.literal_eval(value)
+
config = util.load_config(install.CONFIG_FILE_PATH)
if journal:
config = config["journals"][journal]
@@ -318,6 +277,17 @@ def check_journal_entries(context, number, journal_name="default"):
assert len(journal.entries) == number
+@when("the journal directory is listed")
+def list_journal_directory(context, journal="default"):
+ files = []
+ with open(install.CONFIG_FILE_PATH) as config_file:
+ config = yaml.load(config_file, Loader=yaml.FullLoader)
+ journal_path = config["journals"][journal]
+ for root, dirnames, f in os.walk(journal_path):
+ for file in f:
+ print(os.path.join(root, file))
+
+
@then("fail")
def debug_fail(context):
assert False
diff --git a/features/steps/export_steps.py b/features/steps/export_steps.py
new file mode 100644
index 00000000..b7965ab8
--- /dev/null
+++ b/features/steps/export_steps.py
@@ -0,0 +1,124 @@
+import json
+import os
+import shutil
+from xml.etree import ElementTree
+
+from behave import then, given
+
+
+@then("the output should be parsable as json")
+def check_output_json(context):
+ out = context.stdout_capture.getvalue()
+ assert json.loads(out), out
+
+
+@then('"{field}" in the json output should have {number:d} elements')
+@then('"{field}" in the json output should have 1 element')
+def check_output_field(context, field, number=1):
+ out = context.stdout_capture.getvalue()
+ out_json = json.loads(out)
+ assert field in out_json, [field, out_json]
+ assert len(out_json[field]) == number, len(out_json[field])
+
+
+@then('"{field}" in the json output should not contain "{key}"')
+def check_output_field_not_key(context, field, key):
+ out = context.stdout_capture.getvalue()
+ out_json = json.loads(out)
+ assert field in out_json
+ assert key not in out_json[field]
+
+
+@then('"{field}" in the json output should contain "{key}"')
+def check_output_field_key(context, field, key):
+ out = context.stdout_capture.getvalue()
+ out_json = json.loads(out)
+ assert field in out_json
+ assert key in out_json[field]
+
+
+@then('the json output should contain {path} = "{value}"')
+def check_json_output_path(context, path, value):
+ """ E.g.
+ the json output should contain entries.0.title = "hello"
+ """
+ out = context.stdout_capture.getvalue()
+ struct = json.loads(out)
+
+ for node in path.split("."):
+ try:
+ struct = struct[int(node)]
+ except ValueError:
+ struct = struct[node]
+ assert struct == value, struct
+
+
+@then("the output should be a valid XML string")
+def assert_valid_xml_string(context):
+ output = context.stdout_capture.getvalue()
+ xml_tree = ElementTree.fromstring(output)
+ assert xml_tree, output
+
+
+@then('"entries" node in the xml output should have {number:d} elements')
+def assert_xml_output_entries_count(context, number):
+ output = context.stdout_capture.getvalue()
+ xml_tree = ElementTree.fromstring(output)
+
+ xml_tags = (node.tag for node in xml_tree)
+ assert "entries" in xml_tags, str(list(xml_tags))
+
+ actual_entry_count = len(xml_tree.find("entries"))
+ assert actual_entry_count == number, actual_entry_count
+
+
+@then('"tags" in the xml output should contain {expected_tags_json_list}')
+def assert_xml_output_tags(context, expected_tags_json_list):
+ output = context.stdout_capture.getvalue()
+ xml_tree = ElementTree.fromstring(output)
+
+ xml_tags = (node.tag for node in xml_tree)
+ assert "tags" in xml_tags, str(list(xml_tags))
+
+ expected_tags = json.loads(expected_tags_json_list)
+ actual_tags = set(t.attrib["name"] for t in xml_tree.find("tags"))
+ assert actual_tags == set(expected_tags), [actual_tags, set(expected_tags)]
+
+
+@given('we created a directory named "{dir_name}"')
+def create_directory(context, dir_name):
+ if os.path.exists(dir_name):
+ shutil.rmtree(dir_name)
+ os.mkdir(dir_name)
+
+
+@then('"{dir_name}" should contain the files {expected_files_json_list}')
+def assert_dir_contains_files(context, dir_name, expected_files_json_list):
+ actual_files = os.listdir(dir_name)
+ expected_files = json.loads(expected_files_json_list)
+ assert actual_files == expected_files, [actual_files, expected_files]
+
+
+@then('the content of exported yaml "{file_path}" should be')
+def assert_exported_yaml_file_content(context, file_path):
+ expected_content = context.text.strip().splitlines()
+
+ with open(file_path, "r") as f:
+ actual_content = f.read().strip().splitlines()
+
+ for actual_line, expected_line in zip(actual_content, expected_content):
+ if actual_line.startswith("tags: ") and expected_line.startswith("tags: "):
+ assert_equal_tags_ignoring_order(actual_line, expected_line)
+ else:
+ assert actual_line.strip() == expected_line.strip(), [
+ actual_line.strip(),
+ expected_line.strip(),
+ ]
+
+
+def assert_equal_tags_ignoring_order(actual_line, expected_line):
+ actual_tags = set(tag.strip() for tag in actual_line[len("tags: ") :].split(","))
+ expected_tags = set(
+ tag.strip() for tag in expected_line[len("tags: ") :].split(",")
+ )
+ assert actual_tags == expected_tags, [actual_tags, expected_tags]
diff --git a/features/tagging.feature b/features/tagging.feature
index 2cbf7ce1..8b4cefb7 100644
--- a/features/tagging.feature
+++ b/features/tagging.feature
@@ -82,3 +82,19 @@ Feature: Tagging
"""
@thought : 2
"""
+
+
+ Scenario: Printing a journal that has multiline entries with tags
+ Given we use the config "multiline-tags.yaml"
+ When we run "jrnl -n 1"
+ Then we should get no error
+ and the output should be
+ """
+ 2013-06-09 15:39 Multiple @line entry with @tags.
+ | Tag with @punctuation. afterwards
+ | @TagOnLineAloneWithOutPunctuation
+ | @TagOnLineAloneWithPunctuation.
+ | Text before @tag. And After.
+ | @hi. Hello
+ | hi Hello
+ """
\ No newline at end of file
diff --git a/features/upgrade.feature b/features/upgrade.feature
index ef597d4f..e4377970 100644
--- a/features/upgrade.feature
+++ b/features/upgrade.feature
@@ -22,6 +22,11 @@ Feature: Upgrading Journals from 1.x.x to 2.x.x
Then the output should contain "Password"
and the output should contain "2013-06-10 15:40 Life is good"
+ Scenario: Upgrading a config without colors to colors
+ Given we use the config "no_colors.yaml"
+ When we run "jrnl -n 1"
+ Then the config should have "colors" set to "{'date':'none', 'title':'none', 'body':'none', 'tags':'none'}"
+
Scenario: Upgrade and parse journals with little endian date format
Given we use the config "upgrade_from_195_little_endian_dates.json"
When we run "jrnl -9" and enter "Y"
@@ -32,3 +37,4 @@ Feature: Upgrading Journals from 1.x.x to 2.x.x
10.06.2013 15:40 He said "[this] is the best time to be alive".
"""
Then the journal should have 2 entries
+
diff --git a/jrnl/DayOneJournal.py b/jrnl/DayOneJournal.py
index 83eb6788..8e8b2cd0 100644
--- a/jrnl/DayOneJournal.py
+++ b/jrnl/DayOneJournal.py
@@ -52,7 +52,11 @@ class DayOne(Journal.Journal):
except (KeyError, pytz.exceptions.UnknownTimeZoneError):
timezone = tzlocal.get_localzone()
date = dict_entry["Creation Date"]
- date = date + timezone.utcoffset(date, is_dst=False)
+ # convert the date to UTC rather than keep messing with
+ # timezones
+ if timezone.zone != "UTC":
+ date = date + timezone.utcoffset(date, is_dst=False)
+
entry = Entry.Entry(
self,
date,
diff --git a/jrnl/EncryptedJournal.py b/jrnl/EncryptedJournal.py
index d6681a47..cc5af748 100644
--- a/jrnl/EncryptedJournal.py
+++ b/jrnl/EncryptedJournal.py
@@ -23,7 +23,7 @@ def make_key(password):
length=32,
# Salt is hard-coded
salt=b"\xf2\xd5q\x0e\xc1\x8d.\xde\xdc\x8e6t\x89\x04\xce\xf8",
- iterations=100000,
+ iterations=100_000,
backend=default_backend(),
)
key = kdf.derive(password)
diff --git a/jrnl/Entry.py b/jrnl/Entry.py
index 80754e05..02f7c468 100755
--- a/jrnl/Entry.py
+++ b/jrnl/Entry.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python
import re
-import textwrap
+import ansiwrap
from datetime import datetime
-from .util import split_title
+from .util import split_title, colorize, highlight_tags_with_background_color
class Entry:
@@ -22,7 +22,7 @@ class Entry:
def _parse_text(self):
raw_text = self.text
lines = raw_text.splitlines()
- if lines[0].strip().endswith("*"):
+ if lines and lines[0].strip().endswith("*"):
self.starred = True
raw_text = lines[0].strip("\n *") + "\n" + "\n".join(lines[1:])
self._title, self._body = split_title(raw_text)
@@ -49,7 +49,7 @@ class Entry:
@staticmethod
def tag_regex(tagsymbols):
- pattern = fr"(?u)(?:^|\s)([{tagsymbols}][-+*#/\w]+)"
+ pattern = fr"(? 20 or not all(
diff --git a/jrnl/FolderJournal.py b/jrnl/FolderJournal.py
new file mode 100644
index 00000000..19519a14
--- /dev/null
+++ b/jrnl/FolderJournal.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+from __future__ import absolute_import, unicode_literals
+from . import Entry
+from . import Journal
+import codecs
+import os
+import fnmatch
+
+
+def get_files(journal_config):
+ """Searches through sub directories starting with journal_config and find all text files"""
+ filenames = []
+ for root, dirnames, f in os.walk(journal_config):
+ for filename in fnmatch.filter(f, "*.txt"):
+ filenames.append(os.path.join(root, filename))
+ return filenames
+
+
+class Folder(Journal.Journal):
+ """A Journal handling multiple files in a folder"""
+
+ def __init__(self, **kwargs):
+ self.entries = []
+ self._diff_entry_dates = []
+ super(Folder, self).__init__(**kwargs)
+
+ def open(self):
+ filenames = []
+ self.entries = []
+ filenames = get_files(self.config["journal"])
+ for filename in filenames:
+ with codecs.open(filename, "r", "utf-8") as f:
+ journal = f.read()
+ self.entries.extend(self._parse(journal))
+ self.sort()
+ return self
+
+ def write(self):
+ """Writes only the entries that have been modified into proper files."""
+ # Create a list of dates of modified entries. Start with diff_entry_dates
+ modified_dates = self._diff_entry_dates
+ seen_dates = set(self._diff_entry_dates)
+ for e in self.entries:
+ if e.modified:
+ if e.date not in seen_dates:
+ modified_dates.append(e.date)
+ seen_dates.add(e.date)
+
+ # For every date that had a modified entry, write to a file
+ for d in modified_dates:
+ write_entries = []
+ filename = os.path.join(
+ self.config["journal"],
+ d.strftime("%Y"),
+ d.strftime("%m"),
+ d.strftime("%d") + ".txt",
+ )
+ dirname = os.path.dirname(filename)
+ # create directory if it doesn't exist
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+ for e in self.entries:
+ if (
+ e.date.year == d.year
+ and e.date.month == d.month
+ and e.date.day == d.day
+ ):
+ write_entries.append(e)
+ journal = "\n".join([e.__str__() for e in write_entries])
+ with codecs.open(filename, "w", "utf-8") as journal_file:
+ journal_file.write(journal)
+ # look for and delete empty files
+ filenames = []
+ filenames = get_files(self.config["journal"])
+ for filename in filenames:
+ if os.stat(filename).st_size <= 0:
+ # print("empty file: {}".format(filename))
+ os.remove(filename)
+
+ def parse_editable_str(self, edited):
+ """Parses the output of self.editable_str and updates it's entries."""
+ mod_entries = self._parse(edited)
+ diff_entries = set(self.entries) - set(mod_entries)
+ for e in diff_entries:
+ self._diff_entry_dates.append(e.date)
+ # Match those entries that can be found in self.entries and set
+ # these to modified, so we can get a count of how many entries got
+ # modified and how many got deleted later.
+ for entry in mod_entries:
+ entry.modified = not any(entry == old_entry for old_entry in self.entries)
+ self.entries = mod_entries
diff --git a/jrnl/Journal.py b/jrnl/Journal.py
index e5bf4ecc..3302cfdd 100644
--- a/jrnl/Journal.py
+++ b/jrnl/Journal.py
@@ -153,20 +153,7 @@ class Journal:
def pprint(self, short=False):
"""Prettyprints the journal's entries"""
- sep = "\n"
- pp = sep.join([e.pprint(short=short) for e in self.entries])
- if self.config["highlight"]: # highlight tags
- if self.search_tags:
- for tag in self.search_tags:
- tagre = re.compile(re.escape(tag), re.IGNORECASE)
- pp = re.sub(tagre, lambda match: util.colorize(match.group(0)), pp)
- else:
- pp = re.sub(
- Entry.Entry.tag_regex(self.config["tagsymbols"]),
- lambda match: util.colorize(match.group(0)),
- pp,
- )
- return pp
+ return "\n".join([e.pprint(short=short) for e in self.entries])
def __str__(self):
return self.pprint()
@@ -378,12 +365,9 @@ def open_journal(name, config, legacy=False):
return DayOneJournal.DayOne(**config).open()
else:
- print(
- f"[Error: {config['journal']} is a directory, but doesn't seem to be a DayOne journal either.",
- file=sys.stderr,
- )
+ from . import FolderJournal
- sys.exit(1)
+ return FolderJournal.Folder(**config).open()
if not config["encrypt"]:
if legacy:
diff --git a/jrnl/cli.py b/jrnl/cli.py
index 1c53dc29..408f3518 100644
--- a/jrnl/cli.py
+++ b/jrnl/cli.py
@@ -15,6 +15,7 @@ from .util import ERROR_COLOR, RESET_COLOR, UserAbort
import jrnl
import argparse
import sys
+import re
import logging
log = logging.getLogger(__name__)
@@ -173,7 +174,11 @@ def parse_args(args=None):
action="store_true",
)
- return parser.parse_args(args)
+ # Handle '-123' as a shortcut for '-n 123'
+ num = re.compile(r"^-(\d+)$")
+ if args is None:
+ args = sys.argv[1:]
+ return parser.parse_args([num.sub(r"-n \1", a) for a in args])
def guess_mode(args, config):
@@ -309,14 +314,6 @@ def run(manual_args=None):
config = util.scope_config(config, journal_name)
- # If the first remaining argument looks like e.g. '-3', interpret that as a limiter
- if not args.limit and args.text and args.text[0].startswith("-"):
- try:
- args.limit = int(args.text[0].lstrip("-"))
- args.text = args.text[1:]
- except ValueError:
- pass
-
log.debug('Using journal "%s"', journal_name)
mode_compose, mode_export, mode_import = guess_mode(args, config)
diff --git a/jrnl/export.py b/jrnl/export.py
deleted file mode 100644
index e95d4c12..00000000
--- a/jrnl/export.py
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env python
-
-from .util import ERROR_COLOR, RESET_COLOR
-from .util import slugify
-from .plugins.template import Template
-import os
-
-
-class Exporter:
- """This Exporter can convert entries and journals into text files."""
-
- def __init__(self, format):
- with open("jrnl/templates/" + format + ".template") as f:
- front_matter, body = f.read().strip("-\n").split("---", 2)
- self.template = Template(body)
-
- def export_entry(self, entry):
- """Returns a string representation of a single entry."""
- return str(entry)
-
- def _get_vars(self, journal):
- return {"journal": journal, "entries": journal.entries, "tags": journal.tags}
-
- def export_journal(self, 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):
- """Exports a journal into a single file."""
- try:
- 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:
- return f"[{ERROR_COLOR}ERROR{RESET_COLOR}: {e.filename} {e.strerror}]"
-
- def make_filename(self, entry):
- 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."""
- for entry in journal.entries:
- try:
- full_path = os.path.join(path, self.make_filename(entry))
- 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}]"
- 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
- a single file if output is a file name, or returns the exporter's
- 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
- return self.write_file(journal, output)
- else:
- return self.export_journal(journal)
diff --git a/jrnl/install.py b/jrnl/install.py
index 118b7c32..d8b3255f 100644
--- a/jrnl/install.py
+++ b/jrnl/install.py
@@ -9,7 +9,7 @@ from . import upgrade
from . import __version__
from .Journal import PlainJournal
from .EncryptedJournal import EncryptedJournal
-from .util import UserAbort
+from .util import UserAbort, verify_config
import yaml
import logging
import sys
@@ -47,7 +47,7 @@ def module_exists(module_name):
default_config = {
"version": __version__,
- "journals": {DEFAULT_JOURNAL_KEY: JOURNAL_FILE_PATH},
+ "journals": {"default": JOURNAL_FILE_PATH},
"editor": os.getenv("VISUAL") or os.getenv("EDITOR") or "",
"encrypt": False,
"template": False,
@@ -58,6 +58,7 @@ default_config = {
"highlight": True,
"linewrap": 79,
"indent_character": "|",
+ "colors": {"date": "none", "title": "none", "body": "none", "tags": "none",},
}
@@ -114,6 +115,7 @@ def load_or_install_jrnl():
sys.exit(1)
upgrade_config(config)
+ verify_config(config)
return config
else:
diff --git a/jrnl/util.py b/jrnl/util.py
index add70ff3..d6b796c3 100644
--- a/jrnl/util.py
+++ b/jrnl/util.py
@@ -4,24 +4,24 @@ import sys
import os
import getpass as gp
import yaml
+import colorama
if "win32" in sys.platform:
- import colorama
-
colorama.init()
import re
import tempfile
import subprocess
import unicodedata
import shlex
+from string import punctuation, whitespace
import logging
from typing import Optional, Callable
log = logging.getLogger(__name__)
-WARNING_COLOR = "\033[33m"
-ERROR_COLOR = "\033[31m"
-RESET_COLOR = "\033[0m"
+WARNING_COLOR = colorama.Fore.YELLOW
+ERROR_COLOR = colorama.Fore.RED
+RESET_COLOR = colorama.Fore.RESET
# Based on Segtok by Florian Leitner
# https://github.com/fnl/segtok
@@ -140,6 +140,27 @@ def scope_config(config, journal_name):
return config
+def verify_config(config):
+ """
+ Ensures the keys set for colors are valid colorama.Fore attributes, or "None"
+ :return: True if all keys are set correctly, False otherwise
+ """
+ all_valid_colors = True
+ for key, color in config["colors"].items():
+ upper_color = color.upper()
+ if upper_color == "NONE":
+ continue
+ if not getattr(colorama.Fore, upper_color, None):
+ print(
+ "[{2}ERROR{3}: {0} set to invalid color: {1}]".format(
+ key, color, ERROR_COLOR, RESET_COLOR
+ ),
+ file=sys.stderr,
+ )
+ all_valid_colors = False
+ return all_valid_colors
+
+
def get_text_from_editor(config, template=""):
filehandle, tmpfile = tempfile.mkstemp(prefix="jrnl", text=True, suffix=".txt")
os.close(filehandle)
@@ -165,9 +186,75 @@ def get_text_from_editor(config, template=""):
return raw
-def colorize(string):
- """Returns the string wrapped in cyan ANSI escape"""
- return f"\033[36m{string}\033[39m"
+def colorize(string, color, bold=False):
+ """Returns the string colored with colorama.Fore.color. If the color set by
+ the user is "NONE" or the color doesn't exist in the colorama.Fore attributes,
+ it returns the string without any modification."""
+ color_escape = getattr(colorama.Fore, color.upper(), None)
+ if not color_escape:
+ return string
+ elif not bold:
+ return color_escape + string + colorama.Fore.RESET
+ else:
+ return colorama.Style.BRIGHT + color_escape + string + colorama.Style.RESET_ALL
+
+
+def highlight_tags_with_background_color(entry, text, color, is_title=False):
+ """
+ Takes a string and colorizes the tags in it based upon the config value for
+ color.tags, while colorizing the rest of the text based on `color`.
+ :param entry: Entry object, for access to journal config
+ :param text: Text to be colorized
+ :param color: Color for non-tag text, passed to colorize()
+ :param is_title: Boolean flag indicating if the text is a title or not
+ :return: Colorized str
+ """
+
+ def colorized_text_generator(fragments):
+ """Efficiently generate colorized tags / text from text fragments.
+ Taken from @shobrook. Thanks, buddy :)
+ :param fragments: List of strings representing parts of entry (tag or word).
+ :rtype: List of tuples
+ :returns [(colorized_str, original_str)]"""
+ for part in fragments:
+ if part and part[0] not in config["tagsymbols"]:
+ yield (colorize(part, color, bold=is_title), part)
+ elif part:
+ yield (colorize(part, config["colors"]["tags"], bold=True), part)
+
+ config = entry.journal.config
+ if config["highlight"]: # highlight tags
+ if entry.journal.search_tags:
+ text_fragments = []
+ for tag in entry.journal.search_tags:
+ text_fragments.extend(
+ re.split(re.compile(re.escape(tag), re.IGNORECASE), text)
+ )
+ else:
+ text_fragments = re.split(entry.tag_regex(config["tagsymbols"]), text)
+
+ # Colorizing tags inside of other blocks of text
+ final_text = ""
+ previous_piece = ""
+ for colorized_piece, piece in colorized_text_generator(text_fragments):
+ # If this piece is entirely punctuation or whitespace or the start
+ # of a line or the previous piece was a tag or this piece is a tag,
+ # then add it to the final text without a leading space.
+ if (
+ all(char in punctuation + whitespace for char in piece)
+ or previous_piece.endswith("\n")
+ or (previous_piece and previous_piece[0] in config["tagsymbols"])
+ or piece[0] in config["tagsymbols"]
+ ):
+ final_text += colorized_piece
+ else:
+ # Otherwise add a leading space and then append the piece.
+ final_text += " " + colorized_piece
+
+ previous_piece = piece
+ return final_text.lstrip()
+ else:
+ return text
def slugify(string):
diff --git a/mkdocs.yml b/mkdocs.yml
index ed1795d1..4c56567b 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -12,7 +12,7 @@ markdown_extensions:
- admonition
repo_url: https://github.com/jrnl-org/jrnl/
site_author: Manuel Ebert
-site_description: Never Worry about Money Again.
+site_description: Collect your thoughts and notes without leaving the command line.
nav:
- Overview: overview.md
- Quickstart: installation.md
diff --git a/poetry.lock b/poetry.lock
index 16be3709..1a24ab9b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,3 +1,14 @@
+[[package]]
+category = "main"
+description = "textwrap, but savvy to ANSI colors and styles"
+name = "ansiwrap"
+optional = false
+python-versions = "*"
+version = "0.8.4"
+
+[package.dependencies]
+textwrap3 = ">=0.9.2"
+
[[package]]
category = "dev"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
@@ -22,6 +33,12 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "19.3.0"
+[package.extras]
+azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"]
+dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"]
+docs = ["sphinx", "zope.interface"]
+tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
+
[[package]]
category = "dev"
description = "behave is behaviour-driven development, Python style"
@@ -35,6 +52,10 @@ parse = ">=1.8.2"
parse-type = ">=0.4.2"
six = ">=1.11"
+[package.extras]
+develop = ["coverage", "pytest (>=3.0)", "pytest-cov", "tox", "invoke (>=0.21.0)", "path.py (>=8.1.2)", "pycmd", "pathlib", "modernize (>=0.5)", "pylint"]
+docs = ["sphinx (>=1.6)", "sphinx-bootstrap-theme (>=0.6)"]
+
[[package]]
category = "dev"
description = "The uncompromising code formatter."
@@ -52,13 +73,16 @@ regex = "*"
toml = ">=0.9.4"
typed-ast = ">=1.4.0"
+[package.extras]
+d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
+
[[package]]
category = "main"
description = "Foreign Function Interface for Python calling C code."
name = "cffi"
optional = false
python-versions = "*"
-version = "1.13.2"
+version = "1.14.0"
[package.dependencies]
pycparser = "*"
@@ -74,7 +98,6 @@ version = "7.0"
[[package]]
category = "main"
description = "Cross-platform colored terminal text."
-marker = "sys_platform == \"win32\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@@ -92,6 +115,13 @@ version = "2.8"
cffi = ">=1.8,<1.11.3 || >1.11.3"
six = ">=1.4.1"
+[package.extras]
+docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0)", "sphinx-rtd-theme"]
+docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"]
+idna = ["idna (>=2.1)"]
+pep8test = ["flake8", "flake8-import-order", "pep8-naming"]
+test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"]
+
[[package]]
category = "dev"
description = "Discover and load entry points from installed packages."
@@ -114,6 +144,14 @@ mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.5.0,<2.6.0"
pyflakes = ">=2.1.0,<2.2.0"
+[[package]]
+category = "dev"
+description = "Clean single-source support for Python 3 and 2"
+name = "future"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+version = "0.18.2"
+
[[package]]
category = "main"
description = "Read metadata from Python packages"
@@ -121,11 +159,15 @@ marker = "python_version < \"3.8\""
name = "importlib-metadata"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
-version = "1.3.0"
+version = "1.5.0"
[package.dependencies]
zipp = ">=0.5"
+[package.extras]
+docs = ["sphinx", "rst.linker"]
+testing = ["packaging", "importlib-resources"]
+
[[package]]
category = "main"
description = "Low-level, pure Python DBus protocol wrapper."
@@ -135,17 +177,23 @@ optional = false
python-versions = ">=3.5"
version = "0.4.2"
+[package.extras]
+dev = ["testpath"]
+
[[package]]
category = "dev"
description = "A very fast and expressive template engine."
name = "jinja2"
optional = false
-python-versions = "*"
-version = "2.10.3"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "2.11.1"
[package.dependencies]
MarkupSafe = ">=0.23"
+[package.extras]
+i18n = ["Babel (>=0.8)"]
+
[[package]]
category = "main"
description = "Store and access your passwords safely."
@@ -162,6 +210,10 @@ secretstorage = "*"
python = "<3.8"
version = "*"
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov"]
+
[[package]]
category = "dev"
description = "Python LiveReload is an awesome tool for web developers"
@@ -174,17 +226,39 @@ version = "2.6.1"
six = "*"
tornado = "*"
+[[package]]
+category = "dev"
+description = "A Python implementation of Lunr.js"
+name = "lunr"
+optional = false
+python-versions = "*"
+version = "0.5.6"
+
+[package.dependencies]
+future = ">=0.16.0"
+six = ">=1.11.0"
+
+[package.dependencies.nltk]
+optional = true
+version = ">=3.2.5"
+
+[package.extras]
+languages = ["nltk (>=3.2.5)"]
+
[[package]]
category = "dev"
description = "Python implementation of Markdown."
name = "markdown"
optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
-version = "3.1.1"
+python-versions = ">=3.5"
+version = "3.2.1"
[package.dependencies]
setuptools = ">=36"
+[package.extras]
+testing = ["coverage", "pyyaml"]
+
[[package]]
category = "dev"
description = "Safely add untrusted strings to HTML/XML markup."
@@ -206,25 +280,39 @@ category = "dev"
description = "Project documentation with Markdown."
name = "mkdocs"
optional = false
-python-versions = ">=2.7.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
-version = "1.0.4"
+python-versions = ">=3.5"
+version = "1.1"
[package.dependencies]
-Jinja2 = ">=2.7.1"
-Markdown = ">=2.3.1"
+Jinja2 = ">=2.10.1"
+Markdown = ">=3.2.1"
PyYAML = ">=3.10"
click = ">=3.3"
livereload = ">=2.5.1"
tornado = ">=5.0"
+[package.dependencies.lunr]
+extras = ["languages"]
+version = "0.5.6"
+
[[package]]
-category = "main"
-description = "More routines for operating on iterables, beyond itertools"
-marker = "python_version < \"3.8\""
-name = "more-itertools"
+category = "dev"
+description = "Natural Language Toolkit"
+name = "nltk"
optional = false
-python-versions = ">=3.5"
-version = "8.0.2"
+python-versions = "*"
+version = "3.4.5"
+
+[package.dependencies]
+six = "*"
+
+[package.extras]
+all = ["pyparsing", "scikit-learn", "python-crfsuite", "matplotlib", "scipy", "gensim", "requests", "twython", "numpy"]
+corenlp = ["requests"]
+machine_learning = ["gensim", "numpy", "python-crfsuite", "scikit-learn", "scipy"]
+plot = ["matplotlib"]
+tgrep = ["pyparsing"]
+twitter = ["twython"]
[[package]]
category = "dev"
@@ -246,6 +334,10 @@ version = "0.5.2"
parse = ">=1.8.4"
six = ">=1.11"
+[package.extras]
+develop = ["coverage (>=4.4)", "pytest (>=3.2)", "pytest-cov", "tox (>=2.8)"]
+docs = ["sphinx (>=1.2)"]
+
[[package]]
category = "main"
description = "Parse human-readable date/time text."
@@ -262,6 +354,12 @@ optional = false
python-versions = "*"
version = "1.7.2"
+[package.extras]
+argon2 = ["argon2-cffi (>=18.2.0)"]
+bcrypt = ["bcrypt (>=3.1.0)"]
+build_docs = ["sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)", "cloud-sptheme (>=1.10.0)"]
+totp = ["cryptography"]
+
[[package]]
category = "dev"
description = "Utility library for gitignore style pattern matching of file paths."
@@ -335,8 +433,8 @@ category = "main"
description = "YAML parser and emitter for Python"
name = "pyyaml"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-version = "5.2"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "5.3"
[[package]]
category = "dev"
@@ -344,7 +442,7 @@ description = "Alternative regular expression module, to replace re."
name = "regex"
optional = false
python-versions = "*"
-version = "2019.12.20"
+version = "2020.2.20"
[[package]]
category = "main"
@@ -353,19 +451,27 @@ marker = "sys_platform == \"linux\""
name = "secretstorage"
optional = false
python-versions = ">=3.5"
-version = "3.1.1"
+version = "3.1.2"
[package.dependencies]
cryptography = "*"
-jeepney = "*"
+jeepney = ">=0.4.2"
[[package]]
category = "main"
description = "Python 2 and 3 compatibility utilities"
name = "six"
optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*"
-version = "1.13.0"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+version = "1.14.0"
+
+[[package]]
+category = "main"
+description = "textwrap from Python 3.6 backport (plus a few tweaks)"
+name = "textwrap3"
+optional = false
+python-versions = "*"
+version = "0.9.2"
[[package]]
category = "dev"
@@ -389,7 +495,7 @@ description = "a fork of Python 2 and 3 ast modules with type comment support"
name = "typed-ast"
optional = false
python-versions = "*"
-version = "1.4.0"
+version = "1.4.1"
[[package]]
category = "main"
@@ -408,56 +514,318 @@ description = "Backport of pathlib-compatible object wrapper for zip files"
marker = "python_version < \"3.8\""
name = "zipp"
optional = false
-python-versions = ">=2.7"
-version = "0.6.0"
+python-versions = ">=3.6"
+version = "3.0.0"
-[package.dependencies]
-more-itertools = "*"
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
+testing = ["jaraco.itertools", "func-timeout"]
[metadata]
-content-hash = "98e23837423d5d8621f14cbe592d209ef98e1926b7a3f94e0f88bb6be908aae8"
+content-hash = "98c4d0d25bb309075ca86c1ed3ed0d46b0fd1dad66510f8fe95b0ad350065df5"
python-versions = ">=3.6.0, <3.9.0"
-[metadata.hashes]
-appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"]
-asteval = ["5d64e18b8a72c2c7ae8f9b70d1f80b68bbcaa98c1c0d7047c35489d03209bc86"]
-attrs = ["08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", "f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"]
-behave = ["b9662327aa53294c1351b0a9c369093ccec1d21026f050c3bd9b3e5cccf81a86", "ebda1a6c9e5bfe95c5f9f0a2794e01c7098b3dde86c10a95d8621c5907ff6f1c"]
-black = ["1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b", "c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"]
-cffi = ["0b49274afc941c626b605fb59b59c3485c17dc776dc3cc7cc14aca74cc19cc42", "0e3ea92942cb1168e38c05c1d56b0527ce31f1a370f6117f1d490b8dcd6b3a04", "135f69aecbf4517d5b3d6429207b2dff49c876be724ac0c8bf8e1ea99df3d7e5", "19db0cdd6e516f13329cba4903368bff9bb5a9331d3410b1b448daaadc495e54", "2781e9ad0e9d47173c0093321bb5435a9dfae0ed6a762aabafa13108f5f7b2ba", "291f7c42e21d72144bb1c1b2e825ec60f46d0a7468f5346841860454c7aa8f57", "2c5e309ec482556397cb21ede0350c5e82f0eb2621de04b2633588d118da4396", "2e9c80a8c3344a92cb04661115898a9129c074f7ab82011ef4b612f645939f12", "32a262e2b90ffcfdd97c7a5e24a6012a43c61f1f5a57789ad80af1d26c6acd97", "3c9fff570f13480b201e9ab69453108f6d98244a7f495e91b6c654a47486ba43", "415bdc7ca8c1c634a6d7163d43fb0ea885a07e9618a64bda407e04b04333b7db", "42194f54c11abc8583417a7cf4eaff544ce0de8187abaf5d29029c91b1725ad3", "4424e42199e86b21fc4db83bd76909a6fc2a2aefb352cb5414833c030f6ed71b", "4a43c91840bda5f55249413037b7a9b79c90b1184ed504883b72c4df70778579", "599a1e8ff057ac530c9ad1778293c665cb81a791421f46922d80a86473c13346", "5c4fae4e9cdd18c82ba3a134be256e98dc0596af1e7285a3d2602c97dcfa5159", "5ecfa867dea6fabe2a58f03ac9186ea64da1386af2159196da51c4904e11d652", "62f2578358d3a92e4ab2d830cd1c2049c9c0d0e6d3c58322993cc341bdeac22e", "6471a82d5abea994e38d2c2abc77164b4f7fbaaf80261cb98394d5793f11b12a", "6d4f18483d040e18546108eb13b1dfa1000a089bcf8529e30346116ea6240506", "71a608532ab3bd26223c8d841dde43f3516aa5d2bf37b50ac410bb5e99053e8f", "74a1d8c85fb6ff0b30fbfa8ad0ac23cd601a138f7509dc617ebc65ef305bb98d", "7b93a885bb13073afb0aa73ad82059a4c41f4b7d8eb8368980448b52d4c7dc2c", "7d4751da932caaec419d514eaa4215eaf14b612cff66398dd51129ac22680b20", "7f627141a26b551bdebbc4855c1157feeef18241b4b8366ed22a5c7d672ef858", "8169cf44dd8f9071b2b9248c35fc35e8677451c52f795daa2bb4643f32a540bc", "aa00d66c0fab27373ae44ae26a66a9e43ff2a678bf63a9c7c1a9a4d61172827a", "ccb032fda0873254380aa2bfad2582aedc2959186cce61e3a17abc1a55ff89c3", "d754f39e0d1603b5b24a7f8484b22d2904fa551fe865fd0d4c3332f078d20d4e", "d75c461e20e29afc0aee7172a0950157c704ff0dd51613506bd7d82b718e7410", "dcd65317dd15bc0451f3e01c80da2216a31916bdcffd6221ca1202d96584aa25", "e570d3ab32e2c2861c4ebe6ffcad6a8abf9347432a37608fe1fbd157b3f0036b", "fd43a88e045cf992ed09fa724b5315b790525f2676883a6ea64e3263bae6549d"]
-click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"]
-colorama = ["7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", "e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"]
-cryptography = ["02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c", "1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595", "369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad", "3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651", "44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2", "4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff", "58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d", "6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42", "7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d", "73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e", "7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912", "90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793", "971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13", "a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7", "b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0", "b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879", "d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f", "de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9", "df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2", "ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf", "fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"]
-entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"]
-flake8 = ["45681a117ecc81e870cbf1262835ae4af5e7a8b08e40b944a8a6e6b895914cfb", "49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca"]
-importlib-metadata = ["073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", "d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f"]
-jeepney = ["0ba6d8c597e9bef1ebd18aaec595f942a264e25c1a48f164d46120eacaa2e9bb", "6f45dce1125cf6c58a1c88123d3831f36a789f9204fbad3172eac15f8ccd08d0"]
-jinja2 = ["74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", "9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"]
-keyring = ["9b80469783d3f6106bce1d389c6b8b20c8d4d739943b1b8cd0ddc2a45d065f9d", "ee3d35b7f1ac3cb69e9a1e4323534649d3ab2fea402738a77e4250c152970fed"]
-livereload = ["78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b", "89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66"]
-markdown = ["2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a", "56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"]
-markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"]
-mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"]
-mkdocs = ["17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", "8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"]
-more-itertools = ["b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", "c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564"]
-parse = ["95a4f4469e37c57b5e924629ac99926f28bee7da59515dc5b8078c4c3e779249"]
-parse-type = ["089a471b06327103865dfec2dd844230c3c658a4a1b5b4c8b6c16c8f77577f9e", "7f690b18d35048c15438d6d0571f9045cffbec5907e0b1ccf006f889e3a38c0b"]
-parsedatetime = ["3b835fc54e472c17ef447be37458b400e3fefdf14bb1ffdedb5d2c853acf4ba1", "d2e9ddb1e463de871d32088a3f3cea3dc8282b1b2800e081bd0ef86900451667"]
-passlib = ["68c35c98a7968850e17f1b6892720764cc7eed0ef2b7cb3116a89a28e43fe177", "8d666cef936198bc2ab47ee9b0410c94adf2ba798e5a84bf220be079ae7ab6a8"]
-pathspec = ["163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424", "562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"]
-pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"]
-pycparser = ["a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"]
-pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"]
-python-dateutil = ["73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"]
-pytz = ["1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", "b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"]
-pywin32-ctypes = ["24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942", "9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"]
-pyxdg = ["1948ff8e2db02156c0cccd2529b43c0cff56ebaa71f5f021bbd755bc1419190e", "fe2928d3f532ed32b39c32a482b54136fe766d19936afc96c8f00645f9da1a06"]
-pyyaml = ["0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc", "2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803", "35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc", "38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15", "483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075", "4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd", "7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31", "8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f", "c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c", "e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04", "ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"]
-regex = ["032fdcc03406e1a6485ec09b826eac78732943840c4b29e503b789716f051d8d", "0e6cf1e747f383f52a0964452658c04300a9a01e8a89c55ea22813931b580aa8", "106e25a841921d8259dcef2a42786caae35bc750fb996f830065b3dfaa67b77e", "1768cf42a78a11dae63152685e7a1d90af7a8d71d2d4f6d2387edea53a9e0588", "27d1bd20d334f50b7ef078eba0f0756a640fd25f5f1708d3b5bed18a5d6bced9", "29b20f66f2e044aafba86ecf10a84e611b4667643c42baa004247f5dfef4f90b", "4850c78b53acf664a6578bba0e9ebeaf2807bb476c14ec7e0f936f2015133cae", "57eacd38a5ec40ed7b19a968a9d01c0d977bda55664210be713e750dd7b33540", "724eb24b92fc5fdc1501a1b4df44a68b9c1dda171c8ef8736799e903fb100f63", "77ae8d926f38700432807ba293d768ba9e7652df0cbe76df2843b12f80f68885", "78b3712ec529b2a71731fbb10b907b54d9c53a17ca589b42a578bc1e9a2c82ea", "7bbbdbada3078dc360d4692a9b28479f569db7fc7f304b668787afc9feb38ec8", "8d9ef7f6c403e35e73b7fc3cde9f6decdc43b1cb2ff8d058c53b9084bfcb553e", "a83049eb717ae828ced9cf607845929efcb086a001fc8af93ff15c50012a5716", "adc35d38952e688535980ae2109cad3a109520033642e759f987cf47fe278aa1", "c29a77ad4463f71a506515d9ec3a899ed026b4b015bf43245c919ff36275444b", "cfd31b3300fefa5eecb2fe596c6dee1b91b3a05ece9d5cfd2631afebf6c6fadd", "d3ee0b035816e0520fac928de31b6572106f0d75597f6fa3206969a02baba06f", "d508875793efdf6bab3d47850df8f40d4040ae9928d9d80864c1768d6aeaf8e3", "ef0b828a7e22e58e06a1cceddba7b4665c6af8afeb22a0d8083001330572c147", "faad39fdbe2c2ccda9846cd21581063086330efafa47d87afea4073a08128656"]
-secretstorage = ["20c797ae48a4419f66f8d28fc221623f11fc45b6828f96bdb1ad9990acb59f92", "7a119fb52a88e398dbb22a4b3eb39b779bfbace7e4153b7bc6e5954d86282a8a"]
-six = ["1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", "30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"]
-toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"]
-tornado = ["349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c", "398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60", "4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281", "559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5", "abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7", "c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9", "c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5"]
-typed-ast = ["1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161", "18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", "262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", "2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", "354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", "48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47", "4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", "630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", "66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", "71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", "7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2", "838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e", "95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", "bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", "cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", "d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", "d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", "d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", "fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", "ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"]
-tzlocal = ["4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e"]
-zipp = ["3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", "f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"]
+[metadata.files]
+ansiwrap = [
+ {file = "ansiwrap-0.8.4-py2.py3-none-any.whl", hash = "sha256:7b053567c88e1ad9eed030d3ac41b722125e4c1271c8a99ade797faff1f49fb1"},
+ {file = "ansiwrap-0.8.4.zip", hash = "sha256:ca0c740734cde59bf919f8ff2c386f74f9a369818cdc60efe94893d01ea8d9b7"},
+]
+appdirs = [
+ {file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"},
+ {file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"},
+]
+asteval = [
+ {file = "asteval-0.9.18.tar.gz", hash = "sha256:5d64e18b8a72c2c7ae8f9b70d1f80b68bbcaa98c1c0d7047c35489d03209bc86"},
+]
+attrs = [
+ {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"},
+ {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"},
+]
+behave = [
+ {file = "behave-1.2.6-py2.py3-none-any.whl", hash = "sha256:ebda1a6c9e5bfe95c5f9f0a2794e01c7098b3dde86c10a95d8621c5907ff6f1c"},
+ {file = "behave-1.2.6.tar.gz", hash = "sha256:b9662327aa53294c1351b0a9c369093ccec1d21026f050c3bd9b3e5cccf81a86"},
+]
+black = [
+ {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"},
+ {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"},
+]
+cffi = [
+ {file = "cffi-1.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384"},
+ {file = "cffi-1.14.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30"},
+ {file = "cffi-1.14.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"},
+ {file = "cffi-1.14.0-cp27-cp27m-win32.whl", hash = "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78"},
+ {file = "cffi-1.14.0-cp27-cp27m-win_amd64.whl", hash = "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793"},
+ {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e"},
+ {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a"},
+ {file = "cffi-1.14.0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff"},
+ {file = "cffi-1.14.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f"},
+ {file = "cffi-1.14.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa"},
+ {file = "cffi-1.14.0-cp35-cp35m-win32.whl", hash = "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5"},
+ {file = "cffi-1.14.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4"},
+ {file = "cffi-1.14.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d"},
+ {file = "cffi-1.14.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc"},
+ {file = "cffi-1.14.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac"},
+ {file = "cffi-1.14.0-cp36-cp36m-win32.whl", hash = "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f"},
+ {file = "cffi-1.14.0-cp36-cp36m-win_amd64.whl", hash = "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b"},
+ {file = "cffi-1.14.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3"},
+ {file = "cffi-1.14.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66"},
+ {file = "cffi-1.14.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0"},
+ {file = "cffi-1.14.0-cp37-cp37m-win32.whl", hash = "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f"},
+ {file = "cffi-1.14.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26"},
+ {file = "cffi-1.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd"},
+ {file = "cffi-1.14.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55"},
+ {file = "cffi-1.14.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2"},
+ {file = "cffi-1.14.0-cp38-cp38-win32.whl", hash = "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8"},
+ {file = "cffi-1.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b"},
+ {file = "cffi-1.14.0.tar.gz", hash = "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6"},
+]
+click = [
+ {file = "Click-7.0-py2.py3-none-any.whl", hash = "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13"},
+ {file = "Click-7.0.tar.gz", hash = "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"},
+]
+colorama = [
+ {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
+ {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
+]
+cryptography = [
+ {file = "cryptography-2.8-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"},
+ {file = "cryptography-2.8-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2"},
+ {file = "cryptography-2.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad"},
+ {file = "cryptography-2.8-cp27-cp27m-win32.whl", hash = "sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2"},
+ {file = "cryptography-2.8-cp27-cp27m-win_amd64.whl", hash = "sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912"},
+ {file = "cryptography-2.8-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d"},
+ {file = "cryptography-2.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42"},
+ {file = "cryptography-2.8-cp34-abi3-macosx_10_6_intel.whl", hash = "sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879"},
+ {file = "cryptography-2.8-cp34-abi3-manylinux1_x86_64.whl", hash = "sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d"},
+ {file = "cryptography-2.8-cp34-abi3-manylinux2010_x86_64.whl", hash = "sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9"},
+ {file = "cryptography-2.8-cp34-cp34m-win32.whl", hash = "sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c"},
+ {file = "cryptography-2.8-cp34-cp34m-win_amd64.whl", hash = "sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0"},
+ {file = "cryptography-2.8-cp35-cp35m-win32.whl", hash = "sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf"},
+ {file = "cryptography-2.8-cp35-cp35m-win_amd64.whl", hash = "sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793"},
+ {file = "cryptography-2.8-cp36-cp36m-win32.whl", hash = "sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595"},
+ {file = "cryptography-2.8-cp36-cp36m-win_amd64.whl", hash = "sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7"},
+ {file = "cryptography-2.8-cp37-cp37m-win32.whl", hash = "sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff"},
+ {file = "cryptography-2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f"},
+ {file = "cryptography-2.8-cp38-cp38-win32.whl", hash = "sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e"},
+ {file = "cryptography-2.8-cp38-cp38-win_amd64.whl", hash = "sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13"},
+ {file = "cryptography-2.8.tar.gz", hash = "sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651"},
+]
+entrypoints = [
+ {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"},
+ {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"},
+]
+flake8 = [
+ {file = "flake8-3.7.9-py2.py3-none-any.whl", hash = "sha256:49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca"},
+ {file = "flake8-3.7.9.tar.gz", hash = "sha256:45681a117ecc81e870cbf1262835ae4af5e7a8b08e40b944a8a6e6b895914cfb"},
+]
+future = [
+ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
+]
+importlib-metadata = [
+ {file = "importlib_metadata-1.5.0-py2.py3-none-any.whl", hash = "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b"},
+ {file = "importlib_metadata-1.5.0.tar.gz", hash = "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302"},
+]
+jeepney = [
+ {file = "jeepney-0.4.2-py3-none-any.whl", hash = "sha256:6f45dce1125cf6c58a1c88123d3831f36a789f9204fbad3172eac15f8ccd08d0"},
+ {file = "jeepney-0.4.2.tar.gz", hash = "sha256:0ba6d8c597e9bef1ebd18aaec595f942a264e25c1a48f164d46120eacaa2e9bb"},
+]
+jinja2 = [
+ {file = "Jinja2-2.11.1-py2.py3-none-any.whl", hash = "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"},
+ {file = "Jinja2-2.11.1.tar.gz", hash = "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250"},
+]
+keyring = [
+ {file = "keyring-19.3.0-py2.py3-none-any.whl", hash = "sha256:9b80469783d3f6106bce1d389c6b8b20c8d4d739943b1b8cd0ddc2a45d065f9d"},
+ {file = "keyring-19.3.0.tar.gz", hash = "sha256:ee3d35b7f1ac3cb69e9a1e4323534649d3ab2fea402738a77e4250c152970fed"},
+]
+livereload = [
+ {file = "livereload-2.6.1-py2.py3-none-any.whl", hash = "sha256:78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b"},
+ {file = "livereload-2.6.1.tar.gz", hash = "sha256:89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66"},
+]
+lunr = [
+ {file = "lunr-0.5.6-py2.py3-none-any.whl", hash = "sha256:1208622930c915a07e6f8e8640474357826bad48534c0f57969b6fca9bffc88e"},
+ {file = "lunr-0.5.6.tar.gz", hash = "sha256:7be69d7186f65784a4f2adf81e5c58efd6a9921aa95966babcb1f2f2ada75c20"},
+]
+markdown = [
+ {file = "Markdown-3.2.1-py2.py3-none-any.whl", hash = "sha256:e4795399163109457d4c5af2183fbe6b60326c17cfdf25ce6e7474c6624f725d"},
+ {file = "Markdown-3.2.1.tar.gz", hash = "sha256:90fee683eeabe1a92e149f7ba74e5ccdc81cd397bd6c516d93a8da0ef90b6902"},
+]
+markupsafe = [
+ {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
+ {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
+ {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"},
+ {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"},
+ {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"},
+ {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"},
+ {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"},
+ {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"},
+ {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"},
+ {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"},
+ {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"},
+ {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"},
+ {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"},
+ {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"},
+ {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"},
+ {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"},
+ {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"},
+ {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"},
+ {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"},
+ {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"},
+ {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"},
+ {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"},
+ {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"},
+ {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"},
+ {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
+ {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
+ {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
+ {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
+]
+mccabe = [
+ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
+ {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
+]
+mkdocs = [
+ {file = "mkdocs-1.1-py2.py3-none-any.whl", hash = "sha256:1e385a70aea8a9dedb731aea4fd5f3704b2074801c4f96f06b2920999babda8a"},
+ {file = "mkdocs-1.1.tar.gz", hash = "sha256:9243291392f59e20b655e4e46210233453faf97787c2cf72176510e868143174"},
+]
+nltk = [
+ {file = "nltk-3.4.5.win32.exe", hash = "sha256:a08bdb4b8a1c13de16743068d9eb61c8c71c2e5d642e8e08205c528035843f82"},
+ {file = "nltk-3.4.5.zip", hash = "sha256:bed45551259aa2101381bbdd5df37d44ca2669c5c3dad72439fa459b29137d94"},
+]
+parse = [
+ {file = "parse-1.14.0.tar.gz", hash = "sha256:95a4f4469e37c57b5e924629ac99926f28bee7da59515dc5b8078c4c3e779249"},
+]
+parse-type = [
+ {file = "parse_type-0.5.2-py2.py3-none-any.whl", hash = "sha256:089a471b06327103865dfec2dd844230c3c658a4a1b5b4c8b6c16c8f77577f9e"},
+ {file = "parse_type-0.5.2.tar.gz", hash = "sha256:7f690b18d35048c15438d6d0571f9045cffbec5907e0b1ccf006f889e3a38c0b"},
+]
+parsedatetime = [
+ {file = "parsedatetime-2.5-py2-none-any.whl", hash = "sha256:3b835fc54e472c17ef447be37458b400e3fefdf14bb1ffdedb5d2c853acf4ba1"},
+ {file = "parsedatetime-2.5.tar.gz", hash = "sha256:d2e9ddb1e463de871d32088a3f3cea3dc8282b1b2800e081bd0ef86900451667"},
+]
+passlib = [
+ {file = "passlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:68c35c98a7968850e17f1b6892720764cc7eed0ef2b7cb3116a89a28e43fe177"},
+ {file = "passlib-1.7.2.tar.gz", hash = "sha256:8d666cef936198bc2ab47ee9b0410c94adf2ba798e5a84bf220be079ae7ab6a8"},
+]
+pathspec = [
+ {file = "pathspec-0.7.0-py2.py3-none-any.whl", hash = "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424"},
+ {file = "pathspec-0.7.0.tar.gz", hash = "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"},
+]
+pycodestyle = [
+ {file = "pycodestyle-2.5.0-py2.py3-none-any.whl", hash = "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56"},
+ {file = "pycodestyle-2.5.0.tar.gz", hash = "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"},
+]
+pycparser = [
+ {file = "pycparser-2.19.tar.gz", hash = "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"},
+]
+pyflakes = [
+ {file = "pyflakes-2.1.1-py2.py3-none-any.whl", hash = "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0"},
+ {file = "pyflakes-2.1.1.tar.gz", hash = "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"},
+]
+python-dateutil = [
+ {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
+ {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"},
+]
+pytz = [
+ {file = "pytz-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"},
+ {file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"},
+]
+pywin32-ctypes = [
+ {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"},
+ {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"},
+]
+pyxdg = [
+ {file = "pyxdg-0.26-py2.py3-none-any.whl", hash = "sha256:1948ff8e2db02156c0cccd2529b43c0cff56ebaa71f5f021bbd755bc1419190e"},
+ {file = "pyxdg-0.26.tar.gz", hash = "sha256:fe2928d3f532ed32b39c32a482b54136fe766d19936afc96c8f00645f9da1a06"},
+]
+pyyaml = [
+ {file = "PyYAML-5.3-cp27-cp27m-win32.whl", hash = "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d"},
+ {file = "PyYAML-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6"},
+ {file = "PyYAML-5.3-cp35-cp35m-win32.whl", hash = "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e"},
+ {file = "PyYAML-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689"},
+ {file = "PyYAML-5.3-cp36-cp36m-win32.whl", hash = "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994"},
+ {file = "PyYAML-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e"},
+ {file = "PyYAML-5.3-cp37-cp37m-win32.whl", hash = "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5"},
+ {file = "PyYAML-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf"},
+ {file = "PyYAML-5.3-cp38-cp38-win32.whl", hash = "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811"},
+ {file = "PyYAML-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20"},
+ {file = "PyYAML-5.3.tar.gz", hash = "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"},
+]
+regex = [
+ {file = "regex-2020.2.20-cp27-cp27m-win32.whl", hash = "sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb"},
+ {file = "regex-2020.2.20-cp27-cp27m-win_amd64.whl", hash = "sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74"},
+ {file = "regex-2020.2.20-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400"},
+ {file = "regex-2020.2.20-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0"},
+ {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc"},
+ {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"},
+ {file = "regex-2020.2.20-cp36-cp36m-win32.whl", hash = "sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69"},
+ {file = "regex-2020.2.20-cp36-cp36m-win_amd64.whl", hash = "sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b"},
+ {file = "regex-2020.2.20-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e"},
+ {file = "regex-2020.2.20-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242"},
+ {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce"},
+ {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab"},
+ {file = "regex-2020.2.20-cp37-cp37m-win32.whl", hash = "sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431"},
+ {file = "regex-2020.2.20-cp37-cp37m-win_amd64.whl", hash = "sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1"},
+ {file = "regex-2020.2.20-cp38-cp38-manylinux1_i686.whl", hash = "sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045"},
+ {file = "regex-2020.2.20-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26"},
+ {file = "regex-2020.2.20-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2"},
+ {file = "regex-2020.2.20-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70"},
+ {file = "regex-2020.2.20-cp38-cp38-win32.whl", hash = "sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d"},
+ {file = "regex-2020.2.20-cp38-cp38-win_amd64.whl", hash = "sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa"},
+ {file = "regex-2020.2.20.tar.gz", hash = "sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5"},
+]
+secretstorage = [
+ {file = "SecretStorage-3.1.2-py3-none-any.whl", hash = "sha256:b5ec909dde94d4ae2fa26af7c089036997030f0cf0a5cb372b4cccabd81c143b"},
+ {file = "SecretStorage-3.1.2.tar.gz", hash = "sha256:15da8a989b65498e29be338b3b279965f1b8f09b9668bd8010da183024c8bff6"},
+]
+six = [
+ {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"},
+ {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"},
+]
+textwrap3 = [
+ {file = "textwrap3-0.9.2-py2.py3-none-any.whl", hash = "sha256:bf5f4c40faf2a9ff00a9e0791fed5da7415481054cef45bb4a3cfb1f69044ae0"},
+ {file = "textwrap3-0.9.2.zip", hash = "sha256:5008eeebdb236f6303dcd68f18b856d355f6197511d952ba74bc75e40e0c3414"},
+]
+toml = [
+ {file = "toml-0.10.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"},
+ {file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"},
+ {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"},
+]
+tornado = [
+ {file = "tornado-6.0.3-cp35-cp35m-win32.whl", hash = "sha256:c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5"},
+ {file = "tornado-6.0.3-cp35-cp35m-win_amd64.whl", hash = "sha256:398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60"},
+ {file = "tornado-6.0.3-cp36-cp36m-win32.whl", hash = "sha256:4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281"},
+ {file = "tornado-6.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c"},
+ {file = "tornado-6.0.3-cp37-cp37m-win32.whl", hash = "sha256:559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5"},
+ {file = "tornado-6.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7"},
+ {file = "tornado-6.0.3.tar.gz", hash = "sha256:c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9"},
+]
+typed-ast = [
+ {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"},
+ {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"},
+ {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"},
+ {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"},
+ {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"},
+ {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"},
+ {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"},
+ {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"},
+ {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"},
+ {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"},
+ {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"},
+ {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"},
+ {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"},
+ {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"},
+ {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"},
+ {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"},
+ {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"},
+ {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"},
+ {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"},
+ {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"},
+ {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"},
+]
+tzlocal = [
+ {file = "tzlocal-1.5.1.tar.gz", hash = "sha256:4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e"},
+]
+zipp = [
+ {file = "zipp-3.0.0-py3-none-any.whl", hash = "sha256:12248a63bbdf7548f89cb4c7cda4681e537031eda29c02ea29674bc6854460c2"},
+ {file = "zipp-3.0.0.tar.gz", hash = "sha256:7c0f8e91abc0dc07a5068f315c52cb30c66bfbc581e5b50704c8a2f6ebae794a"},
+]
diff --git a/pyproject.toml b/pyproject.toml
index 9dc76df4..d1d48168 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,9 +25,10 @@ keyring = "^19.0"
pytz = "^2019.1"
tzlocal = "^1.5"
asteval = "^0.9.14"
-colorama = {version = "^0.4.1",platform = "win32"}
+colorama = "^0.4.1"
python-dateutil = "^2.8"
pyyaml = "^5.1"
+ansiwrap = "^0.8.4"
[tool.poetry.dev-dependencies]
behave = "^1.2"
@@ -38,3 +39,7 @@ black = {version = "^19.10b0",allow-prereleases = true}
[tool.poetry.scripts]
jrnl = 'jrnl.cli:run'
+[build-system]
+requires = ["poetry>=0.12"]
+build-backend = "poetry.masonry.api"
+