Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/actions/setup-python-env/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Setup Python env
description: Setup Python and dependencies
inputs:
python-version:
description: Python version to use
required: true
runs:
using: composite
steps:
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: uv.lock
version: "0.10.11"

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version }}

- name: Install dependencies
shell: bash
run: uv sync --all-extras --dev
65 changes: 65 additions & 0 deletions .github/workflows/code-checkers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Code checkers

on: [push, pull_request]

env:
PYTHON_VERSIONS: '["3.8", "3.14"]'

jobs:
setup:
name: Setup
runs-on: ubuntu-latest
outputs:
python-versions: ${{ env.PYTHON_VERSIONS }}
python-latest: ${{ steps.set-vars.outputs.python-latest }}
steps:
- id: set-vars
run: |
LATEST=$(echo '${{ env.PYTHON_VERSIONS }}' | jq -c '.[-1:]')
echo "python-latest=$LATEST" >> $GITHUB_OUTPUT

linting:
name: Linting
runs-on: ubuntu-latest
needs: setup
strategy:
matrix:
python-version: ${{ fromJSON(needs.setup.outputs.python-versions) }}
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-python-env
with:
python-version: ${{ matrix.python-version }}
- run: uv run ruff check

static-type-checking:
name: Static type checking
runs-on: ubuntu-latest
needs: setup
strategy:
matrix:
python-version: ${{ fromJSON(needs.setup.outputs.python-latest) }}
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-python-env
with:
python-version: ${{ matrix.python-version }}

- name: Mypy check
run: uv run mypy

tests:
name: Tests
runs-on: ubuntu-latest
needs: setup
strategy:
matrix:
python-version: ${{ fromJSON(needs.setup.outputs.python-versions) }}
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-python-env
with:
python-version: ${{ matrix.python-version }}

- name: Pytest check
run: uv run pytest
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
__pycache__
.mypy_cache
.pytest_cache
.ruff_cache
*.egg-info
*.pyc
142 changes: 142 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
[project]
name = "xcp-storage"
dynamic = [
"authors",
"description",
"license",
"version"
]
requires-python = ">=3.7" # TODO(XCPNG-3032): Change it for modern python.

[build-system]
requires = ["setuptools>=40"]
build-backend = "setuptools.build_meta"

[dependency-groups]
dev = [
"mypy>=1.4.1",
"pytest>=7.4.4",
"ruff>=0.15.6",
]

[tool.mypy]
mypy_path = "stubs"
files = [
"src",
"tests"
]

disallow_untyped_defs = true
enable_error_code = ["explicit-override"]
ignore_missing_imports = false
implicit_optional = false

[[tool.mypy.overrides]]
module = "setuptools.*"
ignore_missing_imports = true

# To generate LINSTOR stubs, run the following commands in the LINSTOR source folder:
# ./setup.py build
# stubgen --include-private build/lib/linstor/ -o <XCP_STORAGE_PATH>/stubs/
# echo "from . import sharedconsts as consts" >> <XCP_STORAGE_PATH>/stubs/linstor/__init__.pyi
[[tool.mypy.overrides]]
module = "linstor.*"

disable_error_code = [
"arg-type",
"attr-defined",
"explicit-override",
"name-defined",
"no-redef",
"override"
]
disallow_untyped_defs = false

[tool.ruff]
include = [
"src/**/*.py",
"tests/**/*.py"
]
line-length = 120

[tool.ruff.lint]
exclude = ["stubs/*"]
ignore = [
"B019", # flake8-bugbear: cached-instance-method
"EM101", # flake8-errmsg: raw-string-in-exception
"EM102", # flake8-errmsg: f-string-in-exception
"S101", # flake8-bandit: assert
"S603", # flake8-bandit: subprocess-without-shell-equals-true
"TD003", # flake8-todos: missing-todo-link
"UP006", # pyupgrade: non-pep585-annotation
"UP007", # pyupgrade: non-pep604-annotation-union
"UP045", # pyupgrade: non-pep604-annotation-optional
]
select = [
"A", # flake8-builtins
"ANN", # flake8-annotations
"ARG", # flake8-unused-arguments
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"C90", # mccabe
"DTZ", # flake8-datetimez
"E", # pycodestyle: error
"EM", # flake8-errmsg
"ERA", # eradicate
"EXE", # flake8-executable
"F", # Pyflakes
"FBT", # flake8-boolean-trap
"FLY", # flynt
"FURB", # refurb
"G", # flake8-logging-format
"I", # isort
"ICN", # flake8-import-conventions
"INT", # flake8-gettext
"LOG", # flake8-logging
"N", # pep8-naming
"PERF", # perflint
"PIE", # flake8-pie
"PLW", # pylint: warning
"PTH", # flake8-use-pathlib
"Q", # flake8-quotes
"RET", # flake8-return
"RUF013", # ruff: implicit-optional
"RUF059", # ruff: unused-unpacked-variable
"S", # flake8-bandit
"SIM", # flake8-simplify
"SLF", # flake8-self
"T100", # flake8-debugger
"T20", # flake8-print
"TD", # flake8-todos
"TID", # flake8-tidy-imports
"UP", # pyupgrade
"W", # pycodestyle: warning
]
typing-modules = ["xcp_storage.typing"]

[tool.ruff.lint.isort.sections]
"typing" = ["xcp_storage.typing"]

[tool.ruff.lint.isort]
case-sensitive = false
combine-as-imports = true
force-sort-within-sections = true
known-first-party = ["xcp_storage"]
known-third-party = ["linstor"]
lines-after-imports = 1
order-by-type = false
section-order = [
"future",
"standard-library",
"third-party",
"first-party",
"local-folder",
"typing"
]

[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "all"

[tool.pytest.ini_options]
pythonpath = ["src", "stubs"]
testpaths = ["tests"]
18 changes: 18 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[metadata]
name = xcp-storage
version = 1.0.0
description = XCP storage layer, scripts and drivers
author = Ronan Abhamon <ronan.abhamon@vates.tech>
author_email = ronan.abhamon@vates.tech
url = https://vates.tech
license = GPLv3

[options]
packages = find:
package_dir =
=src
# TODO(XCPNG-3032): Change it for modern python.
python_requires = >=3.6

[options.packages.find]
where = src
21 changes: 21 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
#
# Copyright (C) 2026 Vates SAS
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License

from setuptools import setup

# ==============================================================================

if __name__ == "__main__":
setup()
Loading