Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-sievelib for openSUSE:Factory 
checked in at 2026-03-24 18:50:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-sievelib (Old)
 and      /work/SRC/openSUSE:Factory/.python-sievelib.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-sievelib"

Tue Mar 24 18:50:12 2026 rev:10 rq:1342218 version:1.5.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-sievelib/python-sievelib.changes  
2025-01-28 15:00:51.894240833 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-sievelib.new.8177/python-sievelib.changes    
    2026-03-24 18:51:06.724009484 +0100
@@ -1,0 +2,20 @@
+Tue Mar 24 11:59:32 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 1.5.0:
+  * Add optional srvhostname argument for managesieve client
+  * Added support for environment extension.
+  * Improved get_filter_actions() method to support extra args.
+  * Python 3.14 support
+  * Python 3.9 support removed
+- update to 1.4.3:
+  * Added support for address test in factory.
+  * Dropped python 3.8 support and added python 3.12+
+  * Added missing require statement when using create arg with
+    fileinto
+  * Add Notify Extension
+  * Improve code discoverability and fix warnings
+  * Add xoauth2 auth mechanism
+  * Support implicit TLS connections
+  * Refactored SSL connection related code
+
+-------------------------------------------------------------------

Old:
----
  sievelib-1.4.2.tar.gz

New:
----
  sievelib-1.5.0.tar.gz

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

Other differences:
------------------
++++++ python-sievelib.spec ++++++
--- /var/tmp/diff_new_pack.0UYUEQ/_old  2026-03-24 18:51:07.356035626 +0100
+++ /var/tmp/diff_new_pack.0UYUEQ/_new  2026-03-24 18:51:07.356035626 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-sievelib
 #
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 # Copyright (c) 2016 Aeneas Jaissle <[email protected]>
 #
 # All modifications and additions to the file contributed by third parties
@@ -19,7 +19,7 @@
 
 %define modname sievelib
 Name:           python-%{modname}
-Version:        1.4.2
+Version:        1.5.0
 Release:        0
 Summary:        Client-side Sieve and Managesieve library written in Python
 License:        MIT
@@ -27,7 +27,8 @@
 Source:         
https://files.pythonhosted.org/packages/source/s/sievelib/%{modname}-%{version}.tar.gz
 BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module pytest}
-BuildRequires:  %{python_module setuptools_scm}
+BuildRequires:  %{python_module setuptools >= 61.0}
+BuildRequires:  %{python_module setuptools_scm >= 6.4}
 BuildRequires:  %{python_module typing-extensions}
 BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes

++++++ sievelib-1.4.2.tar.gz -> sievelib-1.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/.github/workflows/sievelib.yml 
new/sievelib-1.5.0/.github/workflows/sievelib.yml
--- old/sievelib-1.4.2/.github/workflows/sievelib.yml   2025-01-08 
10:26:03.000000000 +0100
+++ new/sievelib-1.5.0/.github/workflows/sievelib.yml   2026-02-25 
09:16:26.000000000 +0100
@@ -14,7 +14,7 @@
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.8, 3.9, '3.10', '3.11']
+        python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
       fail-fast: false
     steps:
       - uses: actions/checkout@v4
@@ -27,15 +27,15 @@
           pip install codecov pytest pytest-cov
           pip install -e .
       - name: Run tests
-        if: ${{ matrix.python-version != '3.11' }}
+        if: ${{ matrix.python-version != '3.14' }}
         run: |
           pytest
       - name: Run tests and coverage
-        if: ${{ matrix.python-version == '3.11' }}
+        if: ${{ matrix.python-version == '3.14' }}
         run: |
           pytest --cov=sievelib --cov-report xml
       - name: Upload coverage result
-        if: ${{ matrix.python-version == '3.11' }}
+        if: ${{ matrix.python-version == '3.14' }}
         uses: actions/upload-artifact@v4
         with:
           name: coverage-results
@@ -69,13 +69,14 @@
       - uses: actions/checkout@v4
         with:
           fetch-depth: 0
-      - name: Set up Python 3.11
+      - name: Set up Python 3.14
         uses: actions/setup-python@v5
         with:
-          python-version: '3.11'
+          python-version: '3.14'
       - name: Create package
         run: |
-          python setup.py sdist
+          python -m pip install build
+          python -m build
       - name: Publish to Test PyPI
         if: endsWith(github.event.ref, '/master')
         uses: pypa/gh-action-pypi-publish@release/v1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/PKG-INFO new/sievelib-1.5.0/PKG-INFO
--- old/sievelib-1.4.2/PKG-INFO 2025-01-08 10:26:07.744957700 +0100
+++ new/sievelib-1.5.0/PKG-INFO 2026-02-25 09:16:29.279206000 +0100
@@ -1,11 +1,30 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: sievelib
-Version: 1.4.2
+Version: 1.5.0
 Summary: Client-side SIEVE library
-Home-page: https://github.com/tonioo/sievelib
-Author: Antoine Nguyen
-Author-email: [email protected]
-License: MIT
+Author-email: Antoine Nguyen <[email protected]>
+License: Copyright (c) 2011-2024 Antoine Nguyen <[email protected]>
+        
+        Permission is hereby granted, free of charge, to any person obtaining 
a copy
+        of this software and associated documentation files (the "Software"), 
to deal
+        in the Software without restriction, including without limitation the 
rights
+        to use, copy, modify, merge, publish, distribute, sublicense, and/or 
sell
+        copies of the Software, and to permit persons to whom the Software is
+        furnished to do so, subject to the following conditions:
+        
+        The above copyright notice and this permission notice shall be 
included in
+        all copies or substantial portions of the Software.
+        
+        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR
+        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY,
+        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
SHALL THE
+        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
ARISING FROM,
+        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
IN
+        THE SOFTWARE.
+        
+Project-URL: Repository, https://github.com/tonioo/sievelib
+Project-URL: Issues, https://github.com/tonioo/sievelib/issues
 Keywords: sieve,managesieve,parser,client
 Classifier: Programming Language :: Python
 Classifier: Development Status :: 5 - Production/Stable
