diff --git a/CHANGELOG.md b/CHANGELOG.md index 61bb664c..a99cb1d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [Unreleased](https://github.com/jrnl-org/jrnl/) + +[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v3.0...HEAD) + +**Implemented enhancements:** + +- Create an environment variable when jrnl is called [\#1537](https://github.com/jrnl-org/jrnl/issues/1537) +- Warn user when there are duplicate keys in the config file [\#1415](https://github.com/jrnl-org/jrnl/issues/1415) + +**Packaging:** + +- Bump keyring from 23.6.0 to 23.7.0 [\#1539](https://github.com/jrnl-org/jrnl/pull/1539) ([dependabot[bot]](https://github.com/apps/dependabot)) + ## [v3.0](https://pypi.org/project/jrnl/v3.0/) (2022-07-09) [Full Changelog](https://github.com/jrnl-org/jrnl/compare/v3.0-beta2...v3.0) diff --git a/jrnl/config.py b/jrnl/config.py index 045958c1..cea3720e 100644 --- a/jrnl/config.py +++ b/jrnl/config.py @@ -7,6 +7,7 @@ import os import colorama import xdg.BaseDirectory from ruamel.yaml import YAML +from ruamel.yaml import constructor from jrnl import __version__ from jrnl.exception import JrnlException @@ -159,10 +160,25 @@ def verify_config_colors(config): def load_config(config_path): """Tries to load a config file from YAML.""" - with open(config_path, encoding=YAML_FILE_ENCODING) as f: - yaml = YAML(typ="safe") - yaml.allow_duplicate_keys = True - return yaml.load(f) + try: + with open(config_path, encoding=YAML_FILE_ENCODING) as f: + yaml = YAML(typ="safe") + yaml.allow_duplicate_keys = False + return yaml.load(f) + except constructor.DuplicateKeyError as e: + print_msg( + Message( + MsgText.ConfigDoubleKeys, + MsgStyle.WARNING, + { + "error_message": e, + }, + ) + ) + with open(config_path, encoding=YAML_FILE_ENCODING) as f: + yaml = YAML(typ="safe") + yaml.allow_duplicate_keys = True + return yaml.load(f) def is_config_json(config_path): diff --git a/jrnl/messages/MsgText.py b/jrnl/messages/MsgText.py index d26f1dfd..b8250970 100644 --- a/jrnl/messages/MsgText.py +++ b/jrnl/messages/MsgText.py @@ -198,6 +198,13 @@ class MsgText(Enum): Configuration updated to newest version at {config_path} """ + ConfigDoubleKeys = """ + There is at least one duplicate key in your configuration file. + + Details: + {error_message} + """ + # --- Password --- # Password = "Password:" PasswordFirstEntry = "Enter password for journal '{journal_name}': " diff --git a/jrnl/output.py b/jrnl/output.py index 78a1da24..78765ca9 100644 --- a/jrnl/output.py +++ b/jrnl/output.py @@ -89,5 +89,9 @@ def _add_extra_style_args_if_needed(args, msg): def format_msg_text(msg: Message) -> Text: - text = textwrap.dedent(msg.text.value.format(**msg.params)).strip() + text = textwrap.dedent(msg.text.value) + text = text.format(**msg.params) + # dedent again in case inserted text needs it + text = textwrap.dedent(text) + text = text.strip() return Text(text) diff --git a/poetry.lock b/poetry.lock index b355ecc5..63cebaaf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -52,17 +52,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "21.4.0" +version = "22.1.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] [[package]] name = "backcall" @@ -164,7 +164,7 @@ python-versions = ">=3.5" [[package]] name = "distlib" -version = "0.3.4" +version = "0.3.5" description = "Distribution utilities" category = "dev" optional = false @@ -183,7 +183,7 @@ testing = ["pre-commit"] [[package]] name = "executing" -version = "0.8.3" +version = "0.9.1" description = "Get the currently executing AST node of a frame, and other information" category = "dev" optional = false @@ -365,7 +365,7 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "keyring" -version = "23.6.0" +version = "23.7.0" description = "Store and access your passwords safely." category = "main" optional = false @@ -379,7 +379,7 @@ SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [[package]] name = "mako" @@ -399,11 +399,11 @@ testing = ["pytest"] [[package]] name = "markdown" -version = "3.3.7" +version = "3.4.1" description = "Python implementation of Markdown." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} @@ -593,7 +593,7 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "poethepoet" -version = "0.15.0" +version = "0.16.0" description = "A task runner that works well with poetry." category = "dev" optional = false @@ -846,7 +846,7 @@ pyyaml = "*" [[package]] name = "rich" -version = "12.4.4" +version = "12.5.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" category = "main" optional = false @@ -1009,21 +1009,20 @@ test = ["pytest-mock (>=3.3)", "pytest (>=4.3)"] [[package]] name = "virtualenv" -version = "20.15.1" +version = "20.16.2" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] distlib = ">=0.3.1,<1" filelock = ">=3.2,<4" platformdirs = ">=2,<3" -six = ">=1.9.0,<2" [package.extras] docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] [[package]] name = "watchdog" @@ -1054,7 +1053,7 @@ python-versions = ">=3.4" [[package]] name = "yq" -version = "3.0.2" +version = "3.1.0" description = "Command-line YAML/XML processor - jq wrapper for YAML/XML documents" category = "dev" optional = false @@ -1071,20 +1070,20 @@ tests = ["coverage", "flake8", "wheel"] [[package]] name = "zipp" -version = "3.8.0" +version = "3.8.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" python-versions = ">=3.9.0, <3.12" -content-hash = "713557f531c0f78496d6fc70335267fa2ee9a2b2e6eff0c21aabd9ce728a0760" +content-hash = "5f5954af39ca238b840a95e932999bcb1ead3b8d403d0d18e0a89806ddcc59f9" [metadata.files] ansiwrap = [ @@ -1104,10 +1103,7 @@ asttokens = [ {file = "asttokens-2.0.5.tar.gz", hash = "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5"}, ] atomicwrites = [] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] +attrs = [] backcall = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, @@ -1131,18 +1127,12 @@ decorator = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] -distlib = [ - {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, - {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, -] +distlib = [] execnet = [ {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, ] -executing = [ - {file = "executing-0.8.3-py2.py3-none-any.whl", hash = "sha256:d1eef132db1b83649a3905ca6dd8897f71ac6f8cac79a7e58a1a09cf137546c9"}, - {file = "executing-0.8.3.tar.gz", hash = "sha256:c6554e21c6b060590a6d3be4b82fb78f8f0194d809de5ea7df1c093763311501"}, -] +executing = [] filelock = [ {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, @@ -1189,15 +1179,9 @@ jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] -keyring = [ - {file = "keyring-23.6.0-py3-none-any.whl", hash = "sha256:372ff2fc43ab779e3f87911c26e6c7acc8bb440cbd82683e383ca37594cb0617"}, - {file = "keyring-23.6.0.tar.gz", hash = "sha256:3ac00c26e4c93739e19103091a9986a9f79665a78cf15a4df1dba7ea9ac8da2f"}, -] +keyring = [] mako = [] -markdown = [ - {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, - {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, -] +markdown = [] markupsafe = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, @@ -1416,10 +1400,7 @@ pyyaml-env-tag = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] -rich = [ - {file = "rich-12.4.4-py3-none-any.whl", hash = "sha256:d2bbd99c320a2532ac71ff6a3164867884357da3e3301f0240090c5d2fdac7ec"}, - {file = "rich-12.4.4.tar.gz", hash = "sha256:4c586de507202505346f3e32d1363eb9ed6932f0c2f63184dea88983ff4971e2"}, -] +rich = [] "ruamel.yaml" = [ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, @@ -1520,7 +1501,4 @@ xmltodict = [ {file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"}, ] yq = [] -zipp = [ - {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, - {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, -] +zipp = [] diff --git a/tests/bdd/features/config_file.feature b/tests/bdd/features/config_file.feature index 76e93059..d6b6121f 100644 --- a/tests/bdd/features/config_file.feature +++ b/tests/bdd/features/config_file.feature @@ -106,3 +106,20 @@ Feature: Multiple journals And we use the config "basic_onefile.yaml" When we run "jrnl --cf empty_file.yaml" Then the error output should contain "Unable to parse config file" + + Scenario: Show a warning message when the config file contains duplicate keys at the same level + Given the config "duplicate_keys.yaml" exists + And we use the config "duplicate_keys.yaml" + When we run "jrnl -1" + Then the output should contain "There is at least one duplicate key in your configuration file" + + Scenario: Show a warning message when using --config-file with duplicate keys + Given the config "duplicate_keys.yaml" exists + And we use the config "multiple.yaml" + When we run "jrnl --cf duplicate_keys.yaml -1" + Then the output should contain "There is at least one duplicate key in your configuration file" + + Scenario: Don't show a duplicate keys warning message when using --config-override on an existing value + Given we use the config "multiple.yaml" + When we run "jrnl --config-override highlight false" + Then the output should not contain "There is at least one duplicate key in your configuration file" \ No newline at end of file diff --git a/tests/data/configs/duplicate_keys.yaml b/tests/data/configs/duplicate_keys.yaml new file mode 100644 index 00000000..ca74c738 --- /dev/null +++ b/tests/data/configs/duplicate_keys.yaml @@ -0,0 +1,27 @@ +default_hour: 9 +default_minute: 0 +editor: '' +encrypt: false +highlight: true +template: false +template: false +journals: + default: + encrypt: false + journal: features/journals/simple.journal + journal: features/journals/simple.journal + ideas: + encrypt: false + journal: features/journals/does-not-exist.journal + simple: + encrypt: false + journal: features/journals/simple.journal + encrypt: false + work: + encrypt: false + journal: features/journals/work.journal +linewrap: 80 +tagsymbols: '@' +editor: nano +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|"