Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package jrnl for openSUSE:Factory checked in 
at 2024-06-10 17:38:35
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/jrnl (Old)
 and      /work/SRC/openSUSE:Factory/.jrnl.new.19518 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "jrnl"

Mon Jun 10 17:38:35 2024 rev:2 rq:1179696 version:4.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/jrnl/jrnl.changes        2023-08-22 
08:56:49.182635562 +0200
+++ /work/SRC/openSUSE:Factory/.jrnl.new.19518/jrnl.changes     2024-06-10 
17:38:54.193771778 +0200
@@ -1,0 +2,15 @@
+Wed May 22 15:43:14 UTC 2024 - malcolmle...@opensuse.org
+
+- Updated to version 4.1:
+  + Add Python 3.12 support, (gh#jrnl-org/jrnl#1761).
+  + Set new required build fields in the ReadTheDocs config file,
+    (gh#jrnl-org/jrnl#1803).
+  + Replace flake8 and isort with ruff linter and add black --check
+    to linting step, (gh#jrnl-org/jrnl#1763).
+  + Add note about messages going to stderr and the implication for
+    piping, (gh#jrnl-org/jrnl#1768).
+- Drop requires on ansiwrap.
+- Add note about using the keyring to encrypt, (boo#1223003,
+  gh#jrnl-org/jrnl#1883 and gh#marcus-h/python-keyring-keyutils#1).
+
+-------------------------------------------------------------------

Old:
----
  jrnl-4.0.1.tar.gz

New:
----
  jrnl-4.1.tar.gz
  keyring_note.md

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ jrnl.spec ++++++
--- /var/tmp/diff_new_pack.S0vcBy/_old  2024-06-10 17:38:54.937799301 +0200
+++ /var/tmp/diff_new_pack.S0vcBy/_new  2024-06-10 17:38:54.937799301 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package jrnl
 #
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -15,28 +15,29 @@
 # Please submit bugfixes or comments via https://bugs.opensuse.org/
 #
 
+
 %define pythons python3
 
 Name:           jrnl
-Version:        4.0.1
+Version:        4.1
 Release:        0
 Summary:        Collect your thoughts and notes without leaving the command 
line
 License:        GPL-3.0-only
 URL:            https://jrnl.sh
 Source0:        
https://files.pythonhosted.org/packages/source/j/jrnl/jrnl-%{version}.tar.gz
+Source99:       keyring_note.md
 #PATCH-FIX-OPENSUSE jrnl-dateutil.patch malcolmle...@opensuse.org -- Fix 
dateutil naming convention.
 Patch0:         jrnl-dateutil.patch
+BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 BuildRequires:  python3-pip
 BuildRequires:  python3-poetry-core >= 1.0.0
-BuildRequires:  fdupes
 ## MANUAL BEGIN
-Requires:       python3-ansiwrap
 Requires:       python3-colorama
 Requires:       python3-cryptography
+Requires:       python3-dateutil
 Requires:       python3-keyring
 Requires:       python3-parsedatetime
-Requires:       python3-dateutil
 Requires:       python3-pyxdg
 Requires:       python3-rich
 Requires:       python3-ruamel.yaml
@@ -52,6 +53,7 @@
 
 %prep
 %autosetup -p1
+cp %{S:99} .
 
 %build
 %pyproject_wheel
@@ -61,7 +63,7 @@
 %fdupes %{buildroot}%{python_sitelib}
 
 %files
-%doc README.md
+%doc README.md keyring_note.md
 %license LICENSE.md
 %{_bindir}/jrnl
 %{python_sitelib}/jrnl

++++++ jrnl-4.0.1.tar.gz -> jrnl-4.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/PKG-INFO new/jrnl-4.1/PKG-INFO
--- old/jrnl-4.0.1/PKG-INFO     1970-01-01 01:00:00.000000000 +0100
+++ new/jrnl-4.1/PKG-INFO       1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: jrnl
-Version: 4.0.1
+Version: 4.1
 Summary: Collect your thoughts and notes without leaving the command line.
 Home-page: https://jrnl.sh
 License: GPL-3.0-only
@@ -15,8 +15,8 @@
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
 Classifier: Topic :: Office/Business :: News/Diary
-Requires-Dist: ansiwrap (>=0.8.4,<0.9.0)
 Requires-Dist: colorama (>=0.4)
 Requires-Dist: cryptography (>=3.0)
 Requires-Dist: keyring (>=21.0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/__version__.py 
new/jrnl-4.1/jrnl/__version__.py
--- old/jrnl-4.0.1/jrnl/__version__.py  2023-06-21 01:39:40.212125000 +0200
+++ new/jrnl-4.1/jrnl/__version__.py    2023-11-04 20:35:55.613247900 +0100
@@ -1 +1 @@
-__version__ = "v4.0.1"
+__version__ = "v4.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/args.py new/jrnl-4.1/jrnl/args.py
--- old/jrnl-4.0.1/jrnl/args.py 2023-06-21 01:39:27.671273700 +0200
+++ new/jrnl-4.1/jrnl/args.py   2023-11-04 20:35:48.929271700 +0100
@@ -78,7 +78,7 @@
             """
         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 @@
     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 @@
         "-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 @@
         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 @@
         ),
     )
 
-    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 @@
         "--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 @@
             \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 @@
             \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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/commands.py 
new/jrnl-4.1/jrnl/commands.py
--- old/jrnl-4.0.1/jrnl/commands.py     2023-06-21 01:39:27.671273700 +0200
+++ new/jrnl-4.1/jrnl/commands.py       2023-11-04 20:35:48.929271700 +0100
@@ -142,7 +142,7 @@
 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/config.py new/jrnl-4.1/jrnl/config.py
--- old/jrnl-4.0.1/jrnl/config.py       2023-06-21 01:39:27.671273700 +0200
+++ new/jrnl-4.1/jrnl/config.py 2023-11-04 20:35:48.929271700 +0100
@@ -37,9 +37,10 @@
     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
     """
 
@@ -105,7 +106,7 @@
         return config
     config = config.copy()
     journal_conf = config["journals"].get(journal_name)
-    if type(journal_conf) is dict:
+    if isinstance(journal_conf, dict):
         # We can override the default config on a by-journal basis
         logging.debug(
             "Updating configuration with specific journal overrides:\n%s",
@@ -180,7 +181,7 @@
     """Updates a config dict with new values - either global if scope is None
     or config['journals'][scope] is just a string pointing to a journal file,
     or within the scope"""
-    if scope and type(config["journals"][scope]) is dict:  # Update to journal 
specific
+    if scope and isinstance(config["journals"][scope], dict):
         config["journals"][scope].update(new_config)
     elif scope and force_local:  # Convert to dict
         config["journals"][scope] = {"journal": config["journals"][scope]}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/controller.py 
new/jrnl-4.1/jrnl/controller.py
--- old/jrnl-4.0.1/jrnl/controller.py   2023-06-21 01:39:27.671273700 +0200
+++ new/jrnl-4.1/jrnl/controller.py     2023-11-04 20:35:48.929271700 +0100
@@ -34,9 +34,9 @@
 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)
@@ -181,7 +181,9 @@
 def _get_template(args, config) -> str:
     # Read template file and pass as raw text into the composer
     logging.debug(
-        f"Get template:\n--template: {args.template}\nfrom config: 
{config.get('template')}"
+        "Get template:\n"
+        f"--template: {args.template}\n"
+        f"from config: {config.get('template')}"
     )
     template_path = args.template or config.get("template")
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/editor.py new/jrnl-4.1/jrnl/editor.py
--- old/jrnl-4.0.1/jrnl/editor.py       2023-06-21 01:39:27.671273700 +0200
+++ new/jrnl-4.1/jrnl/editor.py 2023-11-04 20:35:48.929271700 +0100
@@ -81,7 +81,8 @@
     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."
+            f"Couldn't open {actual_template_path}. "
+            "Treating template path like a local / abs path."
         )
         actual_template_path = absolute_path(template_path)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/install.py 
new/jrnl-4.1/jrnl/install.py
--- old/jrnl-4.0.1/jrnl/install.py      2023-06-21 01:39:27.671273700 +0200
+++ new/jrnl-4.1/jrnl/install.py        2023-11-04 20:35:48.929271700 +0100
@@ -31,11 +31,11 @@
 
 
 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 _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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/journals/Entry.py 
new/jrnl-4.1/jrnl/journals/Entry.py
--- old/jrnl-4.0.1/jrnl/journals/Entry.py       2023-06-21 01:39:27.671273700 
+0200
+++ new/jrnl-4.1/jrnl/journals/Entry.py 2023-11-04 20:35:48.933271600 +0100
@@ -7,10 +7,9 @@
 import re
 from typing import TYPE_CHECKING
 
-import ansiwrap
-
 from jrnl.color import colorize
 from jrnl.color import highlight_tags_with_background_color
+from jrnl.output import wrap_with_ansi_colors
 
 if TYPE_CHECKING:
     from .Journal import Journal
@@ -89,7 +88,7 @@
         }
 
     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:
@@ -129,7 +128,7 @@
                     columns = 79
 
             # Color date / title and bold title
-            title = ansiwrap.fill(
+            title = wrap_with_ansi_colors(
                 date_str
                 + " "
                 + highlight_tags_with_background_color(
@@ -143,35 +142,17 @@
             body = highlight_tags_with_background_color(
                 self, self.body.rstrip(" \n"), 
self.journal.config["colors"]["body"]
             )
-            body_text = [
-                colorize(
-                    ansiwrap.fill(
-                        line,
-                        columns,
-                        initial_indent=indent,
-                        subsequent_indent=indent,
-                        drop_whitespace=True,
-                    ),
-                    self.journal.config["colors"]["body"],
-                )
-                or indent
-                for line in body.rstrip(" \n").splitlines()
-            ]
-
-            # ansiwrap doesn't handle lines with only the "\n" character and 
some
-            # ANSI escapes properly, so we have this hack here to make sure the
-            # beginning of each line has the indent character and it's colored
-            # properly. textwrap doesn't have this issue, however, it doesn't 
wrap
-            # the strings properly as it counts ANSI escapes as literal 
characters.
-            # TL;DR: I'm sorry.
-            body = "\n".join(
-                [
+
+            body = wrap_with_ansi_colors(body, columns - len(indent))
+            if indent:
+                # Without explicitly colorizing the indent character, it will 
lose its
+                # color after a tag appears.
+                body = "\n".join(
                     colorize(indent, self.journal.config["colors"]["body"]) + 
line
-                    if not ansiwrap.strip_color(line).startswith(indent)
-                    else line
-                    for line in body_text
-                ]
-            )
+                    for line in body.splitlines()
+                )
+
+            body = colorize(body, self.journal.config["colors"]["body"])
         else:
             title = (
                 date_str
@@ -233,7 +214,7 @@
     \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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/journals/FolderJournal.py 
new/jrnl-4.1/jrnl/journals/FolderJournal.py
--- old/jrnl-4.0.1/jrnl/journals/FolderJournal.py       2023-06-21 
01:39:27.675274000 +0200
+++ new/jrnl-4.1/jrnl/journals/FolderJournal.py 2023-11-04 20:35:48.933271600 
+0100
@@ -122,7 +122,8 @@
 
     @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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/journals/Journal.py 
new/jrnl-4.1/jrnl/journals/Journal.py
--- old/jrnl-4.0.1/jrnl/journals/Journal.py     2023-06-21 01:39:27.675274000 
+0200
+++ new/jrnl-4.1/jrnl/journals/Journal.py       2023-11-04 20:35:48.933271600 
+0100
@@ -102,7 +102,7 @@
         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 @@
         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 @@
     @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 @@
 
     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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/messages/MsgText.py 
new/jrnl-4.1/jrnl/messages/MsgText.py
--- old/jrnl-4.0.1/jrnl/messages/MsgText.py     2023-06-21 01:39:27.675274000 
+0200
+++ new/jrnl-4.1/jrnl/messages/MsgText.py       2023-11-04 20:35:48.933271600 
+0100
@@ -43,8 +43,8 @@
         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?"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/output.py new/jrnl-4.1/jrnl/output.py
--- old/jrnl-4.0.1/jrnl/output.py       2023-06-21 01:39:27.675274000 +0200
+++ new/jrnl-4.1/jrnl/output.py 2023-11-04 20:35:48.933271600 +0100
@@ -131,3 +131,12 @@
     text = textwrap.dedent(text)
     text = text.strip()
     return Text(text)
+
+
+def wrap_with_ansi_colors(text: str, width: int) -> str:
+    richtext = Text.from_ansi(text, no_wrap=False, tab_size=None)
+
+    console = Console(width=width, force_terminal=True)
+    with console.capture() as capture:
+        console.print(richtext, sep="", end="")
+    return capture.get()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/override.py 
new/jrnl-4.1/jrnl/override.py
--- old/jrnl-4.0.1/jrnl/override.py     2023-06-21 01:39:27.675274000 +0200
+++ new/jrnl-4.1/jrnl/override.py       2023-11-04 20:35:48.933271600 +0100
@@ -56,7 +56,8 @@
 
     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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/plugins/fancy_exporter.py 
new/jrnl-4.1/jrnl/plugins/fancy_exporter.py
--- old/jrnl-4.0.1/jrnl/plugins/fancy_exporter.py       2023-06-21 
01:39:27.675274000 +0200
+++ new/jrnl-4.1/jrnl/plugins/fancy_exporter.py 2023-11-04 20:35:48.933271600 
+0100
@@ -18,7 +18,7 @@
 
 
 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/plugins/tag_exporter.py 
new/jrnl-4.1/jrnl/plugins/tag_exporter.py
--- old/jrnl-4.0.1/jrnl/plugins/tag_exporter.py 2023-06-21 01:39:27.675274000 
+0200
+++ new/jrnl-4.1/jrnl/plugins/tag_exporter.py   2023-11-04 20:35:48.933271600 
+0100
@@ -12,7 +12,7 @@
 
 
 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/plugins/util.py 
new/jrnl-4.1/jrnl/plugins/util.py
--- old/jrnl-4.0.1/jrnl/plugins/util.py 2023-06-21 01:39:27.675274000 +0200
+++ new/jrnl-4.1/jrnl/plugins/util.py   2023-11-04 20:35:48.933271600 +0100
@@ -10,7 +10,8 @@
 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/plugins/yaml_exporter.py 
new/jrnl-4.1/jrnl/plugins/yaml_exporter.py
--- old/jrnl-4.0.1/jrnl/plugins/yaml_exporter.py        2023-06-21 
01:39:27.675274000 +0200
+++ new/jrnl-4.1/jrnl/plugins/yaml_exporter.py  2023-11-04 20:35:48.933271600 
+0100
@@ -18,14 +18,15 @@
 
 
 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 @@
         # 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/jrnl/time.py new/jrnl-4.1/jrnl/time.py
--- old/jrnl-4.0.1/jrnl/time.py 2023-06-21 01:39:27.675274000 +0200
+++ new/jrnl-4.1/jrnl/time.py   2023-11-04 20:35:48.933271600 +0100
@@ -9,14 +9,11 @@
 
 
 def __get_pdt_calendar():
-    try:
-        import parsedatetime.parsedatetime_consts as pdt
-    except ImportError:
-        import parsedatetime as pdt
+    import parsedatetime as pdt
 
     consts = pdt.Constants(usePyICU=False)
     consts.DOWParseStyle = -1  # "Monday" will be either today or the last 
Monday
-    calendar = pdt.Calendar(consts)
+    calendar = pdt.Calendar(consts, version=pdt.VERSION_CONTEXT_STYLE)
 
     return calendar
 
@@ -34,14 +31,18 @@
     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
 
     default_date = DEFAULT_FUTURE if inclusive else DEFAULT_PAST
     date = None
     year_present = False
+
+    hasTime = False
+    hasDate = False
+
     while not date:
         try:
             from dateutil.parser import parse as dateparse
@@ -53,7 +54,8 @@
                 )
             else:
                 year_present = True
-            flag = 1 if date.hour == date.minute == 0 else 2
+            hasTime = not (date.hour == date.minute == 0)
+            hasDate = True
             date = date.timetuple()
         except Exception as e:
             if e.args[0] == "day is out of range for month":
@@ -61,9 +63,11 @@
                 default_date = datetime.datetime(y, m, d - 1, H, M, S)
             else:
                 calendar = __get_pdt_calendar()
-                date, flag = calendar.parse(date_str)
+                date, parse_context = calendar.parse(date_str)
+                hasTime = parse_context.hasTime
+                hasDate = parse_context.hasDate
 
-    if not flag:  # Oops, unparsable.
+    if not hasDate and not hasTime:
         try:  # Try and parse this as a single year
             year = int(date_str)
             return datetime.datetime(year, 1, 1)
@@ -72,8 +76,8 @@
         except TypeError:
             return None
 
-    if flag == 1:  # Date found, but no time. Use the default time.
-        date = datetime.datetime(
+    if hasDate and not hasTime:
+        date = datetime.datetime(  # Use the default time
             *date[:3],
             hour=23 if inclusive else default_hour or 0,
             minute=59 if inclusive else default_minute or 0,
@@ -82,9 +86,9 @@
     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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jrnl-4.0.1/pyproject.toml new/jrnl-4.1/pyproject.toml
--- old/jrnl-4.0.1/pyproject.toml       2023-06-21 01:39:40.156121300 +0200
+++ new/jrnl-4.1/pyproject.toml 2023-11-04 20:35:55.581247800 +0100
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "jrnl"
-version = "v4.0.1"
+version = "v4.1"
 description = "Collect your thoughts and notes without leaving the command 
line."
 authors = [
     "jrnl contributors <maintain...@jrnl.sh>",
@@ -29,7 +29,6 @@
 [tool.poetry.dependencies]
 python = ">=3.10.0, <3.13"
 
-ansiwrap = "^0.8.4"
 colorama = ">=0.4"       # 
https://github.com/tartley/colorama/blob/master/CHANGELOG.rst
 cryptography = ">=3.0"   # https://cryptography.io/en/latest/api-stability.html
 keyring = ">=21.0"       # https://github.com/jaraco/keyring#integration
@@ -44,13 +43,7 @@
 
 [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 = "*"
@@ -59,6 +52,7 @@
 pytest-clarity = "*"
 pytest-xdist = ">=2.5.0"
 requests = "*"
+ruff = ">=0.0.276"
 toml = ">=0.10"
 tox = "*"
 xmltodict = "*"
@@ -88,18 +82,18 @@
 # 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 = [
@@ -107,11 +101,6 @@
   "test-run",
 ]
 
-[tool.isort]
-profile = "black"
-force_single_line = true
-known_first_party = ["jrnl", "tests"]
-
 [tool.pytest.ini_options]
 minversion = "6.0"
 required_plugins = [
@@ -132,34 +121,40 @@
 
 filterwarnings = [
     "ignore::DeprecationWarning",
-    "ignore:Flag style will be deprecated in.*",
     "ignore:[WinError 32].*",
     "ignore:[WinError 5].*"
 ]
 
-[tool.flakeheaven]
-max_line_length = 88
+[tool.ruff]
+line-length = 88
+target-version = "py310"
+
+# 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"]

++++++ keyring_note.md ++++++
#### Using jrnl with encryption and keyring
If your reading this, your likely running osc on your system and seeing the 
following error `AttributeError: '_PasswordRetriever' object has no attribute 
'encode'`

This is an upstream issue with `python3{ver}-keyring-keyutils` with an upstream 
report <sup>Ref 3</sup> created in September 2022 with no action at this time.

Please use either of the following workarounds.

#### Workaround 1
Drop into the python3 interpretor by running `python3` and run;
```bash
import keyring
journal_name = "default" # Should match name in  `jrnl --list`
password = "mypassword" # Change to your journal's password
keyring.set_password("jrnl", journal_name, password)

exit()
```
#### Workaround 2
Uninstall python3{ver}-keyring-keyutils, create your password in the keyring 
for jrnl, then re-install python3{ver}-keyring-keyutils.

---
Ref 1: [openSUSE Bug Report 
(boo#1223003)](https://bugzilla.suse.com/show_bug.cgi?id=1223003)

Ref 2: [Upstream Bug 
(gh#jrnl-org/jrnl#1883)](https://github.com/jrnl-org/jrnl/issues/1883)

Ref 3: [python-keyring-keyutils (gh#marcus-h/python-keyring-keyutils#1)](
https://github.com/marcus-h/python-keyring-keyutils/issues/1)

---
(No newline at EOF)

Reply via email to