@@ -14,8 +33,16 @@
 Classifier: Operating System :: OS Independent
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Communications :: Email :: Filters
-Requires-Python: >=3.7
+Requires-Python: >=3.10
+Description-Content-Type: text/x-rst
 License-File: COPYING
+Requires-Dist: typing-extensions
+Provides-Extra: dev
+Requires-Dist: pre-commit; extra == "dev"
+Requires-Dist: black; extra == "dev"
+Requires-Dist: pylint; extra == "dev"
+Requires-Dist: pytest; extra == "dev"
+Dynamic: license-file
 
 sievelib
 ========
@@ -62,6 +89,8 @@
 * Relational (`RFC 5231 <https://tools.ietf.org/html/rfc5231>`_)
 * Imap4flags (`RFC 5232 <https://tools.ietf.org/html/rfc5232>`_)
 * Regular expression (`Draft 
<https://datatracker.ietf.org/doc/html/draft-murchison-sieve-regex-08/>`_)
+* Notifications (`RFC 5435 <https://datatracker.ietf.org/doc/html/rfc5435>`_)
+* Environment (`RFC 5183 <https://datatracker.ietf.org/doc/html/rfc5183>`_)
 
 The following extensions are partially supported:
 
@@ -80,7 +109,6 @@
       args_definition = [
           {"name": "testtag",
               "type": ["tag"],
-              "write_tag": True,
               "values": [":testtag"],
               "extra_arg": {"type": "number",
                             "required": False},
@@ -151,7 +179,9 @@
 it.
 
 For the ``AUTHENTICATE`` command, supported mechanisms are ``DIGEST-MD5``,
-``PLAIN``, ``LOGIN`` and ``OAUTHBEARER``.
+``PLAIN``, ``LOGIN``, ``OAUTHBEARER`` and ``XOAUTH2``.
+
+Both explicit TLS via STARTTLS and implicit TLS are supported.
     
 Basic usage
 ^^^^^^^^^^^
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/README.rst 
new/sievelib-1.5.0/README.rst
--- old/sievelib-1.4.2/README.rst       2025-01-08 10:26:03.000000000 +0100
+++ new/sievelib-1.5.0/README.rst       2026-02-25 09:16:26.000000000 +0100
@@ -43,6 +43,8 @@
 * Relational (`RFC 5231 <https://tools.ietf.org/html/rfc5231>`_)
 * Imap4flags (`RFC 5232 <https://tools.ietf.org/html/rfc5232>`_)
 * Regular expression (`Draft 
<https://datatracker.ietf.org/doc/html/draft-murchison-sieve-regex-08/>`_)
+* Notifications (`RFC 5435 <https://datatracker.ietf.org/doc/html/rfc5435>`_)
+* Environment (`RFC 5183 <https://datatracker.ietf.org/doc/html/rfc5183>`_)
 
 The following extensions are partially supported:
 
@@ -61,7 +63,6 @@
       args_definition = [
           {"name": "testtag",
               "type": ["tag"],
-              "write_tag": True,
               "values": [":testtag"],
               "extra_arg": {"type": "number",
                             "required": False},
@@ -132,7 +133,9 @@
 it.
 
 For the ``AUTHENTICATE`` command, supported mechanisms are ``DIGEST-MD5``,
-``PLAIN``, ``LOGIN`` and ``OAUTHBEARER``.
+``PLAIN``, ``LOGIN``, ``OAUTHBEARER`` and ``XOAUTH2``.
+
+Both explicit TLS via STARTTLS and implicit TLS are supported.
     
 Basic usage
 ^^^^^^^^^^^
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/pyproject.toml 
new/sievelib-1.5.0/pyproject.toml
--- old/sievelib-1.4.2/pyproject.toml   1970-01-01 01:00:00.000000000 +0100
+++ new/sievelib-1.5.0/pyproject.toml   2026-02-25 09:16:26.000000000 +0100
@@ -0,0 +1,37 @@
+[build-system]
+requires = ["setuptools>=61.0", "setuptools_scm[toml]>=6.4"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "sievelib"
+dynamic = [
+  "version",
+  "dependencies",
+  "optional-dependencies"
+]
+authors = [
+  { name="Antoine Nguyen", email="[email protected]" },
+]
+description = "Client-side SIEVE library"
+readme = "README.rst"
+requires-python = ">=3.10"
+classifiers = [
+  "Programming Language :: Python",
+  "Development Status :: 5 - Production/Stable",
+  "Intended Audience :: Developers",
+  "License :: OSI Approved :: MIT License",
+  "Operating System :: OS Independent",
+  "Topic :: Software Development :: Libraries :: Python Modules",
+  "Topic :: Communications :: Email :: Filters",
+]
+keywords = ["sieve", "managesieve", "parser", "client"]
+license = { file = "COPYING" }
+
+[project.urls]
+Repository = "https://github.com/tonioo/sievelib";
+Issues = "https://github.com/tonioo/sievelib/issues";
+
+[tool.setuptools.dynamic]
+version = { attr = "sievelib.get_version" }
+dependencies = { file = ["requirements.txt"] }
+optional-dependencies.dev = { file = ["dev-requirements.txt"] }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/requirements.txt 
new/sievelib-1.5.0/requirements.txt
--- old/sievelib-1.4.2/requirements.txt 2025-01-08 10:26:03.000000000 +0100
+++ new/sievelib-1.5.0/requirements.txt 2026-02-25 09:16:26.000000000 +0100
@@ -1,3 +1 @@
-# Requirements are listed in setup.py. The dot on the next line refers to
-# the current directory, instructing installers to use this package's setup.py
-.
+typing-extensions
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/setup.cfg new/sievelib-1.5.0/setup.cfg
--- old/sievelib-1.4.2/setup.cfg        2025-01-08 10:26:07.744957700 +0100
+++ new/sievelib-1.5.0/setup.cfg        2026-02-25 09:16:29.279206000 +0100
@@ -1,6 +1,3 @@
-[bdist_wheel]
-universal = 1
-
 [egg_info]
 tag_build = 
 tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/setup.py new/sievelib-1.5.0/setup.py
--- old/sievelib-1.4.2/setup.py 2025-01-08 10:26:03.000000000 +0100
+++ new/sievelib-1.5.0/setup.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,51 +0,0 @@
-#!/usr/bin/env python
-
-"""
-A setuptools based setup module.
-
-See:
-https://packaging.python.org/en/latest/distributing.html
-"""
-
-import io
-from os import path
-from setuptools import setup, find_packages
-
-
-def local_scheme(version):
-    """
-    Skip the local version (eg. +xyz of 0.6.1.dev4+gdf99fe2)
-    to be able to upload to Test PyPI
-    """
-    return ""
-
-
-if __name__ == "__main__":
-    HERE = path.abspath(path.dirname(__file__))
-    with io.open(path.join(HERE, "README.rst"), encoding="utf-8") as readme:
-        LONG_DESCRIPTION = readme.read()
-    setup(
-        name="sievelib",
-        packages=find_packages(),
-        include_package_data=True,
-        description="Client-side SIEVE library",
-        author="Antoine Nguyen",
-        author_email="[email protected]",
-        url="https://github.com/tonioo/sievelib";,
-        license="MIT",
-        keywords=["sieve", "managesieve", "parser", "client"],
-        install_requires=["typing-extensions"],
-        setup_requires=["setuptools_scm"],
-        use_scm_version={"local_scheme": local_scheme},
-        classifiers=[
-            "Programming Language :: Python",
-            "Development Status :: 5 - Production/Stable",
-            "Intended Audience :: Developers",
-            "License :: OSI Approved :: MIT License",
-            "Operating System :: OS Independent",
-            "Topic :: Software Development :: Libraries :: Python Modules",
-            "Topic :: Communications :: Email :: Filters",
-        ],
-        python_requires=">=3.7",
-        long_description=LONG_DESCRIPTION,
-    )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib/__init__.py 
new/sievelib-1.5.0/sievelib/__init__.py
--- old/sievelib-1.4.2/sievelib/__init__.py     2025-01-08 10:26:03.000000000 
+0100
+++ new/sievelib-1.5.0/sievelib/__init__.py     2026-02-25 09:16:26.000000000 
+0100
@@ -0,0 +1,16 @@
+import os
+
+
+def local_scheme(version):
+    return ""
+
+
+def get_version():
+    from setuptools_scm import get_version as default_version
+
+    github_version = os.environ.get("GITHUB_REF_NAME", None)
+    github_type = os.environ.get("GITHUB_REF_TYPE", None)
+    if github_version is not None and github_type == "tag":
+        print(f"GITHUB_REF_NAME found, using version: {github_version}")
+        return github_version
+    return default_version(local_scheme=local_scheme)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib/commands.py 
new/sievelib-1.5.0/sievelib/commands.py
--- old/sievelib-1.4.2/sievelib/commands.py     2025-01-08 10:26:03.000000000 
+0100
+++ new/sievelib-1.5.0/sievelib/commands.py     2026-02-25 09:16:26.000000000 
+0100
@@ -85,6 +85,7 @@
     type: Union[str, List[str]]
     values: NotRequired[List[str]]
     valid_for: NotRequired[List[str]]
+    required: NotRequired[bool]
 
 
 class CommandArg(TypedDict):
@@ -301,7 +302,7 @@
                         atype = arg["extra_arg"]["type"]
                     else:
                         continue
-                if type(value) == list:
+                if isinstance(value, list):
                     if self.__get_arg_type(arg["name"]) == ["testlist"]:
                         for t in value:
                             t.dump(indentlevel, target)
@@ -325,7 +326,7 @@
                 if not arg["name"] in self.arguments:
                     continue
                 value = self.arguments[arg["name"]]
-                if type(value) == list:
+                if isinstance(value, list):
                     if self.__get_arg_type(arg["name"]) == ["testlist"]:
                         for t in value:
                             for node in t.walk():
@@ -465,7 +466,7 @@
             return False
 
         if self.curarg is not None and "extra_arg" in self.curarg:
-            condition = atype in self.curarg["extra_arg"]["type"] and (
+            condition: bool = atype in self.curarg["extra_arg"]["type"] and (
                 "values" not in self.curarg["extra_arg"]
                 or avalue in self.curarg["extra_arg"]["values"]
             )
@@ -500,7 +501,7 @@
                         self.arguments[curarg["name"]] = avalue
                 break
 
-            condition: bool = atype in curarg["type"] and 
self.__is_valid_value_for_arg(
+            condition = atype in curarg["type"] and 
self.__is_valid_value_for_arg(
                 curarg, avalue, check_extension
             )
             if condition:
@@ -570,7 +571,7 @@
     loaded_extensions: List[str] = []
 
     def complete_cb(self):
-        if type(self.arguments["capabilities"]) != list:
+        if not isinstance(self.arguments["capabilities"], list):
             exts = [self.arguments["capabilities"]]
         else:
             exts = self.arguments["capabilities"]
@@ -611,16 +612,22 @@
 
     def args_as_tuple(self):
         args = []
-        for name, value in list(self.arguments.items()):
+        for argdef in self.args_definition:
             unquote = False
-            for argdef in self.args_definition:
-                if name == argdef["name"]:
-                    condition = (
-                        "string" in argdef["type"] or "stringlist" in 
argdef["type"]
-                    )
-                    if condition:
-                        unquote = True
-                        break
+            if argdef["name"] not in self.arguments:
+                continue
+            value = self.arguments[argdef["name"]]
+            atype = argdef["type"]
+            if "tag" in atype:
+                args.append(value)
+                if argdef["name"] in self.extra_arguments:
+                    value = self.extra_arguments[argdef["name"]]
+                    atype = argdef["extra_arg"]["type"]
+                else:
+                    continue
+            condition = "string" in atype or "stringlist" in atype
+            if condition:
+                unquote = True
             if unquote:
                 if "," in value:
                     args += tools.to_list(value)
@@ -727,6 +734,47 @@
     extension = "imap4flags"
 
 
+class NotifyCommand(ActionCommand):
+    """
+    Notify extension
+
+    https://datatracker.ietf.org/doc/html/rfc5435
+    """
+
+    extension = "enotify"
+    args_definition = [
+        {
+            "name": "from",
+            "type": ["tag"],
+            "values": [":from"],
+            "required": False,
+            "extra_arg": {"type": "string", "required": True},
+        },
+        {
+            "name": "importance",
+            "type": ["tag"],
+            "values": [":importance"],
+            "required": False,
+            "extra_arg": {"type": "string", "required": True},
+        },
+        {
+            "name": "options",
+            "type": ["tag"],
+            "values": [":options"],
+            "required": False,
+            "extra_arg": {"type": "stringlist", "required": True},
+        },
+        {
+            "name": "message",
+            "type": ["tag"],
+            "values": [":message"],
+            "required": False,
+            "extra_arg": {"type": "string", "required": True},
+        },
+        {"name": "method", "type": ["string"], "required": True},
+    ]
+
+
 class TestCommand(Command):
     """Indermediate class to represent "test" commands"""
 
@@ -997,6 +1045,22 @@
         return result
 
 
+class EnvironmentCommand(TestCommand):
+    """
+    environment test.
+
+    https://datatracker.ietf.org/doc/html/rfc5183
+    """
+
+    extension = "environment"
+    args_definition = [
+        comparator,
+        match_type,
+        {"name": "name", "type": ["string"], "required": True},
+        {"name": "key-list", "type": ["string", "stringlist"], "required": 
True},
+    ]
+
+
 class VacationCommand(ActionCommand):
     extension = "vacation"
     args_definition = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib/factory.py 
new/sievelib-1.5.0/sievelib/factory.py
--- old/sievelib-1.4.2/sievelib/factory.py      2025-01-08 10:26:03.000000000 
+0100
+++ new/sievelib-1.5.0/sievelib/factory.py      2026-02-25 09:16:26.000000000 
+0100
@@ -14,6 +14,7 @@
 from typing_extensions import NotRequired
 
 from sievelib import commands
+from sievelib.parser import Parser
 
 
 class FilterAlreadyExists(Exception):
@@ -73,7 +74,7 @@
             return False
         return True
 
-    def from_parser_result(self, parser: "sievelib.parser.Parser"):
+    def from_parser_result(self, parser: Parser) -> None:
         cpt = 1
         for f in parser.result:
             if isinstance(f, commands.RequireCommand):
@@ -113,7 +114,7 @@
 
     def check_if_arg_is_extension(self, arg: str):
         """Include extension if arg requires one."""
-        args_using_extensions = {":copy": "copy"}
+        args_using_extensions = {":copy": "copy", ":create": "mailbox"}
         if arg in args_using_extensions:
             self.require(args_using_extensions[arg])
 
@@ -154,11 +155,15 @@
         cmd = commands.get_command_instance("header", parent)
         cmd.check_next_arg("tag", tag)
         if isinstance(condition[0], list):
-            cmd.check_next_arg("stringlist", [self.__quote_if_necessary(c) for 
c in condition[0]])
+            cmd.check_next_arg(
+                "stringlist", [self.__quote_if_necessary(c) for c in 
condition[0]]
+            )
         else:
             cmd.check_next_arg("string", 
self.__quote_if_necessary(condition[0]))
         if isinstance(condition[2], list):
-            cmd.check_next_arg("stringlist", [self.__quote_if_necessary(c) for 
c in condition[2]])
+            cmd.check_next_arg(
+                "stringlist", [self.__quote_if_necessary(c) for c in 
condition[2]]
+            )
         else:
             cmd.check_next_arg("string", 
self.__quote_if_necessary(condition[2]))
         return cmd
@@ -169,7 +174,8 @@
         actions: List[tuple],
         matchtype: str = "anyof",
     ) -> commands.Command:
-        """Create a new filter
+        """
+        Create a new filter.
 
         A filter is composed of:
          * a name
@@ -228,6 +234,23 @@
                     "stringlist",
                     "[{}]".format(",".join('"{}"'.format(val) for val in 
c[3])),
                 )
+            elif cname == "address":
+                cmd = commands.get_command_instance("address", ifcontrol, 
False)
+                if c[1].startswith(":not"):
+                    comp_tag = c[1].replace("not", "")
+                    negate = True
+                else:
+                    comp_tag = c[1]
+                cmd.check_next_arg("tag", comp_tag)
+                for arg in c[2:]:
+                    if isinstance(arg, str):
+                        finalarg = self.__quote_if_necessary(arg)
+                    else:
+                        finalarg = "[{}]".format(
+                            ",".join('"{}"'.format(val) for val in arg)
+                        )
+                    cmd.check_next_arg("stringlist", finalarg)
+
             elif cname == "body":
                 cmd = commands.get_command_instance("body", ifcontrol, False)
                 self.require(cmd.extension)
@@ -317,7 +340,7 @@
         conditions: List[tuple],
         actions: List[tuple],
         matchtype: str = "anyof",
-    ):
+    ) -> None:
         """Add a new filter to this filters set
 
         :param name: the filter's name
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib/managesieve.py 
new/sievelib-1.5.0/sievelib/managesieve.py
--- old/sievelib-1.4.2/sievelib/managesieve.py  2025-01-08 10:26:03.000000000 
+0100
+++ new/sievelib-1.5.0/sievelib/managesieve.py  2026-02-25 09:16:26.000000000 
+0100
@@ -1,5 +1,5 @@
 """
-A MANAGESIEVE client.
+A MANAGESIEVE client. 
 
 A protocol for securely managing Sieve scripts on a remote server.
 This protocol allows a user to have multiple scripts, and also alerts
@@ -9,9 +9,10 @@
 """
 
 import base64
+from functools import wraps
 import re
 import socket
-import ssl
+import ssl as ssllib
 from typing import Any, List, Optional, Tuple
 
 from .digest_md5 import DigestMD5
@@ -30,7 +31,7 @@
     "VERSION",
 ]
 
-SUPPORTED_AUTH_MECHS = ["DIGEST-MD5", "PLAIN", "LOGIN", "OAUTHBEARER"]
+SUPPORTED_AUTH_MECHS = ["DIGEST-MD5", "PLAIN", "LOGIN", "OAUTHBEARER", 
"XOAUTH2"]
 
 
 class Error(Exception):
@@ -62,6 +63,7 @@
     :param meth: the original called method
     """
 
+    @wraps(meth)
     def check(cls, *args, **kwargs):
         if cls.authenticated:
             return meth(cls, *args, **kwargs)
@@ -74,7 +76,13 @@
     read_size = 4096
     read_timeout = 5
 
-    def __init__(self, srvaddr: str, srvport: int = 4190, debug: bool = False):
+    def __init__(
+        self,
+        srvaddr: str,
+        srvport: int = 4190,
+        srvhostname: Optional[str] = None,
+        debug: bool = False,
+    ):
         self.srvaddr = srvaddr
         self.srvport = srvport
         self.__debug = debug
@@ -83,6 +91,7 @@
         self.authenticated: bool = False
         self.errcode: bytes = None
         self.errmsg: bytes = b""
+        self.srvhostname = srvhostname if srvhostname else self.srvaddr
 
         self.__capabilities: dict[str, str] = {}
         self.__respcode_expr = re.compile(rb"(OK|NO|BYE)\s*(.+)?")
@@ -125,7 +134,7 @@
             return buf
         try:
             buf += self.sock.recv(size)
-        except (socket.timeout, ssl.SSLError):
+        except (socket.timeout, ssllib.SSLError):
             raise Error("Failed to read %d bytes from the server" % size)
         self.__dprint(buf)
         return buf
@@ -160,7 +169,7 @@
                 if not len(nval):
                     break
                 self.__read_buffer += nval
-            except (socket.timeout, ssl.SSLError):
+            except (socket.timeout, ssllib.SSLError):
                 raise Error("Failed to read data from the server")
 
         if len(ret):
@@ -181,7 +190,7 @@
         """Read a response from the server.
 
         In the usual case, we read lines until we find one that looks
-        like a response (OK|NO|BYE\s*(.+)?).
+        like a response (OK|NO|BYE\\s*(.+)?).
 
         If *nblines* > 0, we read excactly nblines before returning.
 
@@ -217,7 +226,7 @@
         """Format command arguments before sending them.
 
         Command arguments of type string must be quoted, the only
-        exception concerns size indication (of the form {\d\+?}).
+        exception concerns size indication (of the form {\\d\\+?}).
 
         :param args: list of arguments
         :return: a list for transformed arguments
@@ -312,7 +321,7 @@
         if text corresponds to a size indication, we grab the
         remaining content from the server.
 
-        Otherwise, we try to match an error of the form \(\w+\)?\s*".+"
+        Otherwise, we try to match an error of the form \\(\\w+\\)?\\s*".+"
 
         On succes, the two public members errcode and errmsg are
         filled with the parsing results.
@@ -419,6 +428,27 @@
             return True
         return False
 
+    def _xoauth2_authentication(
+        self, login: bytes, password: bytes, authz_id: bytes = b""
+    ) -> bool:
+        """
+        OAUTHBEARER authentication.
+
+        :param login: username
+        :param password: access token
+        :return: True on success, False otherwise.
+        """
+        if isinstance(login, str):
+            login = login.encode("utf-8")
+        if isinstance(password, str):
+            password = password.encode("utf-8")
+        token = b"user=" + login + b",\001auth=Bearer " + password + 
b"\001\001"
+        token = base64.b64encode(token)
+        code, data = self.__send_command("AUTHENTICATE", [b"XOAUTH2", token])
+        if code == "OK":
+            return True
+        return False
+
     def __authenticate(
         self,
         login: str,
@@ -467,7 +497,20 @@
         self.errmsg = b"No suitable mechanism found"
         return False
 
-    def __starttls(self, keyfile=None, certfile=None) -> bool:
+    def __enable_ssl(
+        self, keyfile: Optional[str] = None, certfile: Optional[str] = None
+    ):
+        """Enable encryption for current socket."""
+        context = ssllib.create_default_context()
+        if certfile is not None:
+            context.load_cert_chain(certfile, keyfile=keyfile)
+        try:
+            nsock = context.wrap_socket(self.sock, 
server_hostname=self.srvhostname)
+        except ssllib.SSLError as e:
+            raise Error("SSL error: %s" % str(e))
+        self.sock = nsock
+
+    def __starttls(self, **kwargs) -> bool:
         """STARTTLS command
 
         See MANAGESIEVE specifications, section 2.2.
@@ -481,15 +524,7 @@
         code, data = self.__send_command("STARTTLS")
         if code != "OK":
             return False
-        context = ssl.create_default_context()
-        if certfile is not None:
-            context.load_cert_chain(certfile, keyfile=keyfile)
-        try:
-            # nsock = ssl.wrap_socket(self.sock, keyfile, certfile)
-            nsock = context.wrap_socket(self.sock, 
server_hostname=self.srvaddr)
-        except ssl.SSLError as e:
-            raise Error("SSL error: %s" % str(e))
-        self.sock = nsock
+        self.__enable_ssl(**kwargs)
         self.__capabilities = {}
         self.__get_capabilities()
         return True
@@ -542,6 +577,7 @@
         password: str,
         authz_id: str = "",
         starttls: bool = False,
+        ssl: bool = False,
         authmech: Optional[str] = None,
     ):
         """Establish a connection with the server.
@@ -552,6 +588,7 @@
         :param login: username
         :param password: clear password
         :param starttls: use a TLS connection or not
+        :param ssl: use implict TLS/SSL when connecting
         :param authmech: prefered authenticate mechanism
         :rtype: boolean
         """
@@ -561,9 +598,12 @@
         except socket.error as msg:
             raise Error("Connection to server failed: %s" % str(msg))
 
+        if ssl:
+            self.__enable_ssl()
+
         if not self.__get_capabilities():
             raise Error("Failed to read capabilities from server")
-        if starttls and not self.__starttls():
+        if not ssl and starttls and not self.__starttls():
             return False
         if self.__authenticate(login, password, authz_id, authmech):
             return True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib/parser.py 
new/sievelib-1.5.0/sievelib/parser.py
--- old/sievelib-1.4.2/sievelib/parser.py       2025-01-08 10:26:03.000000000 
+0100
+++ new/sievelib-1.5.0/sievelib/parser.py       2026-02-25 09:16:26.000000000 
+0100
@@ -25,7 +25,7 @@
 
 
 class Lexer:
-    """
+    r"""
     The lexical analysis part.
 
     This class provides a simple way to define tokens (with patterns)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib/tests/test_factory.py 
new/sievelib-1.5.0/sievelib/tests/test_factory.py
--- old/sievelib-1.4.2/sievelib/tests/test_factory.py   2025-01-08 
10:26:03.000000000 +0100
+++ new/sievelib-1.5.0/sievelib/tests/test_factory.py   2026-02-25 
09:16:26.000000000 +0100
@@ -1,8 +1,8 @@
 import unittest
 import io
 
+from sievelib import parser
 from sievelib.factory import FilterAlreadyExists, FiltersSet
-from .. import parser
 
 
 class FactoryTestCase(unittest.TestCase):
@@ -577,7 +577,17 @@
         self.fs.addfilter(
             "test",
             [("Subject", ":matches", "*")],
-            [("vacation", ":subject", "Example Autoresponder Subject", 
":days", 7, ":mime", "Example Autoresponder Body")],
+            [
+                (
+                    "vacation",
+                    ":subject",
+                    "Example Autoresponder Subject",
+                    ":days",
+                    7,
+                    ":mime",
+                    "Example Autoresponder Body",
+                )
+            ],
         )
         output = io.StringIO()
         self.fs.tosieve(output)
@@ -595,7 +605,18 @@
     def test_dump(self):
         self.fs.addfilter(
             "test",
-            [("Subject", ":matches", "*")], [("vacation", ":subject", "Example 
Autoresponder Subject", ":days", 7, ":mime", "Example Autoresponder Body")]
+            [("Subject", ":matches", "*")],
+            [
+                (
+                    "vacation",
+                    ":subject",
+                    "Example Autoresponder Subject",
+                    ":days",
+                    7,
+                    ":mime",
+                    "Example Autoresponder Body",
+                )
+            ],
         )
         output = io.StringIO()
         self.fs.dump(output)
@@ -634,8 +655,134 @@
             """# Filter: test
 if anyof (header :contains ["X-Foo", "X-Bar"] ["bar", "baz"]) {
 }
-"""
+""",
+        )
+
+    def test_address_string_args(self):
+        self.fs.addfilter(
+            "test",
+            [("address", ":is", "from", "[email protected]")],
+            [("fileinto", "folder")],
+        )
+        output = io.StringIO()
+        self.fs.tosieve(output)
+        self.assertEqual(
+            output.getvalue(),
+            """require ["fileinto"];
+
+# Filter: test
+if anyof (address :is "from" "[email protected]") {
+    fileinto "folder";
+}
+""",
+        )
+
+    def test_address_list_args(self):
+        self.fs.addfilter(
+            "test",
+            [
+                (
+                    "address",
+                    ":is",
+                    ["from", "reply-to"],
+                    ["[email protected]", "[email protected]"],
+                )
+            ],
+            [("fileinto", ":create", "folder")],
+        )
+        output = io.StringIO()
+        self.fs.tosieve(output)
+        self.assertEqual(
+            output.getvalue(),
+            """require ["fileinto", "mailbox"];
+
+# Filter: test
+if anyof (address :is ["from","reply-to"] ["[email protected]","[email protected]"]) 
{
+    fileinto :create "folder";
+}
+""",
+        )
+
+    def test_notify_action(self):
+        self.fs.addfilter(
+            "test",
+            [
+                (
+                    "from",
+                    ":contains",
+                    "[email protected]",
+                )
+            ],
+            [
+                (
+                    "notify",
+                    ":importance",
+                    "1",
+                    ":message",
+                    "This is probably very important",
+                    "mailto:[email protected]";,
+                )
+            ],
+        )
+
+        output = io.StringIO()
+        self.fs.tosieve(output)
+        self.assertEqual(
+            output.getvalue(),
+            """require ["enotify"];
+
+# Filter: test
+if anyof (header :contains "from" "[email protected]") {
+    notify :importance "1" :message "This is probably very important" 
"mailto:[email protected]";;
+}
+""",
+        )
+
+    def test_get_filter_actions_with_extra_args(self):
+        self.fs.require("date")
+        self.fs.require("relational")
+        self.fs.require("vacation")
+        conditions = [
+            ("currentdate", ":zone", "+0500", ":value", "ge", "date", 
"2026-01-01"),
+            ("currentdate", ":zone", "+0500", ":value", "le", "date", 
"2026-01-31"),
+        ]
+
+        actions = [
+            (
+                "vacation",
+                ":days",
+                1,
+                ":addresses",
+                "[email protected]",
+                ":from",
+                "[email protected]",
+                ":subject",
+                "Subject",
+                "I'll be off until december 31th",
             )
+        ]
+
+        rule_name = "Vacation rule"
+        self.fs.addfilter(rule_name, conditions, actions, "allof")
+        actionsr = self.fs.get_filter_actions(rule_name)
+        self.assertEqual(
+            actionsr,
+            [
+                (
+                    "vacation",
+                    ":subject",
+                    "Subject",
+                    ":days",
+                    1,
+                    ":from",
+                    "[email protected]",
+                    ":addresses",
+                    "[email protected]",
+                    "I'll be off until december 31th",
+                )
+            ],
+        )
+
 
 if __name__ == "__main__":
     unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib/tests/test_managesieve.py 
new/sievelib-1.5.0/sievelib/tests/test_managesieve.py
--- old/sievelib-1.4.2/sievelib/tests/test_managesieve.py       2025-01-08 
10:26:03.000000000 +0100
+++ new/sievelib-1.5.0/sievelib/tests/test_managesieve.py       2026-02-25 
09:16:26.000000000 +0100
@@ -8,14 +8,14 @@
 CAPABILITIES = (
     b'"IMPLEMENTATION" "Example1 ManageSieved v001"\r\n'
     b'"VERSION" "1.0"\r\n'
-    b'"SASL" "PLAIN SCRAM-SHA-1 GSSAPI OAUTHBEARER"\r\n'
+    b'"SASL" "PLAIN SCRAM-SHA-1 GSSAPI OAUTHBEARER XOAUTH2"\r\n'
     b'"SIEVE" "fileinto vacation"\r\n'
     b'"STARTTLS"\r\n'
 )
 
 CAPABILITIES_WITHOUT_VERSION = (
     b'"IMPLEMENTATION" "Example1 ManageSieved v001"\r\n'
-    b'"SASL" "PLAIN SCRAM-SHA-1 GSSAPI OAUTHBEARER"\r\n'
+    b'"SASL" "PLAIN SCRAM-SHA-1 GSSAPI OAUTHBEARER XOAUTH2"\r\n'
     b'"SIEVE" "fileinto vacation"\r\n'
     b'"STARTTLS"\r\n'
 )
@@ -64,6 +64,11 @@
         mock_socket.return_value.recv.side_effect = (AUTHENTICATION,)
         self.assertTrue(self.client.connect("user", "token", 
authmech="OAUTHBEARER"))
 
+    def test_auth_xoauth2(self, mock_socket):
+        """Test XOAUTH2 mechanism."""
+        mock_socket.return_value.recv.side_effect = (AUTHENTICATION,)
+        self.assertTrue(self.client.connect("user", "token", 
authmech="XOAUTH2"))
+
     def test_capabilities(self, mock_socket):
         """Test capabilities command."""
         self.authenticate(mock_socket)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib/tests/test_parser.py 
new/sievelib-1.5.0/sievelib/tests/test_parser.py
--- old/sievelib-1.4.2/sievelib/tests/test_parser.py    2025-01-08 
10:26:03.000000000 +0100
+++ new/sievelib-1.5.0/sievelib/tests/test_parser.py    2026-02-25 
09:16:26.000000000 +0100
@@ -4,7 +4,6 @@
 
 import unittest
 import os.path
-import codecs
 import io
 
 from sievelib.parser import Parser
@@ -17,7 +16,6 @@
         {
             "name": "testtag",
             "type": ["tag"],
-            "write_tag": True,
             "values": [":testtag"],
             "extra_arg": {"type": "number", "required": False},
             "required": False,
@@ -31,7 +29,6 @@
         {
             "name": "subject",
             "type": ["tag"],
-            "write_tag": True,
             "values": [":subject"],
             "extra_arg": {"type": "string"},
             "required": False,
@@ -39,7 +36,6 @@
         {
             "name": "recipient",
             "type": ["tag"],
-            "write_tag": True,
             "values": [":recipient"],
             "extra_arg": {"type": "stringlist"},
             "required": True,
@@ -102,7 +98,7 @@
 class ValidEncodings(SieveTest):
     def test_utf8_file(self):
         utf8_sieve = os.path.join(os.path.dirname(__file__), "files", 
"utf8_sieve.txt")
-        with codecs.open(utf8_sieve, encoding="utf8") as fobj:
+        with open(utf8_sieve, encoding="utf8") as fobj:
             source_sieve = fobj.read()
         self.parser.parse_file(utf8_sieve)
         self.sieve_is(source_sieve)
@@ -663,6 +659,20 @@
 """
         )
 
+    def test_notify_extension(self):
+        self.compilation_ok(
+            b"""require ["enotify", "fileinto", "variables"];
+
+if header :contains "from" "[email protected]" {
+    notify :importance "1"
+        :message "This is probably very important"
+                    "mailto:[email protected]";;
+    # Don't send any further notifications
+    stop;
+}
+"""
+        )
+
 
 class InvalidSyntaxes(SieveTest):
     def test_nested_comments(self):
@@ -850,6 +860,20 @@
 """
         )
 
+    def test_notify_extension_importance_no_args(self):
+        self.compilation_ko(
+            b"""require ["enotify", "fileinto", "variables"];
+
+if header :contains "from" "[email protected]" {
+    notify :importance
+        :message "This is probably very important";
+            "mailto:[email protected]";
+    # Don't send any further notifications
+    stop;
+}
+"""
+        )
+
 
 class LanguageRestrictions(SieveTest):
     def test_unknown_control(self):
@@ -1042,6 +1066,32 @@
         )
 
 
+class EnvironmentCommand(SieveTest):
+
+    def test_environment_test(self):
+        self.compilation_ok(
+            b"""require ["environment", "fileinto"];
+if environment :matches "remote_ip" "192.168.*" {
+    fileinto "INBOX.Unsafe_Emails";
+    stop;
+}
+"""
+        )
+
+    def test_missing_import(self):
+        self.compilation_ko(
+            b"""require ["fileinto"];
+if environment :matches "remote_ip" "192.168.*" {
+    fileinto "INBOX.Unsafe_Emails";
+    stop;
+}
+"""
+        )
+        self.assertEqual(
+            self.parser.error, "line 2: extension 'environment' not loaded"
+        )
+
+
 class VariablesCommands(SieveTest):
     def test_set_command(self):
         self.compilation_ok(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib.egg-info/PKG-INFO 
new/sievelib-1.5.0/sievelib.egg-info/PKG-INFO
--- old/sievelib-1.4.2/sievelib.egg-info/PKG-INFO       2025-01-08 
10:26:07.000000000 +0100
+++ new/sievelib-1.5.0/sievelib.egg-info/PKG-INFO       2026-02-25 
09:16:29.000000000 +0100
@@ -1,11 +1,30 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: sievelib
-Version: 1.4.2
+Version: 1.5.0
 Summary: Client-side SIEVE library
-Home-page: https://github.com/tonioo/sievelib
-Author: Antoine Nguyen
-Author-email: [email protected]
-License: MIT
+Author-email: Antoine Nguyen <[email protected]>
+License: Copyright (c) 2011-2024 Antoine Nguyen <[email protected]>
+        
+        Permission is hereby granted, free of charge, to any person obtaining 
a copy
+        of this software and associated documentation files (the "Software"), 
to deal
+        in the Software without restriction, including without limitation the 
rights
+        to use, copy, modify, merge, publish, distribute, sublicense, and/or 
sell
+        copies of the Software, and to permit persons to whom the Software is
+        furnished to do so, subject to the following conditions:
+        
+        The above copyright notice and this permission notice shall be 
included in
+        all copies or substantial portions of the Software.
+        
+        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR
+        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY,
+        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
SHALL THE
+        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
ARISING FROM,
+        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
IN
+        THE SOFTWARE.
+        
+Project-URL: Repository, https://github.com/tonioo/sievelib
+Project-URL: Issues, https://github.com/tonioo/sievelib/issues
 Keywords: sieve,managesieve,parser,client
 Classifier: Programming Language :: Python
 Classifier: Development Status :: 5 - Production/Stable
@@ -14,8 +33,16 @@
 Classifier: Operating System :: OS Independent
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Communications :: Email :: Filters
-Requires-Python: >=3.7
+Requires-Python: >=3.10
+Description-Content-Type: text/x-rst
 License-File: COPYING
+Requires-Dist: typing-extensions
+Provides-Extra: dev
+Requires-Dist: pre-commit; extra == "dev"
+Requires-Dist: black; extra == "dev"
+Requires-Dist: pylint; extra == "dev"
+Requires-Dist: pytest; extra == "dev"
+Dynamic: license-file
 
 sievelib
 ========
@@ -62,6 +89,8 @@
 * Relational (`RFC 5231 <https://tools.ietf.org/html/rfc5231>`_)
 * Imap4flags (`RFC 5232 <https://tools.ietf.org/html/rfc5232>`_)
 * Regular expression (`Draft 
<https://datatracker.ietf.org/doc/html/draft-murchison-sieve-regex-08/>`_)
+* Notifications (`RFC 5435 <https://datatracker.ietf.org/doc/html/rfc5435>`_)
+* Environment (`RFC 5183 <https://datatracker.ietf.org/doc/html/rfc5183>`_)
 
 The following extensions are partially supported:
 
@@ -80,7 +109,6 @@
       args_definition = [
           {"name": "testtag",
               "type": ["tag"],
-              "write_tag": True,
               "values": [":testtag"],
               "extra_arg": {"type": "number",
                             "required": False},
@@ -151,7 +179,9 @@
 it.
 
 For the ``AUTHENTICATE`` command, supported mechanisms are ``DIGEST-MD5``,
-``PLAIN``, ``LOGIN`` and ``OAUTHBEARER``.
+``PLAIN``, ``LOGIN``, ``OAUTHBEARER`` and ``XOAUTH2``.
+
+Both explicit TLS via STARTTLS and implicit TLS are supported.
     
 Basic usage
 ^^^^^^^^^^^
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib.egg-info/SOURCES.txt 
new/sievelib-1.5.0/sievelib.egg-info/SOURCES.txt
--- old/sievelib-1.4.2/sievelib.egg-info/SOURCES.txt    2025-01-08 
10:26:07.000000000 +0100
+++ new/sievelib-1.5.0/sievelib.egg-info/SOURCES.txt    2026-02-25 
09:16:29.000000000 +0100
@@ -4,9 +4,8 @@
 MANIFEST.in
 README.rst
 dev-requirements.txt
+pyproject.toml
 requirements.txt
-setup.cfg
-setup.py
 .github/workflows/sievelib.yml
 sievelib/__init__.py
 sievelib/commands.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sievelib-1.4.2/sievelib.egg-info/requires.txt 
new/sievelib-1.5.0/sievelib.egg-info/requires.txt
--- old/sievelib-1.4.2/sievelib.egg-info/requires.txt   2025-01-08 
10:26:07.000000000 +0100
+++ new/sievelib-1.5.0/sievelib.egg-info/requires.txt   2026-02-25 
09:16:29.000000000 +0100
@@ -1 +1,7 @@
 typing-extensions
+
+[dev]
+pre-commit
+black
+pylint
+pytest

Reply via email to