Skip to content

Commit 1b1d562

Browse files
Add Python3 pre-commit config checks with pylint and mypy
Also enables spell checking when pre-commit runs pylint. - Add pyproject.toml with config for mypy, black and isort - Add .pre-commit-config.yaml for https://github.com/pre-commit - Add GitHub action with cache to .github/workflows/build.yml - Fix execute permissions on files found by pre-commit checks. - build/mkinfo.py: Add module docstring as requested by pylint. - tests/python/conftest.py: Add a pytest fixture for unit-tests - tests/python/test_parse_test_instance_string.py: Add an initial unit test for xtf-runner using the pytest fixture. - xtf-runner: - Import json always as Python2.7 is the minium version - Add comments to locally disable checks where needed. - Resolve a line-too-long while resolving a no-else-return Signed-off-by: Bernhard Kaindl <[email protected]>
1 parent 0a58a14 commit 1b1d562

File tree

10 files changed

+368
-17
lines changed

10 files changed

+368
-17
lines changed

.github/workflows/build.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,46 @@ name: build
33
on: [push, pull_request]
44

55
jobs:
6+
python-tests:
7+
name: "Python Tests"
8+
runs-on: ubuntu-20.04
9+
steps:
10+
- name: Checkout code
11+
uses: actions/checkout@v3
12+
with:
13+
# To have access to secrets, use the PR branch:
14+
# https://github.com/orgs/community/discussions/26409
15+
ref: ${{github.event.pull_request.head.ref}}
16+
repository: ${{github.event.pull_request.head.repo.full_name}}
17+
18+
- name: pre-commit checks - setup cache
19+
uses: actions/cache@v3
20+
with:
21+
path: ~/.cache/pre-commit
22+
key: pre-commit|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
23+
24+
- name: pre-commit checks - run checks
25+
uses: pre-commit/[email protected]
26+
env:
27+
# For merging PRs to master, skip the no-commit-to-branch check:
28+
SKIP: no-commit-to-branch
29+
30+
- name: Install Python2 dependencies
31+
run: |
32+
#: Install Python 2.7 from Ubuntu 20.04 using apt-get install
33+
sudo apt-get update && sudo apt-get install -y python2
34+
curl -sSL https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py
35+
python2 get-pip.py
36+
if [ -f requirements.txt ]; then pip2 install -r requirements.txt; fi
37+
if [ -f requirements-dev.txt ]; then pip2 install -r requirements-dev.txt; fi
38+
pip2 install pytest pylint==1.9.4
39+
40+
- name: Run pylint-1.9.4
41+
run: python2 -m pylint xtf-runner build/*.py
42+
43+
- name: Run python2 -m pytest to execute all unit and integration tests
44+
run: python2 -m pytest -v -rA
45+
646
build:
747

848
strategy:

.pre-commit-config.yaml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#
2+
# Installation and activation of pre-commit:
3+
#
4+
# $ pip3 install pre-commit && pre-commit install
5+
#
6+
# With this, you get git pre-commit hook checks like shown in the terminal
7+
# on this page: https://pre-commit.com/index.html#usage
8+
#
9+
# It is easy to handle as a pre-commit hook once you get the hang of it.
10+
# If not skipped using git commit --no-verify, it makes each commit bisect-able,
11+
# whereas a check only run on a GitHub CI push will normally only run its checks
12+
# on the latest commit of a tree.
13+
#
14+
# However, it does not have to be used as a pre-commit hook:
15+
# Of course it can be used just for GitHub CI only:
16+
#
17+
# pre-commit runs in the GitHub Workflow of this project on each push and PR.
18+
# You can run it locally as well on demand after making changes to check them:
19+
#
20+
# $ pip3 install pre-commit
21+
# $ pre-commit run # run only needed checks
22+
# $ pre-commit run -a # run all fixes and checks
23+
#
24+
# You can enable it as git pre-commit hook for your local clone using:
25+
# $ pre-commit install
26+
#
27+
# Then, the fixups and checks defined below run by default when you commit.
28+
#
29+
# To commit with all git pre-commit hooks skipped (for exceptional cases):
30+
# $ git commit --no-verify
31+
#
32+
# To commit with some checks skipped:
33+
# $ SKIP=mypy,pylint git commit -m "unchecked emergency commit"
34+
#
35+
# Documentation: https://pre-commit.com/#temporarily-disabling-hooks
36+
# All hooks: https://pre-commit.com/hooks.html
37+
#
38+
fail_fast: false
39+
default_stages: [commit, push]
40+
repos:
41+
- repo: https://github.com/pre-commit/pre-commit-hooks
42+
rev: v4.5.0
43+
# https://github.com/pre-commit/pre-commit-hooks/blob/main/README.md:
44+
hooks:
45+
- id: no-commit-to-branch
46+
name: "ensure that you don't commit to the local master branch"
47+
args: [--branch, master]
48+
always_run: true
49+
- id: trailing-whitespace
50+
name: 'check and fix files to have no trailing whitespace'
51+
exclude: install-sh
52+
- id: end-of-file-fixer
53+
name: 'check and fix that files have a trailing newline'
54+
exclude: |
55+
(?x)^(
56+
arch/x86/include/arch/msr-index.h
57+
)
58+
- id: mixed-line-ending
59+
args: ['--fix=lf']
60+
name: 'check and fix that line endings are line feeds'
61+
- id: check-added-large-files
62+
args: ['--maxkb=12']
63+
name: 'check that no large files are added'
64+
- id: check-executables-have-shebangs
65+
- id: debug-statements
66+
name: 'check for debugger imports and breakpoint calls'
67+
- id: check-shebang-scripts-are-executable
68+
- id: check-merge-conflict
69+
- id: check-yaml
70+
name: 'check the syntax of yaml files'
71+
- repo: local
72+
hooks:
73+
- id: pytest
74+
name: run Python3 unit tests
75+
entry: env PYTHONDEVMODE=yes python3 -m pytest -v
76+
pass_filenames: false
77+
language: python
78+
types: [python]
79+
additional_dependencies: [pytest]
80+
- repo: https://github.com/akaihola/darker
81+
rev: 1.7.2
82+
hooks:
83+
- id: darker
84+
name: format staged changes like black and isort would format them
85+
args: [--skip-string-normalization, --isort, -tpy36]
86+
additional_dependencies: [isort]
87+
- repo: https://github.com/pre-commit/mirrors-mypy
88+
rev: v1.8.0
89+
hooks:
90+
- id: mypy
91+
name: run mypy
92+
- repo: https://github.com/pylint-dev/pylint
93+
rev: v3.0.3
94+
hooks:
95+
- id: pylint
96+
name: run pylint
97+
args: [--jobs=4, --spelling-dict=en_US] # Spell checks: See .pylintrc
98+
files: '(^xtf-runner|\.py)$'
99+
log_file: ".git/pre-commit-pylint.log"
100+
additional_dependencies: ['pylint[spelling]', pytest]
101+
- repo: local
102+
hooks:
103+
- id: git-diff # Ref: https://github.com/pre-commit/pre-commit/issues/1712
104+
name: Show and fail on not staged changes, also fixups may make them
105+
entry: git diff --exit-code
106+
language: system
107+
pass_filenames: false
108+
always_run: true

.pylintrc

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ extension-pkg-whitelist=
3737
# Enable the message, report, category or checker with the given id(s). You can
3838
# either give multiple identifier separated by comma (,) or put this option
3939
# multiple time. See also the "--disable" option for examples.
40-
#enable=
40+
enable=spelling
4141

4242
# Disable the message, report, category or checker with the given id(s). You
4343
# can either give multiple identifiers separated by comma (,) or put this
@@ -48,7 +48,24 @@ extension-pkg-whitelist=
4848
# --enable=similarities". If you want to run only the classes checker, but have
4949
# no Warning level messages displayed, use"--disable=all --enable=classes
5050
# --disable=W"
51-
disable=bad-whitespace, bad-continuation, global-statement, star-args
51+
disable=bad-whitespace, bad-continuation, global-statement, star-args,
52+
# Allow older pylint-1.9.x for Python2 to tolerate newer pylint options:
53+
bad-option-value,
54+
unrecognized-inline-option,
55+
# Not real problems, returns on the same indentation level can be easier:
56+
consider-using-with,
57+
no-else-raise,
58+
no-else-return,
59+
multiple-imports,
60+
len-as-condition,
61+
# For Python3-only projects:
62+
consider-using-f-string,
63+
deprecated-module,
64+
unrecognized-option,
65+
unspecified-encoding,
66+
use-implicit-booleaness-not-len,
67+
useless-object-inheritance,
68+
useless-option-value,
5269

5370

5471
[REPORTS]
@@ -103,6 +120,23 @@ ignore-docstrings=yes
103120
ignore-imports=no
104121

105122

123+
[SPELLING]
124+
125+
# Spelling dictionary name. Available dictionaries: en (aspell), en_AU
126+
# (aspell), en_CA (aspell), en_GB (aspell), en_US (aspell).
127+
# To support spelling checks for older python2 pylint versions,
128+
# `sudo apt-get install -y libenchant-2-2` would be needed,
129+
# so we enable it for newer Python3 pylint in .pre-commit-config.yaml:
130+
#spelling-dict=en
131+
132+
# A path to a file that contains the private dictionary; one word per line.
133+
spelling-private-dict-file=.pylintrc.project-dict.txt
134+
135+
# Tells whether to store unknown words to the private dictionary (see the
136+
# --spelling-private-dict-file option) instead of raising a message.
137+
spelling-store-unknown-words=y
138+
139+
106140
[TYPECHECK]
107141

108142
# Tells whether missing members accessed in mixin class should be ignored. A
@@ -337,4 +371,4 @@ max-public-methods=20
337371

338372
# Exceptions that will emit a warning when being caught. Defaults to
339373
# "Exception"
340-
overgeneral-exceptions=Exception
374+
overgeneral-exceptions=builtins.Exception

.pylintrc.project-dict.txt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
CFG
2+
CWD
3+
ENVS
4+
Normalise
5+
O_CREAT
6+
O_RDONLY
7+
VM
8+
arg
9+
basestring
10+
config
11+
conftest
12+
env
13+
dev
14+
dirs
15+
entrypoint
16+
epilog
17+
hvm
18+
init
19+
invlpg
20+
iopl
21+
json
22+
logfile
23+
logline
24+
logpath
25+
mkcfg
26+
nonexisting
27+
pseduo
28+
pv
29+
py
30+
pylintrc
31+
pyright
32+
pytest
33+
reportMissingImports
34+
reportUndefinedVariable
35+
stdout
36+
subproc
37+
substitue
38+
sys
39+
toolstack
40+
unioned
41+
Unrecognised
42+
xenconsole
43+
xenconsoled
44+
xl
45+
xtf
46+
os
47+
pre
48+
src
49+
dir

build/mkcfg.py

100644100755
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import sys, os
1010

1111
# Usage: mkcfg.py $OUT $DEFAULT-CFG $EXTRA-CFG $VARY-CFG
12-
_, out, defcfg, vcpus, extracfg, varycfg = sys.argv
12+
_, out, defcfg, vcpus, extracfg, varycfg = sys.argv # pylint: disable=unbalanced-tuple-unpacking
1313

1414
# Evaluate environment and name from $OUT
1515
_, env, name = out.split('.')[0].split('-', 2)

build/mkinfo.py

100644100755
Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,43 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
3+
"""
4+
mkcfg.py - Generate a configuration JSON file based on provided parameters.
5+
6+
Usage:
7+
python mkcfg.py $OUT $NAME $CATEGORY $ENVS $VARIATIONS
8+
9+
Arguments:
10+
11+
$OUT: Path to the output file where the generated JSON configuration
12+
will be saved.
13+
$NAME: Name to be assigned in the configuration.
14+
$CATEGORY: Category designation in the configuration.
15+
$ENVS: Optional space-separated list of environments (can be empty).
16+
$VARIATIONS: Optional space-separated list of variations (can be empty).
17+
18+
Description:
19+
20+
This script generates a JSON configuration file using provided parameters
21+
and saves it to the specified output file. The generated JSON structure
22+
includes fields for 'name', 'category', 'environments', and 'variations'.
23+
The 'environments' and 'variations' fields can be populated with
24+
space-separated lists if corresponding arguments ($ENVS and $VARIATIONS)
25+
are provided.
26+
27+
Example:
28+
29+
python mkcfg.py config.json ExampleConfig Utilities prod dev test
30+
31+
This example will create a configuration file named 'config.json' with
32+
'name' as 'ExampleConfig',
33+
'category' as 'Utilities', and
34+
'environments' as ['prod', 'dev', 'test'].
35+
"""
336

437
import sys, json
538

639
# Usage: mkcfg.py $OUT $NAME $CATEGORY $ENVS $VARIATIONS
7-
_, out, name, cat, envs, variations = sys.argv
40+
_, out, name, cat, envs, variations = sys.argv # pylint: disable=unbalanced-tuple-unpacking
841

942
template = {
1043
"name": name,

include/xen/sysctl.h

100755100644
File mode changed.

tests/python/conftest.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""pytest fixtures for unit-testing functions in the xtf-runner script"""
2+
import os
3+
import sys
4+
5+
import pytest
6+
7+
8+
def import_script_as_module(relative_script_path):
9+
"Import a Python script without the .py extension as a python module"
10+
11+
script_path = os.path.join(os.path.dirname(__file__), relative_script_path)
12+
module_name = os.path.basename(script_path)
13+
14+
if sys.version_info.major == 2:
15+
# Use deprecated imp module because it needs also to run with Python27:
16+
# pylint: disable-next=import-outside-toplevel
17+
import imp # pyright: ignore[reportMissingImports]
18+
19+
return imp.load_source(module_name, script_path)
20+
else:
21+
# For Python 3.11+: Import Python script without the .py extension:
22+
# https://gist.github.com/bernhardkaindl/1aaa04ea925fdc36c40d031491957fd3:
23+
24+
# pylint: disable-next=import-outside-toplevel
25+
from importlib import ( # pylint: disable=no-name-in-module
26+
machinery,
27+
util,
28+
)
29+
30+
loader = machinery.SourceFileLoader(module_name, script_path)
31+
spec = util.spec_from_loader(module_name, loader)
32+
assert spec
33+
assert spec.loader
34+
module = util.module_from_spec(spec)
35+
# It is probably a good idea to add the imported module to sys.modules:
36+
sys.modules[module_name] = module
37+
spec.loader.exec_module(module)
38+
return module
39+
40+
41+
@pytest.fixture(scope="session")
42+
def imported_xtf_runner():
43+
"""Fixture to import a script as a module for unit testing its functions"""
44+
return import_script_as_module("../../xtf-runner")
45+
46+
47+
@pytest.fixture(scope="function")
48+
def xtf_runner(imported_xtf_runner): # pylint: disable=redefined-outer-name
49+
"""Test fixture for unit tests: initializes module for each test function"""
50+
# Init the imported xtf-runner, so each unit test function gets it pristine:
51+
# May be used to unit-test xtf-runner with other, different test dirs:
52+
imported_xtf_runner._all_test_info = {} # pylint: disable=protected-access
53+
# The GitHub pre-commit action for does not start the checks in the src dir:
54+
# os.chdir(os.path.join(os.path.dirname(__file__), "../.."))
55+
return imported_xtf_runner

0 commit comments

Comments
 (0)