diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml index 0c40ce8f..4825bffe 100644 --- a/.github/workflows/changelog.yaml +++ b/.github/workflows/changelog.yaml @@ -59,7 +59,7 @@ jobs: if [[ "$(git rev-parse "origin/$BRANCH")" != "$GITHUB_SHA" ]]; then # Normal build on a branch (no tag) - echo "::debug::BRANCH: $BRANCH $(git rev-parse origin/$BRANCH)" + echo "::debug::BRANCH: $BRANCH $(git rev-parse "origin/$BRANCH")" echo "::debug::GITHUB_SHA: $GITHUB_SHA" echo "::error::$BRANCH has been updated since build started. Aborting changelog." exit 1 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 82d9b8a7..67885911 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -200,7 +200,7 @@ jobs: --force - name: Create Pull Request - uses: peter-evans/create-pull-request@v4 + uses: peter-evans/create-pull-request@v5 with: path: ${{ env.BREW_TAP_DIRECTORY }} token: ${{ secrets.JRNL_BOT_TOKEN }} diff --git a/.github/workflows/testing_pipelines.yaml b/.github/workflows/testing_pipelines.yaml index 46b353e2..0239bca5 100644 --- a/.github/workflows/testing_pipelines.yaml +++ b/.github/workflows/testing_pipelines.yaml @@ -14,6 +14,8 @@ on: paths: - '.github/workflows/**' - '.github/actions/**' + schedule: + - cron: '0 0 * * SAT' jobs: test: diff --git a/.gitignore b/.gitignore index 65018206..938692b0 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ var/ node_modules/ __pycache__/ .pytest_cache/ +.flakeheaven_cache/ # Versioning .python-version @@ -39,7 +40,7 @@ exp/ objects.inv searchindex.js -# virtaulenv +# virtualenv .venv*/ env/ env*/ diff --git a/CHANGELOG.md b/CHANGELOG.md index f94e58e2..ca0d0db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,45 +1,71 @@ # Changelog -## [v4.0-beta3](https://pypi.org/project/jrnl/v4.0-beta3/) (2023-04-29) +## [Unreleased](https://github.com/jrnl-org/jrnl/) -[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v4.0-beta2...v4.0-beta3) +[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v4.0.1...HEAD) **Fixed bugs:** -- jrnl reads extraneous text files when reading folder journal [\#1692](https://github.com/jrnl-org/jrnl/issues/1692) -- jrnl crashes when adding tag argument after `--change-time` [\#1644](https://github.com/jrnl-org/jrnl/issues/1644) -- Only read text files that look like entries when opening folder journal [\#1697](https://github.com/jrnl-org/jrnl/pull/1697) ([micahellison](https://github.com/micahellison)) +- Linting rules aren't enforced the same as format rules [\#1742](https://github.com/jrnl-org/jrnl/issues/1742) + +**Build:** + +- Replace flake8 and isort with ruff linter and add `black --check` to linting step [\#1763](https://github.com/jrnl-org/jrnl/pull/1763) ([micahellison](https://github.com/micahellison)) **Documentation:** -- Update contributing.md links in documentation [\#1726](https://github.com/jrnl-org/jrnl/pull/1726) ([ahosking](https://github.com/ahosking)) - -## [v4.0-beta2](https://pypi.org/project/jrnl/v4.0-beta2/) (2023-04-22) - -[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v4.0-beta...v4.0-beta2) - -**Documentation:** - -- Fix various typos [\#1718](https://github.com/jrnl-org/jrnl/pull/1718) ([hezhizhen](https://github.com/hezhizhen)) +- Add note about messages going to `stderr` and the implication for piping [\#1768](https://github.com/jrnl-org/jrnl/pull/1768) ([micahellison](https://github.com/micahellison)) **Packaging:** -- Update dependency cryptography to v40.0.2 [\#1723](https://github.com/jrnl-org/jrnl/pull/1723) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency flakeheaven to v3.3.0 [\#1722](https://github.com/jrnl-org/jrnl/pull/1722) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency pytest to v7.3.1 [\#1720](https://github.com/jrnl-org/jrnl/pull/1720) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency black to v23.3.0 [\#1715](https://github.com/jrnl-org/jrnl/pull/1715) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency flake8-type-checking to v2.4.0 [\#1714](https://github.com/jrnl-org/jrnl/pull/1714) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency rich to v13.3.4 [\#1713](https://github.com/jrnl-org/jrnl/pull/1713) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency tox to v4.4.12 [\#1712](https://github.com/jrnl-org/jrnl/pull/1712) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency keyring to v24.2.0 [\#1760](https://github.com/jrnl-org/jrnl/pull/1760) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency keyring to v24 [\#1758](https://github.com/jrnl-org/jrnl/pull/1758) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency pytest to v7.4.0 [\#1757](https://github.com/jrnl-org/jrnl/pull/1757) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency rich to v13.4.2 [\#1754](https://github.com/jrnl-org/jrnl/pull/1754) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency cryptography to v41 [\#1753](https://github.com/jrnl-org/jrnl/pull/1753) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency ruamel.yaml to v0.17.32 [\#1752](https://github.com/jrnl-org/jrnl/pull/1752) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency tox to v4.6.3 [\#1751](https://github.com/jrnl-org/jrnl/pull/1751) ([renovate[bot]](https://github.com/apps/renovate)) -## [v4.0-beta](https://pypi.org/project/jrnl/v4.0-beta/) (2023-03-25) +## [v4.0.1](https://pypi.org/project/jrnl/v4.0.1/) (2023-06-20) -[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v3.3...v4.0-beta) +[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v4.0.1-beta...v4.0.1) + +**Fixed bugs:** + +- jrnl crashes when running `jrnl --list --format json` and `jrnl --list --format yaml` [\#1737](https://github.com/jrnl-org/jrnl/issues/1737) +- Refactor --template code [\#1711](https://github.com/jrnl-org/jrnl/pull/1711) ([micahellison](https://github.com/micahellison)) + +**Build:** + +- Fix linting issue in CI pipeline [\#1743](https://github.com/jrnl-org/jrnl/pull/1743) ([wren](https://github.com/wren)) + +**Packaging:** + +- Update dependency ruamel.yaml to v0.17.28 [\#1749](https://github.com/jrnl-org/jrnl/pull/1749) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency requests to v2.31.0 [\#1748](https://github.com/jrnl-org/jrnl/pull/1748) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency ruamel.yaml to v0.17.26 [\#1746](https://github.com/jrnl-org/jrnl/pull/1746) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency tzlocal to v5 [\#1741](https://github.com/jrnl-org/jrnl/pull/1741) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency pytest-xdist to v3.3.1 [\#1740](https://github.com/jrnl-org/jrnl/pull/1740) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency poethepoet to v0.20.0 [\#1735](https://github.com/jrnl-org/jrnl/pull/1735) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency mkdocs to v1.4.3 [\#1733](https://github.com/jrnl-org/jrnl/pull/1733) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency rich to v13.3.5 [\#1729](https://github.com/jrnl-org/jrnl/pull/1729) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency requests to v2.30.0 [\#1728](https://github.com/jrnl-org/jrnl/pull/1728) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency tox to v4.5.1 [\#1727](https://github.com/jrnl-org/jrnl/pull/1727) ([renovate[bot]](https://github.com/apps/renovate)) +- Update peter-evans/create-pull-request action to v5 [\#1719](https://github.com/jrnl-org/jrnl/pull/1719) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency flake8-simplify to v0.20.0 [\#1716](https://github.com/jrnl-org/jrnl/pull/1716) ([renovate[bot]](https://github.com/apps/renovate)) + +## [v4.0](https://pypi.org/project/jrnl/v4.0/) (2023-05-20) + +[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v4.0-beta3...v4.0) + +🚨 **BREAKING CHANGES** 🚨 + +**Deprecated:** + +- Drop Python 3.9 and use Python 3.11 official release [\#1611](https://github.com/jrnl-org/jrnl/pull/1611) ([micahellison](https://github.com/micahellison)) **Implemented enhancements:** -- Display locations of config file and documentation after initial install [\#1694](https://github.com/jrnl-org/jrnl/issues/1694) -- Don't import cryptography package if not needed [\#1521](https://github.com/jrnl-org/jrnl/issues/1521) - Add message with config location and docs location when installation is complete [\#1695](https://github.com/jrnl-org/jrnl/pull/1695) ([micahellison](https://github.com/micahellison)) - Prompt to include colors in config when first running jrnl [\#1687](https://github.com/jrnl-org/jrnl/pull/1687) ([micahellison](https://github.com/micahellison)) - Add ability to use template with `--template` [\#1667](https://github.com/jrnl-org/jrnl/pull/1667) ([alichtman](https://github.com/alichtman)) @@ -51,25 +77,15 @@ **Fixed bugs:** -- Combinations of `--change-time`, `--delete`, and `--edit` don't work consistently [\#1696](https://github.com/jrnl-org/jrnl/issues/1696) -- jrnl doesn't display count of entries deleted after deleting entries with `--delete` [\#1666](https://github.com/jrnl-org/jrnl/issues/1666) -- Templated entries should not be saved if the raw text is identical to the original template [\#1652](https://github.com/jrnl-org/jrnl/issues/1652) -- Adding an entry with a combination of flags causes a journal overwrite [\#1639](https://github.com/jrnl-org/jrnl/issues/1639) -- jrnl does not update version key in config file [\#1638](https://github.com/jrnl-org/jrnl/issues/1638) -- jrnl should not create 0-length "encrypted" file on startup [\#1493](https://github.com/jrnl-org/jrnl/issues/1493) +- Only read text files that look like entries when opening folder journal [\#1697](https://github.com/jrnl-org/jrnl/pull/1697) ([micahellison](https://github.com/micahellison)) - Save empty journal on install instead of just creating a zero-length file [\#1690](https://github.com/jrnl-org/jrnl/pull/1690) ([micahellison](https://github.com/micahellison)) - Allow combinations of `--change-time`, `--delete`, and `--edit` while correctly counting the number of entries affected [\#1669](https://github.com/jrnl-org/jrnl/pull/1669) ([wren](https://github.com/wren)) - Don't save templated journal entries if the received raw text is the same as the template itself [\#1653](https://github.com/jrnl-org/jrnl/pull/1653) ([Briscoooe](https://github.com/Briscoooe)) - Add tag to XML file when edited DayOne entry and is searchable afterward [\#1648](https://github.com/jrnl-org/jrnl/pull/1648) ([jonakeys](https://github.com/jonakeys)) - Update version key in config file after version changes [\#1646](https://github.com/jrnl-org/jrnl/pull/1646) ([jonakeys](https://github.com/jonakeys)) -**Deprecated:** - -- Drop Python 3.9 and use Python 3.11 official release [\#1611](https://github.com/jrnl-org/jrnl/pull/1611) ([micahellison](https://github.com/micahellison)) - **Build:** -- Support pytest-bdd 6 [\#1534](https://github.com/jrnl-org/jrnl/issues/1534) - Update copyright notices for 2023 [\#1660](https://github.com/jrnl-org/jrnl/pull/1660) ([wren](https://github.com/wren)) - Fix bug where changelog is always slightly out of date on release tags [\#1631](https://github.com/jrnl-org/jrnl/pull/1631) ([wren](https://github.com/wren)) - Add `simplify` plugin to linting checks [\#1630](https://github.com/jrnl-org/jrnl/pull/1630) ([wren](https://github.com/wren)) @@ -77,11 +93,8 @@ **Documentation:** -- Document template extension behavior [\#1677](https://github.com/jrnl-org/jrnl/issues/1677) -- Visual Studio Code may store unencrypted temporary files [\#1675](https://github.com/jrnl-org/jrnl/issues/1675) -- Document `-tagged`, `-not -tagged`, and `-not -starred` [\#1668](https://github.com/jrnl-org/jrnl/issues/1668) -- Documentation Change [\#1651](https://github.com/jrnl-org/jrnl/issues/1651) -- Update console examples on jrnl.sh front page [\#1622](https://github.com/jrnl-org/jrnl/issues/1622) +- Update contributing.md links in documentation [\#1726](https://github.com/jrnl-org/jrnl/pull/1726) ([ahosking](https://github.com/ahosking)) +- Fix various typos [\#1718](https://github.com/jrnl-org/jrnl/pull/1718) ([hezhizhen](https://github.com/hezhizhen)) - Update documentation front page text [\#1698](https://github.com/jrnl-org/jrnl/pull/1698) ([micahellison](https://github.com/micahellison)) - Support mkdocs 1.4.2 and fix its missing breadcrumb [\#1691](https://github.com/jrnl-org/jrnl/pull/1691) ([micahellison](https://github.com/micahellison)) - Document temporary file extension behavior when using template [\#1686](https://github.com/jrnl-org/jrnl/pull/1686) ([micahellison](https://github.com/micahellison)) @@ -94,17 +107,18 @@ **Packaging:** -- Update dependency cryptography to v40 [\#1710](https://github.com/jrnl-org/jrnl/pull/1710) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency poethepoet to v0.19.0 [\#1709](https://github.com/jrnl-org/jrnl/pull/1709) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency tzlocal to v4.3 [\#1708](https://github.com/jrnl-org/jrnl/pull/1708) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency tox to v4.4.7 [\#1707](https://github.com/jrnl-org/jrnl/pull/1707) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency rich to v13.3.2 [\#1706](https://github.com/jrnl-org/jrnl/pull/1706) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency pytest-xdist to v3.2.1 [\#1705](https://github.com/jrnl-org/jrnl/pull/1705) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency pytest to v7.2.2 [\#1704](https://github.com/jrnl-org/jrnl/pull/1704) ([renovate[bot]](https://github.com/apps/renovate)) +- Lock ruamel.yaml version to v0.17.21 until bug is fixed [\#1738](https://github.com/jrnl-org/jrnl/pull/1738) ([wren](https://github.com/wren)) +- Update dependency black to v23.3.0 [\#1715](https://github.com/jrnl-org/jrnl/pull/1715) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency cryptography to v40.0.2 [\#1723](https://github.com/jrnl-org/jrnl/pull/1723) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency flake8-type-checking to v2.4.0 [\#1714](https://github.com/jrnl-org/jrnl/pull/1714) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency flakeheaven to v3.3.0 [\#1722](https://github.com/jrnl-org/jrnl/pull/1722) ([renovate[bot]](https://github.com/apps/renovate)) - Update dependency ipdb to v0.13.13 [\#1703](https://github.com/jrnl-org/jrnl/pull/1703) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency flake8-type-checking to v2.3.1 [\#1702](https://github.com/jrnl-org/jrnl/pull/1702) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency cryptography to v39.0.2 [\#1701](https://github.com/jrnl-org/jrnl/pull/1701) ([renovate[bot]](https://github.com/apps/renovate)) -- Update dependency rich to v13 [\#1654](https://github.com/jrnl-org/jrnl/pull/1654) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency poethepoet to v0.19.0 [\#1709](https://github.com/jrnl-org/jrnl/pull/1709) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency pytest to v7.3.1 [\#1720](https://github.com/jrnl-org/jrnl/pull/1720) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency pytest-xdist to v3.2.1 [\#1705](https://github.com/jrnl-org/jrnl/pull/1705) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency rich to v13.3.4 [\#1713](https://github.com/jrnl-org/jrnl/pull/1713) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency tox to v4.4.7 [\#1707](https://github.com/jrnl-org/jrnl/pull/1707) ([renovate[bot]](https://github.com/apps/renovate)) +- Update dependency tzlocal to v4.3 [\#1708](https://github.com/jrnl-org/jrnl/pull/1708) ([renovate[bot]](https://github.com/apps/renovate)) ## [v3.3](https://pypi.org/project/jrnl/v3.3/) (2022-10-29) @@ -203,6 +217,10 @@ 🚨 **BREAKING CHANGES** 🚨 +**Deprecated:** + +- Drop support for Python 3.7 and 3.8 [\#1412](https://github.com/jrnl-org/jrnl/pull/1412) ([micahellison](https://github.com/micahellison)) + **Implemented enhancements:** - Show name of journal when creating a password/encrypting [\#1478](https://github.com/jrnl-org/jrnl/pull/1478) ([jonakeys](https://github.com/jonakeys)) @@ -225,10 +243,6 @@ - Display "No entry to save, because no text was received" after empty entry on cmdline [\#1459](https://github.com/jrnl-org/jrnl/pull/1459) ([apainintheneck](https://github.com/apainintheneck)) - Yaml export errors now don't show stack trace [\#1449](https://github.com/jrnl-org/jrnl/pull/1449) ([apainintheneck](https://github.com/apainintheneck)) -**Deprecated:** - -- Drop support for Python 3.7 and 3.8 [\#1412](https://github.com/jrnl-org/jrnl/pull/1412) ([micahellison](https://github.com/micahellison)) - **Build:** - Pin `pytest-bdd` to \<6.0 to temporarily avoid breaking changes [\#1536](https://github.com/jrnl-org/jrnl/pull/1536) ([wren](https://github.com/wren)) diff --git a/docs/formats.md b/docs/formats.md index e78194eb..520a197c 100644 --- a/docs/formats.md +++ b/docs/formats.md @@ -117,6 +117,11 @@ These formats are mainly intended for piping or exporting your journal to other programs. Even so, they can still be used in the same way as any other format (like written to a file, or displayed in your terminal, if you want). +!!! note +You may see boxed messages like "2 entries found" when using these formats, but +those messages are written to `stderr` instead of `stdout`, and won't be piped when +using the `|` operator. + ### JSON ``` sh diff --git a/jrnl/__version__.py b/jrnl/__version__.py index 6689f558..2da057a2 100644 --- a/jrnl/__version__.py +++ b/jrnl/__version__.py @@ -1 +1 @@ -__version__ = "v4.0-beta3" +__version__ = "v4.0.1" diff --git a/jrnl/args.py b/jrnl/args.py index 9b7dafe7..53a77462 100644 --- a/jrnl/args.py +++ b/jrnl/args.py @@ -78,7 +78,7 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: """ We gratefully thank all contributors! Come see the whole list of code and financial contributors at https://github.com/jrnl-org/jrnl - And special thanks to Bad Lip Reading for the Yoda joke in the Writing section above :)""" + And special thanks to Bad Lip Reading for the Yoda joke in the Writing section above :)""" # noqa: E501 ), ) @@ -214,7 +214,8 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: composing.add_argument( "--template", dest="template", - help="Path to template file. Can be a local path, absolute path, or a path relative to $XDG_DATA_HOME/jrnl/templates/", + help="Path to template file. Can be a local path, absolute path, or a path " + "relative to $XDG_DATA_HOME/jrnl/templates/", ) read_msg = ( @@ -265,13 +266,15 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: "-contains", dest="contains", metavar="TEXT", - help="Show entries containing specific text (put quotes around text with spaces)", + help="Show entries containing specific text (put quotes around text with " + "spaces)", ) reading.add_argument( "-and", dest="strict", action="store_true", - help='Show only entries that match all conditions, like saying "x AND y" (default: OR)', + help='Show only entries that match all conditions, like saying "x AND y" ' + "(default: OR)", ) reading.add_argument( "-starred", @@ -290,7 +293,8 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: dest="limit", default=None, metavar="NUMBER", - help="Show a maximum of NUMBER entries (note: '-n 3' and '-3' have the same effect)", + help="Show a maximum of NUMBER entries (note: '-n 3' and '-3' have the same " + "effect)", nargs="?", type=int, ) @@ -308,8 +312,12 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: ), ) - search_options_msg = """ These help you do various tasks with the selected entries from your search. - If used on their own (with no search), they will act on your entire journal""" + search_options_msg = ( + " " # Preserves indentation + """ + These help you do various tasks with the selected entries from your search. + If used on their own (with no search), they will act on your entire journal""" + ) exporting = parser.add_argument_group( "Searching Options", textwrap.dedent(search_options_msg) ) @@ -360,7 +368,8 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: "--tags", dest="tags", action="store_true", - help="Alias for '--format tags'. Returns a list of all tags and number of occurrences", + help="Alias for '--format tags'. Returns a list of all tags and number of " + "occurrences", ) exporting.add_argument( "--short", @@ -400,7 +409,7 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: \t jrnl --config-override editor "nano" \n \t - Override color selections\n \t jrnl --config-override colors.body blue --config-override colors.title green - """, + """, # noqa: E501 ) config_overrides.add_argument( "--co", @@ -430,7 +439,7 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: \t jrnl --config-file /home/user1/work_config.yaml \t - Use a personal config file stored on a thumb drive: \n \t jrnl --config-file /media/user1/my-thumb-drive/personal_config.yaml - """, + """, # noqa: E501 ) alternate_config.add_argument( diff --git a/jrnl/commands.py b/jrnl/commands.py index 83618ce2..5ba449e4 100644 --- a/jrnl/commands.py +++ b/jrnl/commands.py @@ -142,7 +142,7 @@ def postconfig_encrypt( def postconfig_decrypt( args: argparse.Namespace, config: dict, original_config: dict ) -> int: - """Decrypts into new file. If filename is not set, we encrypt the journal file itself.""" + """Decrypts to file. If filename is not set, we encrypt the journal file itself.""" from jrnl.config import update_config from jrnl.install import save_config from jrnl.journals import open_journal diff --git a/jrnl/config.py b/jrnl/config.py index 6cb4bdc3..1fdded19 100644 --- a/jrnl/config.py +++ b/jrnl/config.py @@ -4,12 +4,10 @@ import argparse import logging import os -from pathlib import Path from typing import Any from typing import Callable import colorama -import xdg.BaseDirectory from rich.pretty import pretty_repr from ruamel.yaml import YAML from ruamel.yaml import constructor @@ -21,13 +19,10 @@ from jrnl.messages import MsgStyle from jrnl.messages import MsgText from jrnl.output import list_journals from jrnl.output import print_msg -from jrnl.path import home_dir +from jrnl.path import get_config_path +from jrnl.path import get_default_journal_path # Constants -DEFAULT_CONFIG_NAME = "jrnl.yaml" -XDG_RESOURCE = "jrnl" - -DEFAULT_JOURNAL_NAME = "journal.txt" DEFAULT_JOURNAL_KEY = "default" YAML_SEPARATOR = ": " @@ -42,9 +37,10 @@ def make_yaml_valid_dict(input: list) -> dict: The dict is created through the yaml loader, with the assumption that "input[0]: input[1]" is valid yaml. - :param input: list of configuration keys in dot-notation and their respective values. + :param input: list of configuration keys in dot-notation and their respective values :type input: list - :return: A single level dict of the configuration keys in dot-notation and their respective desired values + :return: A single level dict of the configuration keys in dot-notation and their + respective desired values :rtype: dict """ @@ -73,31 +69,6 @@ def save_config(config: dict, alt_config_path: str | None = None) -> None: yaml.dump(config, f) -def get_config_directory() -> str: - try: - return xdg.BaseDirectory.save_config_path(XDG_RESOURCE) - except FileExistsError: - raise JrnlException( - Message( - MsgText.ConfigDirectoryIsFile, - MsgStyle.ERROR, - { - "config_directory_path": os.path.join( - xdg.BaseDirectory.xdg_config_home, XDG_RESOURCE - ) - }, - ), - ) - - -def get_config_path() -> Path: - try: - config_directory_path = get_config_directory() - except JrnlException: - return Path(home_dir(), DEFAULT_CONFIG_NAME) - return Path(config_directory_path, DEFAULT_CONFIG_NAME) - - def get_default_config() -> dict[str, Any]: return { "version": __version__, @@ -130,20 +101,6 @@ def get_default_colors() -> dict[str, Any]: } -def get_default_journal_path() -> str: - journal_data_path = xdg.BaseDirectory.save_data_path(XDG_RESOURCE) or home_dir() - return os.path.join(journal_data_path, DEFAULT_JOURNAL_NAME) - - -def get_templates_path() -> Path: - # jrnl_xdg_resource_path is created by save_data_path if it does not exist - jrnl_xdg_resource_path = Path(xdg.BaseDirectory.save_data_path(XDG_RESOURCE)) - jrnl_templates_path = jrnl_xdg_resource_path / "templates" - # Create the directory if needed. - jrnl_templates_path.mkdir(exist_ok=True) - return jrnl_templates_path - - def scope_config(config: dict, journal_name: str) -> dict: if journal_name not in config["journals"]: return config diff --git a/jrnl/controller.py b/jrnl/controller.py index 07fdeab6..2e36551b 100644 --- a/jrnl/controller.py +++ b/jrnl/controller.py @@ -11,10 +11,10 @@ from jrnl import time from jrnl.config import DEFAULT_JOURNAL_KEY from jrnl.config import get_config_path from jrnl.config import get_journal_name -from jrnl.config import get_templates_path from jrnl.config import scope_config from jrnl.editor import get_text_from_editor from jrnl.editor import get_text_from_stdin +from jrnl.editor import read_template_file from jrnl.exception import JrnlException from jrnl.journals import open_journal from jrnl.messages import Message @@ -23,7 +23,6 @@ from jrnl.messages import MsgText from jrnl.output import print_msg from jrnl.output import print_msgs from jrnl.override import apply_overrides -from jrnl.path import absolute_path if TYPE_CHECKING: from argparse import Namespace @@ -35,9 +34,9 @@ if TYPE_CHECKING: def run(args: "Namespace"): """ Flow: - 1. Run standalone command if it doesn't require config (help, version, etc), then exit + 1. Run standalone command if it doesn't need config (help, version, etc), then exit 2. Load config - 3. Run standalone command if it does require config (encrypt, decrypt, etc), then exit + 3. Run standalone command if it does need config (encrypt, decrypt, etc), then exit 4. Load specified journal 5. Start append mode, or search mode 6. Perform actions with results from search mode (if needed) @@ -130,74 +129,6 @@ def _is_append_mode(args: "Namespace", config: dict, **kwargs) -> bool: return append_mode -def _read_template_file(template_arg: str, template_path_from_config: str) -> str: - """ - This function is called when either a template file is passed with --template, or config.template is set. - - The processing logic is: - If --template was not used: Load the global template file. - If --template was used: - * Check $XDG_DATA_HOME/jrnl/templates/template_arg. - * Check template_arg as an absolute / relative path. - - If a file is found, its contents are returned as a string. - If not, a JrnlException is raised. - """ - logging.debug( - "Append mode: Either a template arg was passed, or the global config is set." - ) - - # If filename is unset, we are in this flow due to a global template being configured - if not template_arg: - logging.debug("Append mode: Global template configuration detected.") - global_template_path = absolute_path(template_path_from_config) - try: - with open(global_template_path, encoding="utf-8") as f: - template_data = f.read() - return template_data - except FileNotFoundError: - raise JrnlException( - Message( - MsgText.CantReadTemplateGlobalConfig, - MsgStyle.ERROR, - { - "global_template_path": global_template_path, - }, - ) - ) - else: # A template CLI arg was passed. - logging.debug("Trying to load template from $XDG_DATA_HOME/jrnl/templates/") - jrnl_template_dir = get_templates_path() - logging.debug(f"Append mode: jrnl templates directory: {jrnl_template_dir}") - template_path = jrnl_template_dir / template_arg - try: - with open(template_path, encoding="utf-8") as f: - template_data = f.read() - return template_data - except FileNotFoundError: - logging.debug( - f"Couldn't open {template_path}. Treating --template argument like a local / abs path." - ) - pass - - normalized_template_arg_filepath = absolute_path(template_arg) - try: - with open(normalized_template_arg_filepath, encoding="utf-8") as f: - template_data = f.read() - return template_data - except FileNotFoundError: - raise JrnlException( - Message( - MsgText.CantReadTemplateCLIArg, - MsgStyle.ERROR, - { - "normalized_template_arg_filepath": normalized_template_arg_filepath, - "jrnl_template_dir": template_path, - }, - ) - ) - - def append_mode(args: "Namespace", config: dict, journal: "Journal", **kwargs) -> None: """ Gets input from the user to write to the journal @@ -210,26 +141,22 @@ def append_mode(args: "Namespace", config: dict, journal: "Journal", **kwargs) - """ logging.debug("Append mode: starting") - if args.template or config["template"]: - logging.debug(f"Append mode: template CLI arg detected: {args.template}") - # Read template file and pass as raw text into the composer - template_data = _read_template_file(args.template, config["template"]) - raw = _write_in_editor(config, template_data) - if raw == template_data: - logging.error("Append mode: raw text was the same as the template") - raise JrnlException(Message(MsgText.NoChangesToTemplate, MsgStyle.NORMAL)) - elif args.text: + template_text = _get_template(args, config) + + if args.text: logging.debug(f"Append mode: cli text detected: {args.text}") raw = " ".join(args.text).strip() if args.edit: raw = _write_in_editor(config, raw) - elif not sys.stdin.isatty(): logging.debug("Append mode: receiving piped text") raw = sys.stdin.read() - else: - raw = _write_in_editor(config) + raw = _write_in_editor(config, template_text) + + if template_text is not None and raw == template_text: + logging.error("Append mode: raw text was the same as the template") + raise JrnlException(Message(MsgText.NoChangesToTemplate, MsgStyle.NORMAL)) if not raw or raw.isspace(): logging.error("Append mode: couldn't get raw text or entry was empty") @@ -251,6 +178,23 @@ def append_mode(args: "Namespace", config: dict, journal: "Journal", **kwargs) - logging.debug("Append mode: completed journal.write()") +def _get_template(args, config) -> str: + # Read template file and pass as raw text into the composer + logging.debug( + "Get template:\n" + f"--template: {args.template}\n" + f"from config: {config.get('template')}" + ) + template_path = args.template or config.get("template") + + template_text = None + + if template_path: + template_text = read_template_file(template_path) + + return template_text + + def search_mode(args: "Namespace", journal: "Journal", **kwargs) -> None: """ Search for entries in a journal, and return the diff --git a/jrnl/editor.py b/jrnl/editor.py index adde528f..61afed3c 100644 --- a/jrnl/editor.py +++ b/jrnl/editor.py @@ -15,6 +15,8 @@ from jrnl.messages import MsgText from jrnl.os_compat import on_windows from jrnl.os_compat import split_args from jrnl.output import print_msg +from jrnl.path import absolute_path +from jrnl.path import get_templates_path def get_text_from_editor(config: dict, template: str = "") -> str: @@ -73,3 +75,47 @@ def get_text_from_stdin() -> str: ) return raw + + +def get_template_path(template_path: str, jrnl_template_dir: str) -> str: + actual_template_path = os.path.join(jrnl_template_dir, template_path) + if not os.path.exists(actual_template_path): + logging.debug( + f"Couldn't open {actual_template_path}. " + "Treating template path like a local / abs path." + ) + actual_template_path = absolute_path(template_path) + + return actual_template_path + + +def read_template_file(template_path: str) -> str: + """ + Reads the template file given a template path in this order: + + * Check $XDG_DATA_HOME/jrnl/templates/template_path. + * Check template_arg as an absolute / relative path. + + If a file is found, its contents are returned as a string. + If not, a JrnlException is raised. + """ + + jrnl_template_dir = get_templates_path() + actual_template_path = get_template_path(template_path, jrnl_template_dir) + + try: + with open(actual_template_path, encoding="utf-8") as f: + template_data = f.read() + return template_data + except FileNotFoundError: + raise JrnlException( + Message( + MsgText.CantReadTemplate, + MsgStyle.ERROR, + { + "template_path": template_path, + "actual_template_path": actual_template_path, + "jrnl_template_dir": str(jrnl_template_dir) + os.sep, + }, + ) + ) diff --git a/jrnl/install.py b/jrnl/install.py index f9d9ac1a..4033c33c 100644 --- a/jrnl/install.py +++ b/jrnl/install.py @@ -31,11 +31,11 @@ from jrnl.upgrade import is_old_version def upgrade_config(config_data: dict, alt_config_path: str | None = None) -> None: - """Checks if there are keys missing in a given config dict, and if so, updates the config file accordingly. - This essentially automatically ports jrnl installations if new config parameters are introduced in later - versions. - Also checks for existence of and difference in version number between config dict and current jrnl version, - and if so, update the config file accordingly. + """Checks if there are keys missing in a given config dict, and if so, updates the + config file accordingly. This essentially automatically ports jrnl installations + if new config parameters are introduced in later versions. Also checks for + existence of and difference in version number between config dict + and current jrnl version, and if so, update the config file accordingly. Supply alt_config_path if using an alternate config through --config-file.""" default_config = get_default_config() missing_keys = set(default_config).difference(config_data) @@ -167,7 +167,7 @@ def install() -> dict: def _initialize_autocomplete() -> None: - # readline is not included in Windows Active Python and perhaps some other distributions + # readline is not included in Windows Active Python and perhaps some other distss if sys.modules.get("readline"): import readline diff --git a/jrnl/journals/Entry.py b/jrnl/journals/Entry.py index a099ade3..6c45303e 100644 --- a/jrnl/journals/Entry.py +++ b/jrnl/journals/Entry.py @@ -88,7 +88,7 @@ class Entry: } def __str__(self): - """Returns a string representation of the entry to be written into a journal file.""" + """Returns string representation of the entry to be written to journal file.""" date_str = self.date.strftime(self.journal.config["timeformat"]) title = "[{}] {}".format(date_str, self.title.rstrip("\n ")) if self.starred: @@ -214,7 +214,7 @@ SENTENCE_SPLITTER = re.compile( \s+ # AND a sequence of required spaces. ) |[\uFF01\uFF0E\uFF1F\uFF61\u3002] # CJK full/half width terminals usually do not have following spaces. - """, + """, # noqa: E501 re.VERBOSE, ) diff --git a/jrnl/journals/FolderJournal.py b/jrnl/journals/FolderJournal.py index 0d497fb8..c6faf1e0 100644 --- a/jrnl/journals/FolderJournal.py +++ b/jrnl/journals/FolderJournal.py @@ -122,7 +122,8 @@ class Folder(Journal): @staticmethod def _get_files(journal_path: str) -> list[str]: - """Searches through sub directories starting with journal_path and find all text files that look like entries""" + """Searches through sub directories starting with journal_path and find all text + files that look like entries""" for year_folder in Folder._get_year_folders(pathlib.Path(journal_path)): for month_folder in Folder._get_month_folders(year_folder): yield from Folder._get_day_files(month_folder) diff --git a/jrnl/journals/Journal.py b/jrnl/journals/Journal.py index bd72788b..d1054814 100644 --- a/jrnl/journals/Journal.py +++ b/jrnl/journals/Journal.py @@ -102,7 +102,7 @@ class Journal: return self.encryption_method.encrypt(text) def open(self, filename: str | None = None) -> "Journal": - """Opens the journal file defined in the config and parses it into a list of Entries. + """Opens the journal file and parses it into a list of Entries Entries have the form (date, title, body).""" filename = filename or self.config["journal"] dirname = os.path.dirname(filename) @@ -144,7 +144,7 @@ class Journal: self._store(filename, text) def validate_parsing(self) -> bool: - """Confirms that the jrnl is still parsed correctly after being dumped to text.""" + """Confirms that the jrnl is still parsed correctly after conversion to text.""" new_entries = self._parse(self._to_text()) return all(entry == new_entries[i] for i, entry in enumerate(self.entries)) @@ -225,8 +225,9 @@ class Journal: @property def tags(self) -> list[Tag]: """Returns a set of tuples (count, tag) for all tags present in the journal.""" - # Astute reader: should the following line leave you as puzzled as me the first time - # I came across this construction, worry not and embrace the ensuing moment of enlightment. + # Astute reader: should the following line leave you as puzzled as me the first + # time I came across this construction, worry not and embrace the ensuing moment + # of enlightment. tags = [tag for entry in self.entries for tag in set(entry.tags)] # To be read: [for entry in journal.entries: for tag in set(entry.tags): tag] tag_counts = {(tags.count(tag), tag) for tag in tags} @@ -343,7 +344,8 @@ class Journal: def new_entry(self, raw: str, date=None, sort: bool = True) -> Entry: """Constructs a new entry from some raw text input. - If a date is given, it will parse and use this, otherwise scan for a date in the input first. + If a date is given, it will parse and use this, otherwise scan for a date in + the input first. """ raw = raw.replace("\\n ", "\n").replace("\\n", "\n") diff --git a/jrnl/messages/MsgStyle.py b/jrnl/messages/MsgStyle.py index ee898fdb..666896b6 100644 --- a/jrnl/messages/MsgStyle.py +++ b/jrnl/messages/MsgStyle.py @@ -90,4 +90,4 @@ class MsgStyle(Enum): @property def box_title(self) -> MsgText: - return self.value.get("box_title", None) + return self.value.get("box_title") diff --git a/jrnl/messages/MsgText.py b/jrnl/messages/MsgText.py index 1310231b..9361781d 100644 --- a/jrnl/messages/MsgText.py +++ b/jrnl/messages/MsgText.py @@ -43,8 +43,8 @@ class MsgText(Enum): Do you want to encrypt your journal? (You can always change this later) """ UseColorsQuestion = """ - Do you want jrnl to use colors when displaying entries? (You can always change this later) - """ + Do you want jrnl to use colors to display entries? (You can always change this later) + """ # noqa: E501 - the line is still under 88 when dedented YesOrNoPromptDefaultYes = "[Y/n]" YesOrNoPromptDefaultNo = "[y/N]" ContinueUpgrade = "Continue upgrading jrnl?" @@ -105,16 +105,12 @@ class MsgText(Enum): KeyboardInterruptMsg = "Aborted by user" - CantReadTemplateGlobalConfig = """ - Could not read template file defined in config: - {global_template_path} - """ + CantReadTemplate = """ + Unable to find a template file {template_path}. - CantReadTemplateCLIArg = """ - Unable to find a template file based on the passed arg, and no global template was detected. - The following filepaths were checked: - jrnl XDG Template Directory : {jrnl_template_dir} - Local Filepath : {normalized_template_arg_filepath} + The following paths were checked: + * {jrnl_template_dir}{template_path} + * {actual_template_path} """ NoNamedJournal = "No '{journal_name}' journal configured\n{journals}" diff --git a/jrnl/output.py b/jrnl/output.py index cd47846f..0230244b 100644 --- a/jrnl/output.py +++ b/jrnl/output.py @@ -39,7 +39,10 @@ def journal_list_to_yaml(journal_list: dict) -> str: from ruamel.yaml import YAML output = StringIO() - YAML().dump(journal_list, output) + dumper = YAML() + dumper.width = 1000 + dumper.dump(journal_list, output) + return output.getvalue() diff --git a/jrnl/override.py b/jrnl/override.py index 64a0fe86..368d1a37 100644 --- a/jrnl/override.py +++ b/jrnl/override.py @@ -21,7 +21,7 @@ def apply_overrides(args: "Namespace", base_config: dict) -> dict: :return: Configuration to be used during runtime with the overrides applied :rtype: dict """ - overrides = vars(args).get("config_override", None) + overrides = vars(args).get("config_override") if not overrides: return base_config @@ -56,7 +56,8 @@ def _recursively_apply(tree: dict, nodes: list, override_value) -> dict: Args: config (dict): Configuration to modify - nodes (list): Vector of override keys; the length of the vector indicates tree depth + nodes (list): Vector of override keys; the length of the vector indicates tree + depth override_value (str): Runtime override passed from the command-line """ key = nodes[0] diff --git a/jrnl/path.py b/jrnl/path.py index 306e34cc..4361cf97 100644 --- a/jrnl/path.py +++ b/jrnl/path.py @@ -2,6 +2,19 @@ # License: https://www.gnu.org/licenses/gpl-3.0.html import os.path +from pathlib import Path + +import xdg.BaseDirectory + +from jrnl.exception import JrnlException +from jrnl.messages import Message +from jrnl.messages import MsgStyle +from jrnl.messages import MsgText + +# Constants +XDG_RESOURCE = "jrnl" +DEFAULT_CONFIG_NAME = "jrnl.yaml" +DEFAULT_JOURNAL_NAME = "journal.txt" def home_dir() -> str: @@ -14,3 +27,46 @@ def expand_path(path: str) -> str: def absolute_path(path: str) -> str: return os.path.abspath(expand_path(path)) + + +def get_default_journal_path() -> str: + journal_data_path = xdg.BaseDirectory.save_data_path(XDG_RESOURCE) or home_dir() + return os.path.join(journal_data_path, DEFAULT_JOURNAL_NAME) + + +def get_templates_path() -> str: + """ + Get the path to the XDG templates directory. Creates the directory if it + doesn't exist. + """ + # jrnl_xdg_resource_path is created by save_data_path if it does not exist + jrnl_xdg_resource_path = Path(xdg.BaseDirectory.save_data_path(XDG_RESOURCE)) + jrnl_templates_path = jrnl_xdg_resource_path / "templates" + # Create the directory if needed. + jrnl_templates_path.mkdir(exist_ok=True) + return str(jrnl_templates_path) + + +def get_config_directory() -> str: + try: + return xdg.BaseDirectory.save_config_path(XDG_RESOURCE) + except FileExistsError: + raise JrnlException( + Message( + MsgText.ConfigDirectoryIsFile, + MsgStyle.ERROR, + { + "config_directory_path": os.path.join( + xdg.BaseDirectory.xdg_config_home, XDG_RESOURCE + ) + }, + ), + ) + + +def get_config_path() -> str: + try: + config_directory_path = get_config_directory() + except JrnlException: + return os.path.join(home_dir(), DEFAULT_CONFIG_NAME) + return os.path.join(config_directory_path, DEFAULT_CONFIG_NAME) diff --git a/jrnl/plugins/fancy_exporter.py b/jrnl/plugins/fancy_exporter.py index 447f1347..a6cbe361 100644 --- a/jrnl/plugins/fancy_exporter.py +++ b/jrnl/plugins/fancy_exporter.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: class FancyExporter(TextExporter): - """This Exporter can convert entries and journals into text with unicode box drawing characters.""" + """This Exporter converts entries and journals into text with unicode boxes.""" names = ["fancy", "boxed"] extension = "txt" diff --git a/jrnl/plugins/tag_exporter.py b/jrnl/plugins/tag_exporter.py index b8b5eb79..c1160626 100644 --- a/jrnl/plugins/tag_exporter.py +++ b/jrnl/plugins/tag_exporter.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: class TagExporter(TextExporter): - """This Exporter can lists the tags for entries and journals, exported as a plain text file.""" + """This Exporter lists the tags for entries and journals.""" names = ["tags"] extension = "tags" diff --git a/jrnl/plugins/util.py b/jrnl/plugins/util.py index ceaa0b04..180d86ea 100644 --- a/jrnl/plugins/util.py +++ b/jrnl/plugins/util.py @@ -10,7 +10,8 @@ if TYPE_CHECKING: def get_tags_count(journal: "Journal") -> set[tuple[int, str]]: """Returns a set of tuples (count, tag) for all tags present in the journal.""" # Astute reader: should the following line leave you as puzzled as me the first time - # I came across this construction, worry not and embrace the ensuing moment of enlightment. + # I came across this construction, worry not and embrace the ensuing moment of + # enlightment. tags = [tag for entry in journal.entries for tag in set(entry.tags)] # To be read: [for entry in journal.entries: for tag in set(entry.tags): tag] tag_counts = {(tags.count(tag), tag) for tag in tags} diff --git a/jrnl/plugins/yaml_exporter.py b/jrnl/plugins/yaml_exporter.py index d960ef8a..90c14086 100644 --- a/jrnl/plugins/yaml_exporter.py +++ b/jrnl/plugins/yaml_exporter.py @@ -18,14 +18,15 @@ if TYPE_CHECKING: class YAMLExporter(TextExporter): - """This Exporter can convert entries and journals into Markdown formatted text with YAML front matter.""" + """This Exporter converts entries and journals into Markdown formatted text with + YAML front matter.""" names = ["yaml"] extension = "md" @classmethod def export_entry(cls, entry: "Entry", to_multifile: bool = True) -> str: - """Returns a markdown representation of a single entry, with YAML front matter.""" + """Returns a markdown representation of an entry, with YAML front matter.""" if to_multifile is False: raise JrnlException(Message(MsgText.YamlMustBeDirectory, MsgStyle.ERROR)) @@ -117,7 +118,14 @@ class YAMLExporter(TextExporter): # source directory is entry.journal.config['journal'] # output directory is...? - return "{start}\ntitle: {title}\ndate: {date}\nstarred: {starred}\ntags: {tags}\n{dayone}body: |{body}{end}".format( + return ( + "{start}\n" + "title: {title}\n" + "date: {date}\n" + "starred: {starred}\n" + "tags: {tags}\n" + "{dayone}body: |{body}{end}" + ).format( start="---", date=date_str, title=entry.title, diff --git a/jrnl/time.py b/jrnl/time.py index dd6fcb0f..da13f1d0 100644 --- a/jrnl/time.py +++ b/jrnl/time.py @@ -34,8 +34,8 @@ def parse( elif isinstance(date_str, datetime.datetime): return date_str - # Don't try to parse anything with 6 or fewer characters and was parsed from the existing journal. - # It's probably a markdown footnote + # Don't try to parse anything with 6 or fewer characters and was parsed from the + # existing journal. It's probably a markdown footnote if len(date_str) <= 6 and bracketed: return None @@ -82,9 +82,9 @@ def parse( else: date = datetime.datetime(*date[:6]) - # Ugly heuristic: if the date is more than 4 weeks in the future, we got the year wrong. - # Rather than this, we would like to see parsedatetime patched so we can tell it to prefer - # past dates + # Ugly heuristic: if the date is more than 4 weeks in the future, we got the year + # wrong. Rather than this, we would like to see parsedatetime patched so we can + # tell it to prefer past dates dt = datetime.datetime.now() - date if dt.days < -28 and not year_present: date = date.replace(date.year - 1) diff --git a/poetry.lock b/poetry.lock index 9d432f72..35da6714 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. [[package]] name = "appnope" version = "0.1.3" description = "Disable App Nap on macOS >= 10.9" -category = "dev" optional = false python-versions = "*" files = [ @@ -12,23 +11,10 @@ files = [ {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, ] -[[package]] -name = "astor" -version = "0.8.1" -description = "Read/rewrite/write Python ASTs" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -files = [ - {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, - {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, -] - [[package]] name = "asttokens" version = "2.2.1" description = "Annotate AST trees with source code positions" -category = "dev" optional = false python-versions = "*" files = [ @@ -46,7 +32,6 @@ test = ["astroid", "pytest"] name = "backcall" version = "0.2.0" description = "Specifications for callback functions passed in to an API" -category = "dev" optional = false python-versions = "*" files = [ @@ -58,7 +43,6 @@ files = [ name = "black" version = "23.3.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -105,21 +89,19 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cachetools" -version = "5.3.0" +version = "5.3.1" description = "Extensible memoizing collections and decorators" -category = "dev" optional = false -python-versions = "~=3.7" +python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, - {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, ] [[package]] name = "certifi" version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -131,7 +113,6 @@ files = [ name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = "*" files = [ @@ -208,7 +189,6 @@ pycparser = "*" name = "chardet" version = "5.1.0" description = "Universal encoding detector for Python 3" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -220,7 +200,6 @@ files = [ name = "charset-normalizer" version = "3.0.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" optional = false python-versions = "*" files = [ @@ -314,23 +293,10 @@ files = [ {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, ] -[[package]] -name = "classify-imports" -version = "4.2.0" -description = "Utilities for refactoring imports in python-like syntax." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "classify_imports-4.2.0-py2.py3-none-any.whl", hash = "sha256:dbbc264b70a470ed8c6c95976a11dfb8b7f63df44ed1af87328bbed2663f5161"}, - {file = "classify_imports-4.2.0.tar.gz", hash = "sha256:7abfb7ea92149b29d046bd34573d247ba6e68cc28100c801eba4af17964fc40e"}, -] - [[package]] name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -345,7 +311,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -355,31 +320,30 @@ files = [ [[package]] name = "cryptography" -version = "40.0.2" +version = "41.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b"}, - {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440"}, - {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d"}, - {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288"}, - {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2"}, - {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b"}, - {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9"}, - {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c"}, - {file = "cryptography-40.0.2-cp36-abi3-win32.whl", hash = "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9"}, - {file = "cryptography-40.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b"}, - {file = "cryptography-40.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b"}, - {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e"}, - {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a"}, - {file = "cryptography-40.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958"}, - {file = "cryptography-40.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b"}, - {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636"}, - {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e"}, - {file = "cryptography-40.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404"}, - {file = "cryptography-40.0.2.tar.gz", hash = "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99"}, + {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699"}, + {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a"}, + {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca"}, + {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43"}, + {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b"}, + {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3"}, + {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db"}, + {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31"}, + {file = "cryptography-41.0.1-cp37-abi3-win32.whl", hash = "sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5"}, + {file = "cryptography-41.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c"}, + {file = "cryptography-41.0.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb"}, + {file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3"}, + {file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039"}, + {file = "cryptography-41.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc"}, + {file = "cryptography-41.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485"}, + {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c"}, + {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a"}, + {file = "cryptography-41.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5"}, + {file = "cryptography-41.0.1.tar.gz", hash = "sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006"}, ] [package.dependencies] @@ -388,18 +352,17 @@ cffi = ">=1.12" [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] -pep8test = ["black", "check-manifest", "mypy", "ruff"] -sdist = ["setuptools-rust (>=0.11.4)"] +nox = ["nox"] +pep8test = ["black", "check-sdist", "mypy", "ruff"] +sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist"] +test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] -tox = ["tox"] [[package]] name = "decorator" version = "5.1.1" description = "Decorators for Humans" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -411,7 +374,6 @@ files = [ name = "distlib" version = "0.3.6" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ @@ -419,23 +381,10 @@ files = [ {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, ] -[[package]] -name = "entrypoints" -version = "0.4" -description = "Discover and load entry points from installed packages." -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, - {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, -] - [[package]] name = "exceptiongroup" version = "1.1.0" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -450,7 +399,6 @@ test = ["pytest (>=6)"] name = "execnet" version = "1.9.0" description = "execnet: rapid multi-Python deployment" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -465,7 +413,6 @@ testing = ["pre-commit"] name = "executing" version = "1.2.0" description = "Get the currently executing AST node of a frame, and other information" -category = "dev" optional = false python-versions = "*" files = [ @@ -478,136 +425,23 @@ tests = ["asttokens", "littleutils", "pytest", "rich"] [[package]] name = "filelock" -version = "3.11.0" +version = "3.12.2" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "filelock-3.11.0-py3-none-any.whl", hash = "sha256:f08a52314748335c6460fc8fe40cd5638b85001225db78c2aa01c8c0db83b318"}, - {file = "filelock-3.11.0.tar.gz", hash = "sha256:3618c0da67adcc0506b015fd11ef7faf1b493f0b40d87728e19986b536890c37"}, + {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, + {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, ] [package.extras] -docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.2)", "diff-cover (>=7.5)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "flake8" -version = "4.0.1" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, - {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, -] - -[package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.8.0,<2.9.0" -pyflakes = ">=2.4.0,<2.5.0" - -[[package]] -name = "flake8-black" -version = "0.3.6" -description = "flake8 plugin to call black as a code style validator" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-black-0.3.6.tar.gz", hash = "sha256:0dfbca3274777792a5bcb2af887a4cad72c72d0e86c94e08e3a3de151bb41c34"}, - {file = "flake8_black-0.3.6-py3-none-any.whl", hash = "sha256:fe8ea2eca98d8a504f22040d9117347f6b367458366952862ac3586e7d4eeaca"}, -] - -[package.dependencies] -black = ">=22.1.0" -flake8 = ">=3" -tomli = {version = "*", markers = "python_version < \"3.11\""} - -[package.extras] -develop = ["build", "twine"] - -[[package]] -name = "flake8-isort" -version = "6.0.0" -description = "flake8 plugin that integrates isort ." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-isort-6.0.0.tar.gz", hash = "sha256:537f453a660d7e903f602ecfa36136b140de279df58d02eb1b6a0c84e83c528c"}, - {file = "flake8_isort-6.0.0-py3-none-any.whl", hash = "sha256:aa0cac02a62c7739e370ce6b9c31743edac904bae4b157274511fc8a19c75bbc"}, -] - -[package.dependencies] -flake8 = "*" -isort = ">=5.0.0,<6" - -[package.extras] -test = ["pytest"] - -[[package]] -name = "flake8-simplify" -version = "0.19.3" -description = "flake8 plugin which checks for code that can be simplified" -category = "dev" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "flake8_simplify-0.19.3-py3-none-any.whl", hash = "sha256:1057320e9312d75849541fee822900d27bcad05b2405edc84713affee635629e"}, - {file = "flake8_simplify-0.19.3.tar.gz", hash = "sha256:2fb083bf5142a98d9c9554755cf2f56f8926eb4a33eae30c0809041b1546879e"}, -] - -[package.dependencies] -astor = ">=0.1" -flake8 = ">=3.7" - -[[package]] -name = "flake8-type-checking" -version = "2.4.0" -description = "A flake8 plugin for managing type-checking imports & forward references" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "flake8_type_checking-2.4.0-py3-none-any.whl", hash = "sha256:2dee127f300bb95b7f17b7c3fff4f6336f5e4ba92082c15928c6e19b666cfba4"}, - {file = "flake8_type_checking-2.4.0.tar.gz", hash = "sha256:9ea96d01e6557a47835acf04020c48fabb9c3d4664c15f2920915e09e65c1d55"}, -] - -[package.dependencies] -classify-imports = "*" -flake8 = "*" - -[[package]] -name = "flakeheaven" -version = "3.3.0" -description = "FlakeHeaven is a [Flake8](https://gitlab.com/pycqa/flake8) wrapper to make it cool." -category = "dev" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "flakeheaven-3.3.0-py3-none-any.whl", hash = "sha256:ae246197a178845b30b63fc03023f7ba925cc84cc96314ec19807dafcd6b39a3"}, - {file = "flakeheaven-3.3.0.tar.gz", hash = "sha256:eb07860e028ff8dd56cce742c4766624a37a4ce397fd34300254ab623d13047b"}, -] - -[package.dependencies] -colorama = "*" -entrypoints = "*" -flake8 = ">=4.0.1,<5.0.0" -pygments = "*" -toml = "*" -urllib3 = "*" - -[package.extras] -docs = ["alabaster", "myst-parser (>=0.18.0,<0.19.0)", "pygments-github-lexers", "sphinx"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "ghp-import" version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." -category = "dev" optional = false python-versions = "*" files = [ @@ -625,7 +459,6 @@ dev = ["flake8", "markdown", "twine", "wheel"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -637,7 +470,6 @@ files = [ name = "importlib-metadata" version = "6.0.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -657,7 +489,6 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -669,7 +500,6 @@ files = [ name = "ipdb" version = "0.13.13" description = "IPython-enabled pdb" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -686,7 +516,6 @@ tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < name = "ipython" version = "8.10.0" description = "IPython: Productive Interactive Computing" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -721,29 +550,10 @@ qtconsole = ["qtconsole"] test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] -[[package]] -name = "isort" -version = "5.12.0" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, -] - -[package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - [[package]] name = "jaraco-classes" version = "3.2.3" description = "Utility functions for Python class constructs" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -762,7 +572,6 @@ testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-chec name = "jedi" version = "0.18.2" description = "An autocompletion tool for Python that can be used for text editors." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -782,7 +591,6 @@ testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] name = "jeepney" version = "0.8.0" description = "Low-level, pure Python DBus protocol wrapper." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -798,7 +606,6 @@ trio = ["async_generator", "trio"] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -814,14 +621,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "keyring" -version = "23.13.1" +version = "24.2.0" description = "Store and access your passwords safely." -category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "keyring-23.13.1-py3-none-any.whl", hash = "sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd"}, - {file = "keyring-23.13.1.tar.gz", hash = "sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678"}, + {file = "keyring-24.2.0-py3-none-any.whl", hash = "sha256:4901caaf597bfd3bbd78c9a0c7c4c29fcd8310dab2cffefe749e916b6527acd6"}, + {file = "keyring-24.2.0.tar.gz", hash = "sha256:ca0746a19ec421219f4d713f848fa297a661a8a8c1504867e55bfb5e09091509"}, ] [package.dependencies] @@ -833,14 +639,13 @@ SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] completion = ["shtab"] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [[package]] name = "mako" version = "1.2.4" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -860,7 +665,6 @@ testing = ["pytest"] name = "markdown" version = "3.3.7" description = "Python implementation of Markdown." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -875,7 +679,6 @@ testing = ["coverage", "pyyaml"] name = "markdown-it-py" version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -900,7 +703,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -960,7 +762,6 @@ files = [ name = "matplotlib-inline" version = "0.1.6" description = "Inline Matplotlib backend for Jupyter" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -971,23 +772,10 @@ files = [ [package.dependencies] traitlets = "*" -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] - [[package]] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -999,7 +787,6 @@ files = [ name = "mergedeep" version = "1.3.4" description = "A deep merge function for 🐍." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1009,14 +796,13 @@ files = [ [[package]] name = "mkdocs" -version = "1.4.2" +version = "1.4.3" description = "Project documentation with Markdown." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "mkdocs-1.4.2-py3-none-any.whl", hash = "sha256:c8856a832c1e56702577023cd64cc5f84948280c1c0fcc6af4cd39006ea6aa8c"}, - {file = "mkdocs-1.4.2.tar.gz", hash = "sha256:8947af423a6d0facf41ea1195b8e1e8c85ad94ac95ae307fe11232e0424b11c5"}, + {file = "mkdocs-1.4.3-py3-none-any.whl", hash = "sha256:6ee46d309bda331aac915cd24aab882c179a933bd9e77b80ce7d2eaaa3f689dd"}, + {file = "mkdocs-1.4.3.tar.gz", hash = "sha256:5955093bbd4dd2e9403c5afaf57324ad8b04f16886512a3ee6ef828956481c57"}, ] [package.dependencies] @@ -1039,7 +825,6 @@ min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-imp name = "more-itertools" version = "9.0.0" description = "More routines for operating on iterables, beyond itertools" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1051,7 +836,6 @@ files = [ name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1061,21 +845,19 @@ files = [ [[package]] name = "packaging" -version = "23.0" +version = "23.1" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] [[package]] name = "parse" version = "1.19.0" description = "parse() is the opposite of format()" -category = "dev" optional = false python-versions = "*" files = [ @@ -1086,7 +868,6 @@ files = [ name = "parse-type" version = "0.6.0" description = "Simplifies to build parse types based on the parse module" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*" files = [ @@ -1106,7 +887,6 @@ docs = ["sphinx (>=1.2)"] name = "parsedatetime" version = "2.6" description = "Parse human-readable date/time text." -category = "main" optional = false python-versions = "*" files = [ @@ -1118,7 +898,6 @@ files = [ name = "parso" version = "0.8.3" description = "A Python Parser" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1134,7 +913,6 @@ testing = ["docopt", "pytest (<6.0.0)"] name = "pastel" version = "0.2.1" description = "Bring colors to your terminal." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1146,7 +924,6 @@ files = [ name = "pathspec" version = "0.11.0" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1158,7 +935,6 @@ files = [ name = "pexpect" version = "4.8.0" description = "Pexpect allows easy control of interactive console applications." -category = "dev" optional = false python-versions = "*" files = [ @@ -1173,7 +949,6 @@ ptyprocess = ">=0.5" name = "pickleshare" version = "0.7.5" description = "Tiny 'shelve'-like database with concurrency support" -category = "dev" optional = false python-versions = "*" files = [ @@ -1183,25 +958,23 @@ files = [ [[package]] name = "platformdirs" -version = "3.2.0" +version = "3.6.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.2.0-py3-none-any.whl", hash = "sha256:ebe11c0d7a805086e99506aa331612429a72ca7cd52a1f0d277dc4adc20cb10e"}, - {file = "platformdirs-3.2.0.tar.gz", hash = "sha256:d5b638ca397f25f979350ff789db335903d7ea010ab28903f57b27e1b16c2b08"}, + {file = "platformdirs-3.6.0-py3-none-any.whl", hash = "sha256:ffa199e3fbab8365778c4a10e1fbf1b9cd50707de826eb304b50e57ec0cc8d38"}, + {file = "platformdirs-3.6.0.tar.gz", hash = "sha256:57e28820ca8094678b807ff529196506d7a21e17156cb1cddb3e74cebce54640"}, ] [package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1215,14 +988,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "poethepoet" -version = "0.19.0" +version = "0.20.0" description = "A task runner that works well with poetry." -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "poethepoet-0.19.0-py3-none-any.whl", hash = "sha256:87038be589077e4b407050a9da644d9cd9e4076ccfc8abc7f855cf6870d5c6c2"}, - {file = "poethepoet-0.19.0.tar.gz", hash = "sha256:897eb85ec15876d79befc7d19d4c80ce7c8b214d1bb0dcfec640abd81616bfed"}, + {file = "poethepoet-0.20.0-py3-none-any.whl", hash = "sha256:cb37be15f3895ccc65ddf188c2e3d8fb79e26cc9d469a6098cb1c6f994659f6f"}, + {file = "poethepoet-0.20.0.tar.gz", hash = "sha256:ca5a2a955f52dfb0a53fad3c989ef0b69ce3d5ec0f6bfa9b1da1f9e32d262e20"}, ] [package.dependencies] @@ -1236,7 +1008,6 @@ poetry-plugin = ["poetry (>=1.0,<2.0)"] name = "pprintpp" version = "0.4.0" description = "A drop-in replacement for pprint that's actually pretty" -category = "dev" optional = false python-versions = "*" files = [ @@ -1248,7 +1019,6 @@ files = [ name = "prompt-toolkit" version = "3.0.37" description = "Library for building powerful interactive command lines in Python" -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -1263,7 +1033,6 @@ wcwidth = "*" name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" -category = "dev" optional = false python-versions = "*" files = [ @@ -1275,7 +1044,6 @@ files = [ name = "pure-eval" version = "0.2.2" description = "Safely evaluate AST nodes without side effects" -category = "dev" optional = false python-versions = "*" files = [ @@ -1286,23 +1054,10 @@ files = [ [package.extras] tests = ["pytest"] -[[package]] -name = "pycodestyle" -version = "2.8.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, - {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, -] - [[package]] name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1310,23 +1065,10 @@ files = [ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] -[[package]] -name = "pyflakes" -version = "2.4.0" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, - {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, -] - [[package]] name = "pygments" version = "2.14.0" description = "Pygments is a syntax highlighting package written in Python." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1339,34 +1081,32 @@ plugins = ["importlib-metadata"] [[package]] name = "pyproject-api" -version = "1.5.1" +version = "1.5.2" description = "API to interact with the python pyproject.toml based projects" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pyproject_api-1.5.1-py3-none-any.whl", hash = "sha256:4698a3777c2e0f6b624f8a4599131e2a25376d90fe8d146d7ac74c67c6f97c43"}, - {file = "pyproject_api-1.5.1.tar.gz", hash = "sha256:435f46547a9ff22cf4208ee274fca3e2869aeb062a4834adfc99a4dd64af3cf9"}, + {file = "pyproject_api-1.5.2-py3-none-any.whl", hash = "sha256:9cffcbfb64190f207444d7579d315f3278f2c04ba46d685fad93197b5326d348"}, + {file = "pyproject_api-1.5.2.tar.gz", hash = "sha256:999f58fa3c92b23ebd31a6bad5d1f87d456744d75e05391be7f5c729015d3d91"}, ] [package.dependencies] -packaging = ">=23" +packaging = ">=23.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -testing = ["covdefaults (>=2.2.2)", "importlib-metadata (>=6)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "virtualenv (>=20.17.1)", "wheel (>=0.38.4)"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "importlib-metadata (>=6.6)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "setuptools (>=67.8)", "wheel (>=0.40)"] [[package]] name = "pytest" -version = "7.3.1" +version = "7.4.0" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, - {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, ] [package.dependencies] @@ -1378,13 +1118,12 @@ pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-bdd" version = "6.1.1" description = "BDD for pytest" -category = "dev" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -1403,7 +1142,6 @@ typing-extensions = "*" name = "pytest-clarity" version = "1.0.1" description = "A plugin providing an alternative, colourful diff output for failing assertions." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1417,14 +1155,13 @@ rich = ">=8.0.0" [[package]] name = "pytest-xdist" -version = "3.2.1" +version = "3.3.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-xdist-3.2.1.tar.gz", hash = "sha256:1849bd98d8b242b948e472db7478e090bf3361912a8fed87992ed94085f54727"}, - {file = "pytest_xdist-3.2.1-py3-none-any.whl", hash = "sha256:37290d161638a20b672401deef1cba812d110ac27e35d213f091d15b8beb40c9"}, + {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"}, + {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"}, ] [package.dependencies] @@ -1440,7 +1177,6 @@ testing = ["filelock"] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1451,26 +1187,10 @@ files = [ [package.dependencies] six = ">=1.5" -[[package]] -name = "pytz-deprecation-shim" -version = "0.1.0.post0" -description = "Shims to make deprecation of pytz easier" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, - {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, -] - -[package.dependencies] -tzdata = {version = "*", markers = "python_version >= \"3.6\""} - [[package]] name = "pywin32-ctypes" version = "0.2.0" description = "" -category = "main" optional = false python-versions = "*" files = [ @@ -1482,7 +1202,6 @@ files = [ name = "pyxdg" version = "0.28" description = "PyXDG contains implementations of freedesktop.org standards in python." -category = "main" optional = false python-versions = "*" files = [ @@ -1494,7 +1213,6 @@ files = [ name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1544,7 +1262,6 @@ files = [ name = "pyyaml-env-tag" version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1557,21 +1274,20 @@ pyyaml = "*" [[package]] name = "requests" -version = "2.28.2" +version = "2.31.0" description = "Python HTTP for Humans." -category = "dev" optional = false -python-versions = ">=3.7, <4" +python-versions = ">=3.7" files = [ - {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, - {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" +urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] @@ -1579,18 +1295,17 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.3.4" +version = "13.4.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "main" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.3.4-py3-none-any.whl", hash = "sha256:22b74cae0278fd5086ff44144d3813be1cedc9115bdfabbfefd86400cb88b20a"}, - {file = "rich-13.3.4.tar.gz", hash = "sha256:b5d573e13605423ec80bdd0cd5f8541f7844a0e71a13f74cf454ccb2f490708b"}, + {file = "rich-13.4.2-py3-none-any.whl", hash = "sha256:8f87bc7ee54675732fa66a05ebfe489e27264caeeff3728c945d25971b6485ec"}, + {file = "rich-13.4.2.tar.gz", hash = "sha256:d653d6bccede5844304c605d5aac802c7cf9621efd700b46c7ec2b51ea914898"}, ] [package.dependencies] -markdown-it-py = ">=2.2.0,<3.0.0" +markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" [package.extras] @@ -1598,18 +1313,17 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruamel-yaml" -version = "0.17.21" +version = "0.17.32" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" -category = "main" optional = false python-versions = ">=3" files = [ - {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, - {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, + {file = "ruamel.yaml-0.17.32-py3-none-any.whl", hash = "sha256:23cd2ed620231677564646b0c6a89d138b6822a0d78656df7abda5879ec4f447"}, + {file = "ruamel.yaml-0.17.32.tar.gz", hash = "sha256:ec939063761914e14542972a5cba6d33c23b0859ab6342f61cf070cfc600efc2"}, ] [package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.12\""} [package.extras] docs = ["ryd"] @@ -1619,7 +1333,6 @@ jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] name = "ruamel-yaml-clib" version = "0.2.7" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1632,6 +1345,8 @@ files = [ {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"}, + {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"}, + {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"}, {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"}, {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"}, {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"}, @@ -1659,11 +1374,36 @@ files = [ {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, ] +[[package]] +name = "ruff" +version = "0.0.276" +description = "An extremely fast Python linter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.0.276-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:c150912b8ebde843c10b33db90705d4fee48db6f05441e5d143be9f4c2f35df5"}, + {file = "ruff-0.0.276-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:5e9cd7238d34f24d7ccfadcce4dc6807b8c5a390f547dd7236d06488d9d6f40f"}, + {file = "ruff-0.0.276-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc7dc557cc3fa2a03a88e99425ceee91429cc7432e5a41087850c1629294faed"}, + {file = "ruff-0.0.276-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13e5983836ae383c04de213954da731f14ea884aaf74467abc47e1d79d8cf1b7"}, + {file = "ruff-0.0.276-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ac65df96be3e2f4b10bc97bbb624152281611b06ef1068d5bb064b7ad35d800"}, + {file = "ruff-0.0.276-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d070a64de4affd17e006d6986ef25601dbbc6b373844ece5396c33900f8b8563"}, + {file = "ruff-0.0.276-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2edcd6948a21fa7fb4594094da37a1aa1d205b7abaa718bd27d48ba1d7977348"}, + {file = "ruff-0.0.276-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57c49b525d8ca3838d8b614f42e342077bed95aedd9fe6e6ec419f39320c214e"}, + {file = "ruff-0.0.276-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5980960a748ada3ddfe4ea7ff3a01b9113c456a14cb1a39b4c30783012d4cba6"}, + {file = "ruff-0.0.276-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:12be4f007114cf5ed1242e522762651539521ec32ae0210cc4b8dfe434a872f0"}, + {file = "ruff-0.0.276-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6ed8fc729b3e7b9f20a4e2aa6f24c798b06912f8a94cb3e8fd590eba055780df"}, + {file = "ruff-0.0.276-py3-none-musllinux_1_2_i686.whl", hash = "sha256:735d724031212c2ab63fafdea49d4581ae866a1180d06c29b0b5481228ca6bb9"}, + {file = "ruff-0.0.276-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:100ad9055d50977c2b4ab3de0db62d6e525bcd4aafbb660a842733bdbf650be9"}, + {file = "ruff-0.0.276-py3-none-win32.whl", hash = "sha256:1b34a3673b2e5d97df8f7f04090c0b74e9ae6d3d172921d0e0781192954afddf"}, + {file = "ruff-0.0.276-py3-none-win_amd64.whl", hash = "sha256:02deadc0f6abead6cc2d38ddd7100a52aba27a0d90315facaa44b8b4acdba162"}, + {file = "ruff-0.0.276-py3-none-win_arm64.whl", hash = "sha256:a6bd5b53ac689a43c7afc45bd574a7b3efe0ceb192e26e95a055c770ef2045b9"}, + {file = "ruff-0.0.276.tar.gz", hash = "sha256:d456c86eb6ce9225507f24fcc7bf72fa031bb7cc750023310e62889bf4ad4b6a"}, +] + [[package]] name = "secretstorage" version = "3.3.3" description = "Python bindings to FreeDesktop.org Secret Service API" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1679,7 +1419,6 @@ jeepney = ">=0.6" name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1691,7 +1430,6 @@ files = [ name = "stack-data" version = "0.6.2" description = "Extract data from python stack frames and tracebacks for informative displays" -category = "dev" optional = false python-versions = "*" files = [ @@ -1711,7 +1449,6 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1723,7 +1460,6 @@ files = [ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1733,37 +1469,35 @@ files = [ [[package]] name = "tox" -version = "4.4.12" +version = "4.6.3" description = "tox is a generic virtualenv management and test command line tool" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "tox-4.4.12-py3-none-any.whl", hash = "sha256:d4be558809d86fad13f4553976b0500352630a8fbfa39ea4b1ce3bd945ba680b"}, - {file = "tox-4.4.12.tar.gz", hash = "sha256:740f5209d0dec19451b951ee5b1cce4a207acdc7357af84dbc8ec35bcf2c454e"}, + {file = "tox-4.6.3-py3-none-any.whl", hash = "sha256:2946a0bb38924c3a9f9575c7fb4ca1f4c11a7c69c61592f176778892155cb50c"}, + {file = "tox-4.6.3.tar.gz", hash = "sha256:9e2c5091a117d03b583c57c4c40aecd068099c17d40520e7b165e85c19334534"}, ] [package.dependencies] -cachetools = ">=5.3" +cachetools = ">=5.3.1" chardet = ">=5.1" colorama = ">=0.4.6" -filelock = ">=3.11" -packaging = ">=23" -platformdirs = ">=3.2" +filelock = ">=3.12.2" +packaging = ">=23.1" +platformdirs = ">=3.5.3" pluggy = ">=1" -pyproject-api = ">=1.5.1" +pyproject-api = ">=1.5.2" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} -virtualenv = ">=20.21" +virtualenv = ">=20.23.1" [package.extras] -docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-argparse-cli (>=1.11)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)", "sphinx-copybutton (>=0.5.1)", "sphinx-inline-tabs (>=2022.1.2b11)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "devpi-process (>=0.3)", "diff-cover (>=7.5)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.14)", "psutil (>=5.9.4)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-xdist (>=3.2.1)", "re-assert (>=1.1)", "time-machine (>=2.9)", "wheel (>=0.40)"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.23.2,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.1.1)", "devpi-process (>=0.3.1)", "diff-cover (>=7.6)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17.1)", "psutil (>=5.9.5)", "pytest (>=7.3.2)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.10)", "wheel (>=0.40)"] [[package]] name = "traitlets" version = "5.9.0" description = "Traitlets Python configuration system" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1779,7 +1513,6 @@ test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] name = "typing-extensions" version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1791,7 +1524,6 @@ files = [ name = "tzdata" version = "2022.7" description = "Provider of IANA time zone data" -category = "main" optional = false python-versions = ">=2" files = [ @@ -1801,18 +1533,16 @@ files = [ [[package]] name = "tzlocal" -version = "4.3" +version = "5.0.1" description = "tzinfo object for the local timezone" -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "tzlocal-4.3-py3-none-any.whl", hash = "sha256:b44c4388f3d34f25862cfbb387578a4d70fec417649da694a132f628a23367e2"}, - {file = "tzlocal-4.3.tar.gz", hash = "sha256:3f21d09e1b2aa9f2dacca12da240ca37de3ba5237a93addfd6d593afe9073355"}, + {file = "tzlocal-5.0.1-py3-none-any.whl", hash = "sha256:f3596e180296aaf2dbd97d124fe76ae3a0e3d32b258447de7b939b3fd4be992f"}, + {file = "tzlocal-5.0.1.tar.gz", hash = "sha256:46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803"}, ] [package.dependencies] -pytz-deprecation-shim = "*" tzdata = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] @@ -1822,7 +1552,6 @@ devenv = ["black", "check-manifest", "flake8", "pyroma", "pytest (>=4.3)", "pyte name = "urllib3" version = "1.26.14" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1837,30 +1566,28 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.21.0" +version = "20.23.1" description = "Virtual Python Environment builder" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.21.0-py3-none-any.whl", hash = "sha256:31712f8f2a17bd06234fa97fdf19609e789dd4e3e4bf108c3da71d710651adbc"}, - {file = "virtualenv-20.21.0.tar.gz", hash = "sha256:f50e3e60f990a0757c9b68333c9fdaa72d7188caa417f96af9e52407831a3b68"}, + {file = "virtualenv-20.23.1-py3-none-any.whl", hash = "sha256:34da10f14fea9be20e0fd7f04aba9732f84e593dac291b757ce42e3368a39419"}, + {file = "virtualenv-20.23.1.tar.gz", hash = "sha256:8ff19a38c1021c742148edc4f81cb43d7f8c6816d2ede2ab72af5b84c749ade1"}, ] [package.dependencies] distlib = ">=0.3.6,<1" -filelock = ">=3.4.1,<4" -platformdirs = ">=2.4,<4" +filelock = ">=3.12,<4" +platformdirs = ">=3.5.1,<4" [package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -test = ["covdefaults (>=2.2.2)", "coverage (>=7.1)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23)", "pytest (>=7.2.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"] [[package]] name = "watchdog" version = "2.3.0" description = "Filesystem events monitoring" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1901,7 +1628,6 @@ watchmedo = ["PyYAML (>=3.10)"] name = "wcwidth" version = "0.2.6" description = "Measures the displayed width of unicode strings in a terminal" -category = "dev" optional = false python-versions = "*" files = [ @@ -1913,7 +1639,6 @@ files = [ name = "xmltodict" version = "0.13.0" description = "Makes working with XML feel like you are working with JSON" -category = "dev" optional = false python-versions = ">=3.4" files = [ @@ -1925,7 +1650,6 @@ files = [ name = "zipp" version = "3.14.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1940,4 +1664,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = ">=3.10.0, <3.13" -content-hash = "39227dc2d020370fccdb4c3a0c140dbe1cda73e0ea703abb5d9c6857d2c145b9" +content-hash = "83b197f5b2f23aa128778616baf8a0300e8a4fb0ad8ac539593cf4e7702fa5e6" diff --git a/pyproject.toml b/pyproject.toml index 2f905919..e752aa2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "jrnl" -version = "v4.0-beta3" +version = "v4.0.1" description = "Collect your thoughts and notes without leaving the command line." authors = [ "jrnl contributors ", @@ -35,7 +35,7 @@ keyring = ">=21.0" # https://github.com/jaraco/keyring#integration parsedatetime = ">=2.6" python-dateutil = "^2.8" # https://github.com/dateutil/dateutil/blob/master/RELEASING pyxdg = ">=0.27.0" -"ruamel.yaml" = "^0.17.21" +"ruamel.yaml" = ">=0.17.22" rich = ">=12.2.0, <14.0.0" # dayone-only deps @@ -43,13 +43,7 @@ tzlocal = ">=4.0" # https://github.com/regebro/tzlocal/blob/master/CHANGES.txt [tool.poetry.dev-dependencies] black = { version = ">=21.5b2", allow-prereleases = true } -flakeheaven = ">=3.0" -flake8-black = ">=0.3.3" -flake8-isort = ">=5.0.0" -flake8-type-checking = ">=2.2.0" -flake8-simplify = ">=0.19" ipdb = "*" -isort = ">=5.10" mkdocs = ">=1.4" parse-type = ">=0.6.0" poethepoet = "*" @@ -58,6 +52,7 @@ pytest-bdd = ">=6.0" pytest-clarity = "*" pytest-xdist = ">=2.5.0" requests = "*" +ruff = ">=0.0.276" toml = ">=0.10" tox = "*" xmltodict = "*" @@ -87,18 +82,18 @@ test-run = [ # Groups of tasks format.default_item_type = "cmd" format.sequence = [ - "isort .", + "ruff check . --select I --fix", # equivalent to "isort ." "black .", ] -lint.env = { FLAKEHEAVEN_CACHE_TIMEOUT = "0" } lint.default_item_type = "cmd" lint.sequence = [ "poetry --version", "poetry check", - "flakeheaven --version", - "flakeheaven plugins", - "flakeheaven lint", + "ruff --version", + "ruff .", + "black --version", + "black --check ." ] test = [ @@ -106,11 +101,6 @@ test = [ "test-run", ] -[tool.isort] -profile = "black" -force_single_line = true -known_first_party = ["jrnl", "tests"] - [tool.pytest.ini_options] minversion = "6.0" required_plugins = [ @@ -136,29 +126,35 @@ filterwarnings = [ "ignore:[WinError 5].*" ] -[tool.flakeheaven] -max_line_length = 88 +[tool.ruff] +line-length = 88 + +# https://beta.ruff.rs/docs/rules/ +select = [ + 'F', # Pyflakes + 'E', # pycodestyle errors + 'W', # pycodestyle warnings + 'I', # isort + 'ASYNC', # flake8-async + 'S110', # try-except-pass + 'S112', # try-except-continue + 'EM', # flake8-errmsg + 'ISC', # flake8-implicit-str-concat + 'Q', # flake8-quotes + 'RSE', # flake8-raise + 'TID', # flake8-tidy-imports + 'TCH', # flake8-type-checking + 'T100', # debugger, don't allow break points + 'ICN' # flake8-import-conventions +] exclude = [".git", ".tox", ".venv", "node_modules"] -[tool.flakeheaven.plugins] -"py*" = ["+*"] -pycodestyle = [ - "-E101", - "-E111", "-E114", "-E115", "-E116", "-E117", - "-E12*", - "-E13*", - "-E2*", - "-E3*", - "-E401", - "-E5*", - "-E70", - "-W1*", "-W2*", "-W3*", "-W5*", -] -"flake8-*" = ["+*"] -flake8-black = ["-BLK901"] +[tool.ruff.isort] +force-single-line = true +known-first-party = ["jrnl", "tests"] -[tool.flakeheaven.exceptions."jrnl/journals/__init__.py"] -pyflakes = ["-F401"] +[tool.ruff.per-file-ignores] +"__init__.py" = ["F401"] # unused imports [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/bdd/features/template.feature b/tests/bdd/features/template.feature index a6a69b9f..ab13115a 100644 --- a/tests/bdd/features/template.feature +++ b/tests/bdd/features/template.feature @@ -38,7 +38,7 @@ Feature: Using templates And we use the password "test" if prompted When we run "jrnl --template this_template_does_not_exist.template" Then we should get an error - Then the error output should contain "Unable to find a template file based on the passed arg" + Then the error output should contain "Unable to find a template file" Examples: configs | config_file | diff --git a/tests/lib/fixtures.py b/tests/lib/fixtures.py index b9cf0ea4..168a8007 100644 --- a/tests/lib/fixtures.py +++ b/tests/lib/fixtures.py @@ -183,10 +183,10 @@ def mock_default_journal_path(temp_dir): @fixture def mock_default_templates_path(temp_dir): - templates_path = Path(temp_dir.name, "templates") + templates_path = os.path.join(temp_dir.name, "templates") return { "get_templates_path": lambda: patch( - "jrnl.controller.get_templates_path", return_value=templates_path + "jrnl.editor.get_templates_path", return_value=templates_path ), } diff --git a/tests/lib/then_steps.py b/tests/lib/then_steps.py index 7eaf07d6..7216a23f 100644 --- a/tests/lib/then_steps.py +++ b/tests/lib/then_steps.py @@ -38,38 +38,41 @@ def output_should_match(regex, cli_run): assert matches, f"\nRegex didn't match:\n{regex}\n{str(out)}\n{str(matches)}" -@then(parse("the output {it_should:Should} contain\n{expected_output}", SHOULD_DICT)) -@then(parse('the output {it_should:Should} contain "{expected_output}"', SHOULD_DICT)) +@then(parse("the output {it_should:Should} contain\n{expected}", SHOULD_DICT)) +@then(parse('the output {it_should:Should} contain "{expected}"', SHOULD_DICT)) @then( parse( - "the {which_output_stream} output {it_should:Should} contain\n{expected_output}", + "the {which_output_stream} output {it_should:Should} contain\n{expected}", SHOULD_DICT, ) ) @then( parse( - 'the {which_output_stream} output {it_should:Should} contain "{expected_output}"', + 'the {which_output_stream} output {it_should:Should} contain "{expected}"', SHOULD_DICT, ) ) -def output_should_contain(expected_output, which_output_stream, cli_run, it_should): - output_str = f"\nEXPECTED:\n{expected_output}\n\nACTUAL STDOUT:\n{cli_run['stdout']}\n\nACTUAL STDERR:\n{cli_run['stderr']}" - assert expected_output +def output_should_contain(expected, which_output_stream, cli_run, it_should): + output_str = ( + f"\nEXPECTED:\n{expected}\n\n" + f"ACTUAL STDOUT:\n{cli_run['stdout']}\n\n" + f"ACTUAL STDERR:\n{cli_run['stderr']}" + ) + + assert expected if which_output_stream is None: - assert ((expected_output in cli_run["stdout"]) == it_should) or ( - (expected_output in cli_run["stderr"]) == it_should + assert ((expected in cli_run["stdout"]) == it_should) or ( + (expected in cli_run["stderr"]) == it_should ), output_str elif which_output_stream == "standard": - assert (expected_output in cli_run["stdout"]) == it_should, output_str + assert (expected in cli_run["stdout"]) == it_should, output_str elif which_output_stream == "error": - assert (expected_output in cli_run["stderr"]) == it_should, output_str + assert (expected in cli_run["stderr"]) == it_should, output_str else: - assert ( - expected_output in cli_run[which_output_stream] - ) == it_should, output_str + assert (expected in cli_run[which_output_stream]) == it_should, output_str @then(parse("the output should not contain\n{expected_output}")) @@ -119,7 +122,8 @@ def output_should_be_columns_wide(cli_run, width): @then( parse( - 'the default journal "{journal_file}" should be in the "{journal_dir}" directory' + 'the default journal "{journal_file}" ' + 'should be in the "{journal_dir}" directory' ) ) def default_journal_location(journal_file, journal_dir, config_on_disk, temp_dir): @@ -135,13 +139,15 @@ def default_journal_location(journal_file, journal_dir, config_on_disk, temp_dir @then( parse( - 'the config for journal "{journal_name}" {it_should:Should} contain "{some_yaml}"', + 'the config for journal "{journal_name}" ' + '{it_should:Should} contain "{some_yaml}"', SHOULD_DICT, ) ) @then( parse( - 'the config for journal "{journal_name}" {it_should:Should} contain\n{some_yaml}', + 'the config for journal "{journal_name}" ' + "{it_should:Should} contain\n{some_yaml}", SHOULD_DICT, ) ) @@ -157,20 +163,22 @@ def config_var_on_disk(config_on_disk, journal_name, it_should, some_yaml): actual_slice = actual if type(actual) is dict: # `expected` objects formatted in yaml only compare one level deep - actual_slice = {key: actual.get(key, None) for key in expected.keys()} + actual_slice = {key: actual.get(key) for key in expected.keys()} assert (expected == actual_slice) == it_should @then( parse( - 'the config in memory for journal "{journal_name}" {it_should:Should} contain "{some_yaml}"', + 'the config in memory for journal "{journal_name}" ' + '{it_should:Should} contain "{some_yaml}"', SHOULD_DICT, ) ) @then( parse( - 'the config in memory for journal "{journal_name}" {it_should:Should} contain\n{some_yaml}', + 'the config in memory for journal "{journal_name}" ' + "{it_should:Should} contain\n{some_yaml}", SHOULD_DICT, ) ) diff --git a/tests/unit/test_editor.py b/tests/unit/test_editor.py new file mode 100644 index 00000000..5c21daca --- /dev/null +++ b/tests/unit/test_editor.py @@ -0,0 +1,45 @@ +# Copyright © 2012-2023 jrnl contributors +# License: https://www.gnu.org/licenses/gpl-3.0.html + +import os +from unittest.mock import mock_open +from unittest.mock import patch + +import pytest + +from jrnl.editor import get_template_path +from jrnl.editor import read_template_file +from jrnl.exception import JrnlException + + +@patch( + "os.getcwd", side_effect="/" +) # prevent failures in CI if current directory has been deleted +@patch("builtins.open", side_effect=FileNotFoundError()) +def test_read_template_file_with_no_file_raises_exception(mock_open, mock_getcwd): + with pytest.raises(JrnlException) as ex: + read_template_file("invalid_file.txt") + assert isinstance(ex.value, JrnlException) + + +@patch( + "os.getcwd", side_effect="/" +) # prevent failures in CI if current directory has been deleted +@patch("builtins.open", new_callable=mock_open, read_data="template text") +def test_read_template_file_with_valid_file_returns_text(mock_file, mock_getcwd): + assert read_template_file("valid_file.txt") == "template text" + + +def test_get_template_path_when_exists_returns_correct_path(): + with patch("os.path.exists", return_value=True): + output = get_template_path("template", "templatepath") + + assert output == os.path.join("templatepath", "template") + + +@patch("jrnl.editor.absolute_path") +def test_get_template_path_when_doesnt_exist_returns_correct_path(mock_absolute_paths): + with patch("os.path.exists", return_value=False): + output = get_template_path("template", "templatepath") + + assert output == mock_absolute_paths.return_value diff --git a/tests/unit/test_parse_args.py b/tests/unit/test_parse_args.py index fd1d0844..e3036020 100644 --- a/tests/unit/test_parse_args.py +++ b/tests/unit/test_parse_args.py @@ -242,7 +242,9 @@ def test_color_override(): def test_multiple_overrides(): parsed_args = cli_as_dict( - '--config-override colors.title green --config-override editor "nano" --config-override journal.scratchpad "/tmp/scratchpad"' + "--config-override colors.title green " + '--config-override editor "nano" ' + '--config-override journal.scratchpad "/tmp/scratchpad"' ) assert parsed_args == expected_args( config_override=[