Repository: tonioo/sievelib

Issue:
Add 'mailbox' into 'require' when ':create' is used
[RFC 5490](https://datatracker.ietf.org/doc/html/rfc5490#section-3.2) defines ':create' argument for 'fileinto' command.

To use it 'mailbox' needs to be in 'require' line.

Can sievelib add it automatically? I am adding it via `fs.require("mailbox")` right after creation of FilterSet but that's because I remember about it...

Discussion:
@tonioo: @hrw Can you check with the PR I created?@hrw: Works as expected.

Solution patch:
diff --git a/.github/workflows/sievelib.yml b/.github/workflows/sievelib.yml
index 76800a2..f89059d 100644
--- a/.github/workflows/sievelib.yml
+++ b/.github/workflows/sievelib.yml
@@ -14,7 +14,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.8, 3.9, '3.10', '3.11']
+        python-version: [3.9, '3.10', '3.11', '3.12', '3.13']
       fail-fast: false
     steps:
       - uses: actions/checkout@v4
@@ -27,15 +27,15 @@ jobs:
           pip install codecov pytest pytest-cov
           pip install -e .
       - name: Run tests
-        if: ${{ matrix.python-version != '3.11' }}
+        if: ${{ matrix.python-version != '3.13' }}
         run: |
           pytest
       - name: Run tests and coverage
-        if: ${{ matrix.python-version == '3.11' }}
+        if: ${{ matrix.python-version == '3.13' }}
         run: |
           pytest --cov=sievelib --cov-report xml
       - name: Upload coverage result
-        if: ${{ matrix.python-version == '3.11' }}
+        if: ${{ matrix.python-version == '3.13' }}
         uses: actions/upload-artifact@v4
         with:
           name: coverage-results
@@ -69,13 +69,14 @@ jobs:
       - uses: actions/checkout@v4
         with:
           fetch-depth: 0
-      - name: Set up Python 3.11
+      - name: Set up Python 3.13
         uses: actions/setup-python@v5
         with:
-          python-version: '3.11'
+          python-version: '3.13'
       - 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 --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..4f04714
--- /dev/null
+++ b/pyproject.toml
@@ -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="tonio@ngyn.org" },
+]
+description = "Client-side SIEVE library"
+readme = "README.rst"
+requires-python = ">=3.9"
+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 --git a/requirements.txt b/requirements.txt
index a10e857..3492fa5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -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 --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 7c2b287..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-[bdist_wheel]
-universal = 1
\ No newline at end of file
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 89f4183..0000000
--- a/setup.py
+++ /dev/null
@@ -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="tonio@ngyn.org",
-        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 --git a/sievelib/__init__.py b/sievelib/__init__.py
index e69de29..bbb3522 100644
--- a/sievelib/__init__.py
+++ b/sievelib/__init__.py
@@ -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 --git a/sievelib/factory.py b/sievelib/factory.py
index bd7aeed..b2b7646 100644
--- a/sievelib/factory.py
+++ b/sievelib/factory.py
@@ -114,7 +114,7 @@ def require(self, name: str):
 
     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])
 


Test patch:
diff --git a/sievelib/tests/test_factory.py b/sievelib/tests/test_factory.py
index cf5a709..8b65aa4 100644
--- a/sievelib/tests/test_factory.py
+++ b/sievelib/tests/test_factory.py
@@ -662,7 +662,7 @@ def test_address_string_args(self):
         self.fs.addfilter(
             "test",
             [("address", ":is", "from", "user1@test.com")],
-            [("fileinto", ":create", "folder")],
+            [("fileinto", "folder")],
         )
         output = io.StringIO()
         self.fs.tosieve(output)
@@ -672,7 +672,7 @@ def test_address_string_args(self):
 
 # Filter: test
 if anyof (address :is "from" "user1@test.com") {
-    fileinto :create "folder";
+    fileinto "folder";
 }
 """,
         )
@@ -694,7 +694,7 @@ def test_address_list_args(self):
         self.fs.tosieve(output)
         self.assertEqual(
             output.getvalue(),
-            """require ["fileinto"];
+            """require ["fileinto", "mailbox"];
 
 # Filter: test
 if anyof (address :is ["from","reply-to"] ["user1@test.com","user2@test.com"]) {