From 4e5f849138d59c139c125c9896e49ec0f14e94e7 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 15:30:01 +0100 Subject: [PATCH 01/10] sftp: remove unused variable --- src/borgstore/backends/sftp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/borgstore/backends/sftp.py b/src/borgstore/backends/sftp.py index 49d875f..71ef325 100644 --- a/src/borgstore/backends/sftp.py +++ b/src/borgstore/backends/sftp.py @@ -162,7 +162,7 @@ def delete_recursive(path): self._connect() try: try: - st = self.client.stat(self.base_path) # check if this storage exists, fail early if not. + self.client.stat(self.base_path) # check if this storage exists, fail early if not. except FileNotFoundError: raise BackendDoesNotExist(f"sftp storage base path does not exist: {self.base_path}") from None delete_recursive(self.base_path) From 9f15c39ffa3b8c623969db3c08b3f735885567d2 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 16:01:07 +0100 Subject: [PATCH 02/10] CI/tox: fix tox envs / tox configuration --- .github/workflows/ci.yml | 9 +-------- tox.ini | 28 ++-------------------------- 2 files changed, 3 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78fbd6c..50b174f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -150,12 +150,5 @@ jobs: - name: Install borgstore if: runner.os == 'macOS' run: pip install -ve ".[rest,rclone]" - - name: run tox envs (Linux) - if: runner.os == 'Linux' - run: tox -e all_extras - - name: run tox envs (Windows) - if: runner.os == 'Windows' - run: tox -e ${{ matrix.toxenv }} - - name: run tox envs (macOS) - if: runner.os == 'macOS' + - name: run tox envs run: tox -e ${{ matrix.toxenv }} diff --git a/tox.ini b/tox.ini index a6c1f27..8929b24 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,8 @@ envlist = py{310,311,312,313,314},flake8,mypy deps = pytest requests + boto3 + paramiko commands = pytest -v -rs tests pass_env = BORGSTORE_TEST_*_URL @@ -26,29 +28,3 @@ deps = types-paramiko >= 1.9.1 types-requests >= 2.25.1 commands = mypy - -[testenv:s3] -deps = - pytest - boto3 -commands = pytest -v -rs tests -pass_env = - BORGSTORE_TEST_S3_URL - -[testenv:sftp] -deps = - pytest - paramiko -commands = pytest -v -rs tests -pass_env = - BORGSTORE_TEST_SFTP_URL - -[testenv:all_extras] -deps = - pytest - boto3 - paramiko - requests -commands = pytest -v -rs tests -pass_env = - BORGSTORE_TEST_*_URL From 1b53a97d93647de4e111f4a01d715ad0ed0d2dc1 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 17:02:00 +0100 Subject: [PATCH 03/10] fix mypy complaints --- src/borgstore/backends/rest.py | 12 +++++++++--- src/borgstore/backends/sftp.py | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/borgstore/backends/rest.py b/src/borgstore/backends/rest.py index 361a1fa..acac323 100644 --- a/src/borgstore/backends/rest.py +++ b/src/borgstore/backends/rest.py @@ -6,14 +6,20 @@ import re import json from typing import Iterator, Dict, Optional +from types import ModuleType from http import HTTPStatus as HTTP from urllib.parse import unquote +requests: Optional[ModuleType] = None +HTTPBasicAuth: Optional[type] = None try: - import requests - from requests.auth import HTTPBasicAuth + import requests as requests_module + from requests.auth import HTTPBasicAuth as HTTPBasicAuth_class + + requests = requests_module + HTTPBasicAuth = HTTPBasicAuth_class except ImportError: - requests = HTTPBasicAuth = None + pass from ._base import BackendBase, ItemInfo, validate_name from ._utils import make_range_header diff --git a/src/borgstore/backends/sftp.py b/src/borgstore/backends/sftp.py index 71ef325..99028f7 100644 --- a/src/borgstore/backends/sftp.py +++ b/src/borgstore/backends/sftp.py @@ -297,6 +297,7 @@ def _sftp_hash(self, name: str, algorithm: str) -> str | None: except IOError: # check-file not supported or algorithm not supported self.check_file_supported = False + return None def hash(self, name: str, algorithm: str = "sha256") -> str: if not self.opened: From 4e6ec7c5f08c5bee10bbdc4e80df83de83dd245d Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 17:07:07 +0100 Subject: [PATCH 04/10] flake8: ignore E402 (imports not at top of file) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2a40ed6..881906a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,7 @@ testpaths = ["tests"] [tool.flake8] # Ignoring E203 due to https://github.com/PyCQA/pycodestyle/issues/373 -ignore = ['E226', 'W503', 'E203'] +ignore = ['E226', 'W503', 'E203', 'E402'] max_line_length = 120 exclude = ['build', 'dist', '.git', '.idea', '.mypy_cache', '.tox'] From 8f4e32ecd727ac5bbf3845a8538cc1888beeb42d Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 17:11:55 +0100 Subject: [PATCH 05/10] CI: do not install/configure sftp/s3 server for flake8/mypy --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50b174f..23afec8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,7 +79,7 @@ jobs: brew update brew install rclone - name: Configure OpenSSH SFTP server (test only) - if: runner.os == 'Linux' + if: runner.os == 'Linux' && matrix.toxenv != 'flake8' && matrix.toxenv != 'mypy' run: | sudo mkdir -p /run/sshd sudo useradd -m -s /bin/bash sftpuser || true @@ -108,7 +108,7 @@ jobs: # Export SFTP test URL for tox via GITHUB_ENV echo "BORGSTORE_TEST_SFTP_URL=sftp://sftpuser@localhost:22/borgstore/temp-store" >> $GITHUB_ENV - name: Install and configure MinIO S3 server (test only) - if: runner.os == 'Linux' + if: runner.os == 'Linux' && matrix.toxenv != 'flake8' && matrix.toxenv != 'mypy' run: | set -e arch=$(uname -m) From cbd2696c69d8eac8be0a9fb877024ab1a2d5a42e Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 17:13:26 +0100 Subject: [PATCH 06/10] tox: install dependencies --- tox.ini | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 8929b24..3cc1740 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,10 @@ commands = flake8 src changedir = deps = mypy + requests + boto3 + paramiko + types-requests types-boto3 - types-paramiko >= 1.9.1 - types-requests >= 2.25.1 + types-paramiko commands = mypy From 6061224c3ae4a0d6d6ccb9defa512aaf5fc437d0 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 17:15:48 +0100 Subject: [PATCH 07/10] CI: clarify install borgstore step --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23afec8..cc7b68d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,13 +141,13 @@ jobs: run: | python -m pip install --upgrade pip setuptools pip install -r requirements.d/dev.txt - - name: Install borgstore + - name: Install borgstore (Linux) if: runner.os == 'Linux' run: pip install -ve ".[s3,sftp,rest,rclone]" - - name: Install borgstore + - name: Install borgstore (Windows) if: runner.os == 'Windows' run: pip install -ve ".[rest]" - - name: Install borgstore + - name: Install borgstore (macOS) if: runner.os == 'macOS' run: pip install -ve ".[rest,rclone]" - name: run tox envs From 2a4966bab0b56cd7cfa22c01d947afbc29246a85 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 17:33:14 +0100 Subject: [PATCH 08/10] sftp: host_config["port"] is a str --- src/borgstore/backends/sftp.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/borgstore/backends/sftp.py b/src/borgstore/backends/sftp.py index 99028f7..1db00d6 100644 --- a/src/borgstore/backends/sftp.py +++ b/src/borgstore/backends/sftp.py @@ -92,11 +92,11 @@ def _get_host_config(self): host_config.update(self._get_host_config_from_file("~/.ssh/config", self.hostname)) # Now override configured values with provided values if self.username is not None: - host_config.update({"user": self.username}) + host_config["user"] = self.username if self.port != 0: - host_config.update({"port": self.port}) - # Make sure port is present and is an int - host_config["port"] = int(host_config.get("port") or 22) + host_config["port"] = str(self.port) + # Make sure port is present. + host_config["port"] = str(host_config.get("port") or "22") return host_config def _connect(self): @@ -109,7 +109,7 @@ def _connect(self): ssh.connect( hostname=host_config["hostname"], username=host_config.get("user"), # if None, paramiko will use current user - port=host_config["port"], + port=int(host_config["port"]), key_filename=host_config.get("identityfile"), # list of keys, ~ is already expanded allow_agent=True, ) From 0fa6cc6aa9a2b4c54edabc615efec9ca1cf424dd Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 17:36:22 +0100 Subject: [PATCH 09/10] add .DS_Store to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4d60281..ce73426 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea +.DS_Store .pytest_cache .mypy_cache .tox From 62546b5005843a51e3cb7b57423ea36b773bf48e Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Mar 2026 17:38:55 +0100 Subject: [PATCH 10/10] pyproject.toml: add ruff config --- pyproject.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 881906a..401a9f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,13 @@ ignore = ['E226', 'W503', 'E203', 'E402'] max_line_length = 120 exclude = ['build', 'dist', '.git', '.idea', '.mypy_cache', '.tox'] +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +# E402: Module level import not at top of file +ignore = ["E402"] + [tool.mypy] python_version = '3.10' strict_optional = false