Repository: grindsa/dkb-robo (language: python)

Issue:
Recommendation: Switch to Poetry instead of relying on setup and requirements.txt
See title. 
I think more and more (including PEP, see https://peps.python.org/pep-0517/) recommendations point toward the us of pyproject.toml. I think poetry is an easy way to be compliant with PEP-517. 


Discussion / hints:

Fix patch:
diff --git a/.github/pylintrc b/.github/pylintrc
index 75b537f..4f22196 100644
--- a/.github/pylintrc
+++ b/.github/pylintrc
@@ -1,4 +1,5 @@
-# plyintrc for acme2certifier CI pipeline
+# plyintrc for dkb-robo CI pipeline
+
 [MESSAGES CONTROL]
 # c0301 - line to long
 # r0205 - useless-object-inheritance
diff --git a/.github/workflows/codescanner.yml b/.github/workflows/codescanner.yml
index b1c9ac9..a498e7c 100644
--- a/.github/workflows/codescanner.yml
+++ b/.github/workflows/codescanner.yml
@@ -4,72 +4,45 @@ on:
     branches:
       - 'master'
       - 'devel'
-jobs:
-  codecov:
-    name: Codecov Analysis
-    runs-on: ubuntu-latest
 
-    steps:
-      - uses: actions/checkout@v4
-      - name: Set up Python
-        uses: actions/setup-python@master
-        with:
-          python-version: 3.9
-      - name: Generate coverage report
-        run: |
-          python -m pip install --upgrade pip
-          pip install lxml beautifulsoup4 html5lib
-          pip install pytest
-          pip install pytest-cov
-          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
-          pytest --cov=./ --cov-report=xml
-      - name: Upload coverage to Codecov
-        uses: codecov/codecov-action@v3
-        with:
-          token: ${{ secrets.CODECOV_TOKEN }}
-          file: ./coverage.xml
-          flags: unittests
-
-  #sonarcloud:
-  #  name: SonarCloud Analysis
-  #  runs-on: ubuntu-latest
-  #  steps:
-  #    - uses: actions/checkout@v4
-  #      with:
-  #        fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
-  #    - name: SonarCloud Scan
-  #      uses: SonarSource/sonarcloud-github-action@master
-  #      env:
-  #        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
-  #        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
 
+jobs:
   sonarcloud:
     name: SonarCloud Analysis
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - name: Checkout source code 
+        uses: actions/checkout@v4
         with:
+          # Disabling shallow clone is recommended for improving relevancy of reporting.
           fetch-depth: 0
-      - name: Setup Python
-        uses: actions/setup-python@v2
+    
+      - name: Set up Python
+        uses: actions/setup-python@v5
         with:
           python-version: '3.x'
+      
+      - name: Install package
+        run: python -m pip install .[test]
+      
+      - name: Run tests
+        run: python -m pytest
+
+      - name: Upload coverage reports to Codecov
+        uses: codecov/codecov-action@v4.5.0
+        with:
+          token: ${{ secrets.CODECOV_TOKEN }}
+          flags: unittests
 
-      - name: Install pytest coverage and any other packages
-        run: |
-          python -m pip install --upgrade pip
-          pip install pytest coverage lxml beautifulsoup4 html5lib bs4
-          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
-
-      - name: run coverage
-        run: |
-          pip install pytest-cov
-          pytest --cov=./ --cov-report=xml
       - name: SonarCloud Scan
-        uses: SonarSource/sonarcloud-github-action@master
+        uses: SonarSource/sonarcloud-github-action@v3
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+          # Needed to get PR information, if any.
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 
 
   codeql:
     name: CodeQL Analysis
diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml
index 0e6116e..779effa 100644
--- a/.github/workflows/create_release.yml
+++ b/.github/workflows/create_release.yml
@@ -1,9 +1,13 @@
+name: Create Release
+
 on:
   push:
     branches:
       - "master"
 
-name: Create Release
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
 
 jobs:
   build:
@@ -11,9 +15,13 @@ jobs:
     runs-on: ubuntu-latest
     steps:
 
-      - name: "[ PREPARE ] install packages"
-        run: |
-          sudo apt-get update && sudo apt-get install -y curl twine python3 python3-mechanicalsoup python3-venv
+      - name: "[ PREPARE ] Set up Python"
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.x"
+
+      - name: "[ PREPARE ] Install pypa/build"
+        run: python3 -m pip install build --user
 
       - name: "[ PREPARE ] Get current version"
         uses: oprypin/find-latest-tag@v1
@@ -25,14 +33,16 @@ jobs:
       - name: "[ PREPARE ] Checkout code"
         uses: actions/checkout@v4
 
-      - name: "[ PREPARE ] Retrieve Version from version.py"
+      - name: "[ PREPARE ] Retrieve version"
         run: |
+          python3 -m pip install .
+          echo TAG_NAME=$(python3 -c "import dkb_robo; print(dkb_robo.__version__)") >> $GITHUB_ENV
           echo APP_NAME=$(echo ${{ github.repository }} | awk -F / '{print $2}') >> $GITHUB_ENV
-          echo TAG_NAME=$(cat setup.py | grep -i version | head -n 1 | sed 's/version=//g' | sed s/\'//g | sed 's/\,//g' | sed 's/\ //g') >> $GITHUB_ENV
 
-      - run: echo "Repo is at version ${{ steps.dkb_robo_ver.outputs.tag }}"
-      - run: echo "APP tag is ${{ env.APP_NAME }}"
-      - run: echo "Latest tag is ${{ env.TAG_NAME }}"
+      - run: |
+          echo "Repo is at version ${{ steps.dkb_robo_ver.outputs.tag }}"
+          echo "APP tag is ${{ env.APP_NAME }}"
+          echo "Latest tag is ${{ env.TAG_NAME }}"
 
       - name: "[ BUILD ] Create Release"
         id: create_release
@@ -48,14 +58,13 @@ jobs:
           draft: false
           prerelease: false
 
-      - name: "[ BUILD ] create pypi package"
+      - name: "[ BUILD ] Build a binary wheel and a source tarball"
         id: create_package
         if: steps.dbv_robo_ver.outputs.tag != env.TAG_NAME
-        run: |
-          python3 setup.py sdist
+        run: python3 -m build
 
       - name: "[ UPLOAD ] Publish package"
-        uses: pypa/gh-action-pypi-publish@master
+        uses: pypa/gh-action-pypi-publish@release/v1
         if: steps.dbv_robo_ver.outputs.tag != env.TAG_NAME
         with:
           user: __token__
@@ -74,8 +83,8 @@ jobs:
         run: |
           python3 -m venv /tmp/dkbroboenv
           source /tmp/dkbroboenv/bin/activate
-          pip install -r requirements.txt
-          pip freeze > /tmp/requirements_freeze.txt
+          python3 -m pip install .
+          python3 -m pip freeze > /tmp/requirements_freeze.txt
           cat /tmp/requirements_freeze.txt
           deactivate
 
@@ -84,9 +93,9 @@ jobs:
         run: |
           mkdir -p /tmp/sbom/sbom/dkb-robo
           cp /tmp/requirements_freeze.txt /tmp/sbom/sbom/dkb-robo/dkb-robo_sbom.txt
-          pip install cyclonedx-bom
-          python3 -m cyclonedx_py requirements -i /tmp/requirements_freeze.txt --of xml -o /tmp/sbom/sbom/dkb-robo/dkb-robo_sbom.xml
-          python3 -m cyclonedx_py requirements -i /tmp/requirements_freeze.txt --of json -o /tmp/sbom/sbom/dkb-robo/dkb-robo_sbom.json
+          python3 -m pip install cyclonedx-bom
+          python3 -m cyclonedx_py environment -v --pyproject pyproject.toml --mc-type library --output-reproducible --output-format xml  --outfile /tmp/sbom/sbom/dkb-robo/dkb-robo_sbom.xml  /tmp/dkbroboenv
+          python3 -m cyclonedx_py environment -v --pyproject pyproject.toml --mc-type library --output-reproducible --output-format json --outfile /tmp/sbom/sbom/dkb-robo/dkb-robo_sbom.json /tmp/dkbroboenv
 
       - name: "[ BUILD ] Upload SBOM"
         # if: steps.dbv_robo_ver.outputs.tag != env.TAG_NAME
diff --git a/dkb_robo/__init__.py b/dkb_robo/__init__.py
index 2df36d6..75fb66c 100644
--- a/dkb_robo/__init__.py
+++ b/dkb_robo/__init__.py
@@ -1,4 +1,8 @@
-""" init """
+"""init"""
+
+from importlib.metadata import version
+
 from .dkb_robo import DKBRobo, DKBRoboError
 
-__author__ = 'GrindSa'
+__author__ = "GrindSa"
+__version__ = version("dkb_robo")
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..eb8b286
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,60 @@
+[project]
+authors = [{ name = "grindsa", email = "grindelsack@gmail.com" }]
+name = "dkb_robo"
+description = "Download transactions from the website of Deutsche Kreditbak AG"
+keywords = ["Deutsche Kreditbank", "DKB"]
+version = "0.27"
+readme = "README.md"
+classifiers = [
+    # Keep in sync with the Python version matrix in test_lint.yaml GitHub workflow.
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
+    "Development Status :: 4 - Beta",
+    "Natural Language :: German",
+    "Intended Audience :: Developers",
+    "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
+    "Operating System :: OS Independent",
+    "Topic :: Software Development :: Libraries :: Python Modules",
+]
+requires-python = ">= 3.8"
+dependencies = [
+    "bs4",
+    "mechanicalsoup",
+    "requests",
+    "pillow",
+    "tabulate",
+    "click",
+]
+optional-dependencies.test = ["pytest", "pytest-cov", "html5lib"]
+optional-dependencies.dev = ["build", "dkb_robo[test]"]
+scripts = { dkb = "dkb_robo.cli:main" }
+
+[project.urls]
+Repository = "https://github.com/grindsa/dkb-robo"
+Issues = "https://github.com/grindsa/dkb-robo/issues"
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.hatch]
+build.targets.sdist.only-packages = true
+
+[tool.pytest.ini_options]
+addopts = [
+    "-ra",
+    "--cov=dkb_robo",
+    "--import-mode=importlib",
+    "--strict-config",
+    "--strict-markers",
+]
+filterwarnings = ["error"]
+log_cli_level = "INFO"
+minversion = "7"
+xfail_strict = true
+testpaths = ["test"]
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index c487fb3..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-mechanicalsoup
-tabulate
-click
-requests
-pillow
\ No newline at end of file
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 63d2614..0000000
--- a/setup.py
+++ /dev/null
@@ -1,43 +0,0 @@
-""" setup.py """
-from pathlib import Path
-from setuptools import setup
-
-this_directory = Path(__file__).parent
-long_description = (this_directory / 'README.md').read_text()
-
-setup(name='dkb_robo',
-    version='0.27',
-    description='library to access the internet banking area of "Deutsche Kreditbank" to get account information and transactions.',
-    long_description=long_description,
-    long_description_content_type='text/markdown',
-    url='https://github.com/grindsa/dkb-robo',
-    author='grindsa',
-    author_email='grindelsack@gmail.com',
-    license='GPL',
-    packages=['dkb_robo'],
-    platforms='any',
-    install_requires=[
-        'mechanicalsoup',
-        'bs4',
-        'html5lib',
-        'six',
-        'tabulate',
-        'click'
-    ],
-    entry_points={
-        "console_scripts": ["dkb=dkb_robo.cli:main"],
-    },
-    extras_require={
-        "cli": ["click", "tabulate"],
-    },
-    classifiers = [
-        'Programming Language :: Python',
-        'Development Status :: 4 - Beta',
-        'Natural Language :: German',
-        'Intended Audience :: Developers',
-        'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
-        'Operating System :: OS Independent',
-        'Topic :: Software Development :: Libraries :: Python Modules'
-        ],
-    zip_safe=False,
-    test_suite="test")


Test patch:
diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml
deleted file mode 100644
index e41146c..0000000
--- a/.github/workflows/python-test.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-# workflow to run the dkb-robo unittest suite
-
-name: Python Tests
-
-on:
-  push:
-  pull_request:
-    branches: [ devel ]
-  schedule:
-    - cron:  '0 2 * * 6'
-
-jobs:
-  unittest:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        python_version: [3.x, 3.12, 3.11, '3.10', 3.9, 3.8, 3.7]
-    name: Python Unittest (${{ matrix.python_version }})
-    steps:
-    - uses: actions/checkout@v4
-    - name: Set up Python ${{ matrix.python_version }}
-      uses: actions/setup-python@v5
-      with:
-        python-version: ${{ matrix.python_version }}
-    - name: Install dependencies
-      run: |
-        python -m pip install --upgrade pip
-        pip install lxml beautifulsoup4 html5lib
-        pip install pytest
-        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
-    - name: Python test
-      run: |
-        pytest
-  pylint:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        python_version: [3.x, 3.12, 3.11, '3.10', 3.9, 3.8, 3.7]
-    name: Pylint test (${{ matrix.python_version }})
-    steps:
-      - uses: actions/checkout@v4
-      - name: Set up Python ${{ matrix.python_version }}
-        uses: actions/setup-python@v5
-        with:
-          python-version: ${{ matrix.python_version }}
-      - name: Install dependencies
-        run: |
-          sudo apt-get install libxml2-dev libxslt-dev python-dev-is-python3
-          python -m pip install --upgrade pip
-          pip install pylint pylint-exit mechanicalsoup
-          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
-      - name: "Pylint folder: dkb_robo"
-        run: |
-          pylint --rcfile=".github/pylintrc" dkb_robo/ || pylint-exit $?
-
-      - name: Lint with pycodestyle
-        run: |
-          pip install pycodestyle
-          pycodestyle --max-line-length=380  dkb_robo/.
diff --git a/.github/workflows/test_lint.yml b/.github/workflows/test_lint.yml
new file mode 100644
index 0000000..d4668d4
--- /dev/null
+++ b/.github/workflows/test_lint.yml
@@ -0,0 +1,66 @@
+name: Test and lint
+on:
+  push:
+  pull_request:
+    branches: [ devel ]
+  schedule:
+    - cron:  '0 2 * * 6'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        # Keep in sync with the Python Trove classifiers in pyproject.toml.
+        python_version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
+    name: Test with Python (${{ matrix.python_version }})
+
+    steps:
+    - name: Checkout source code 
+      uses: actions/checkout@v4
+    
+    - name: Set up Python ${{ matrix.python_version }}
+      uses: actions/setup-python@v5
+      with:
+        python-version: ${{ matrix.python_version }}
+        allow-prereleases: true
+    
+    - name: Install package
+      run: python -m pip install .[test]
+    
+    - name: Run tests
+      run: python3 -m pytest
+
+    # - name: Upload coverage reports to Codecov
+    #   uses: codecov/codecov-action@v4.5.0
+    #   with:
+    #     token: ${{ secrets.CODECOV_TOKEN }}
+    #     flags: unittests
+
+  lint:
+    runs-on: ubuntu-latest
+    name: Lint
+
+    steps:
+      - name: Checkout source code 
+        uses: actions/checkout@v4
+    
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.x"
+
+      - name: Install linters
+        run: python3 -m pip install pylint pylint-exit pycodestyle
+
+      - name: Lint with pylint
+        run: |
+          pylint --rcfile=".github/pylintrc" dkb_robo/ || pylint-exit $?
+
+      - name: Lint with pycodestyle
+        run: |
+          pycodestyle --max-line-length=380 dkb_robo/.