Skip to content

Commit 56d20a2

Browse files
authored
Merge pull request #600 from Lzzz666/python-formatter
Enhance CI formatting checks for Python files
2 parents 88ddb73 + 58e975e commit 56d20a2

File tree

12 files changed

+399
-226
lines changed

12 files changed

+399
-226
lines changed

.ci/check-format.sh

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,14 @@ for file in ${SH_SOURCES}; do
2121
done
2222
SH_MISMATCH_FILE_CNT=$(shfmt -l ${SH_SOURCES})
2323

24-
exit $((C_MISMATCH_LINE_CNT + SH_MISMATCH_FILE_CNT))
24+
PY_SOURCES=$(find "${REPO_ROOT}" | egrep "\.py$")
25+
for file in ${PY_SOURCES}; do
26+
echo "Checking Python file: ${file}"
27+
black --diff "${file}"
28+
done
29+
PY_MISMATCH_FILE_CNT=0
30+
if [ -n "${PY_SOURCES}" ]; then
31+
PY_MISMATCH_FILE_CNT=$(echo "$(black --check ${PY_SOURCES} 2>&1)" | grep -c "^would reformat ")
32+
fi
33+
34+
exit $((C_MISMATCH_LINE_CNT + SH_MISMATCH_FILE_CNT + PY_MISMATCH_FILE_CNT))

.github/workflows/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,8 @@ jobs:
505505
- uses: actions/checkout@v4
506506
- name: coding convention
507507
run: |
508-
sudo apt-get install -q=2 clang-format-18 shfmt
508+
sudo apt-get install -q=2 clang-format-18 shfmt python3-pip
509+
pip3 install black==25.1.0
509510
.ci/check-newline.sh
510511
.ci/check-format.sh
511512
shell: bash

CONTRIBUTING.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ However, participation requires adherence to fundamental ground rules:
3939
While there is some flexibility in basic style, it is crucial to stick to the current coding standards.
4040
Complex algorithmic constructs without proper comments will not be accepted.
4141
* Shell scripts must be formatted before submission. Use consistent flags across the project to ensure uniform formatting.
42+
* Python scripts must be formatted before submission. Use consistent flags across the project to ensure uniform formatting.
4243
* External pull requests should include thorough documentation in the pull request comments for consideration.
4344
* When composing documentation, code comments, and other materials in English,
4445
please adhere to the American English (`en_US`) dialect.
@@ -48,9 +49,12 @@ However, participation requires adherence to fundamental ground rules:
4849
Software requirement:
4950
* [clang-format](https://clang.llvm.org/docs/ClangFormat.html) version 18 or later.
5051
* [shfmt](https://github.com/mvdan/sh).
52+
* [black](https://github.com/psf/black) version 25.1.0.
5153

52-
This repository consistently contains an up-to-date `.clang-format` file with rules that match the explained ones and uses shell script formatting supported by `shfmt`.
53-
For maintaining a uniform coding style, execute the command `clang-format -i *.{c,h}` and `shfmt -w $(find . -type f -name "*.sh")`.
54+
To maintain a uniform style across languages, run:
55+
* `clang-format -i *.{c,h}` to apply the project’s C/C++ formatting rules from the up-to-date .clang-format file.
56+
* `shfmt -w $(find . -type f -name "*.sh")` to clean and standardize all shell scripts.
57+
* `black .` to enforce a consistent, idiomatic layout for Python code.
5458

5559
## Coding Style for Shell Script
5660

@@ -65,6 +69,19 @@ Shell scripts must be clean, consistent, and portable. The following `shfmt` rul
6569
* Add spaces around redirection operators (e.g., `>`, `>>`).
6670
* Place binary operators (e.g., `&&`, `|`) on the next line when breaking lines.
6771

72+
## Coding Style for Python
73+
74+
Python scripts must be clean, consistent, and adhere to modern Python best practices. The following formatting rules are enforced project-wide using `black`:
75+
76+
* Use 4 spaces for indentation (never tabs).
77+
* Limit lines to 80 characters maximum.
78+
* Use double quotes for strings (unless single quotes avoid escaping).
79+
* Use trailing commas in multi-line constructs.
80+
* Format imports according to PEP 8 guidelines.
81+
* Use Unix-style line endings (LF).
82+
* Remove trailing whitespace at the end of lines.
83+
* Ensure files end with a newline.
84+
6885
## Coding Style for Modern C
6986

7087
This coding style is a variant of the [K&R style](https://en.wikipedia.org/wiki/Indentation_style#K&R).

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[tool.black]
2+
line-length = 80
3+
target-version = ['py39', 'py310', 'py311','py312','py313']
4+
include = '\.pyi?$'

tests/arch-test-target/constants.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import os
22

3-
dutname = 'rv32emu'
4-
refname = 'sail_cSim'
3+
dutname = "rv32emu"
4+
refname = "sail_cSim"
55

66
root = os.path.abspath(os.path.dirname(__file__))
77
cwd = os.getcwd()
88

9-
misa_A = (1 << 0)
10-
misa_C = (1 << 2)
11-
misa_E = (1 << 4)
12-
misa_F = (1 << 5)
13-
misa_I = (1 << 8)
14-
misa_M = (1 << 12)
9+
misa_A = 1 << 0
10+
misa_C = 1 << 2
11+
misa_E = 1 << 4
12+
misa_F = 1 << 5
13+
misa_I = 1 << 8
14+
misa_M = 1 << 12
1515

16-
config_temp = '''[RISCOF]
16+
config_temp = """[RISCOF]
1717
ReferencePlugin={0}
1818
ReferencePluginPath={1}
1919
DUTPlugin={2}
@@ -29,4 +29,4 @@
2929
[{0}]
3030
pluginpath={1}
3131
path={1}
32-
'''
32+
"""

tests/arch-test-target/rv32emu/riscof_rv32emu.py

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515

1616
logger = logging.getLogger()
1717

18+
1819
class rv32emu(pluginTemplate):
1920
__model__ = "rv32emu"
2021
__version__ = "dev"
2122

2223
def __init__(self, *args, **kwargs):
2324
sclass = super().__init__(*args, **kwargs)
2425

25-
config = kwargs.get('config')
26+
config = kwargs.get("config")
2627

2728
# If the config node for this DUT is missing or empty. Raise an error. At minimum we need
2829
# the paths to the ispec and pspec files
@@ -34,25 +35,27 @@ def __init__(self, *args, **kwargs):
3435
# test-bench produced by a simulator (like verilator, vcs, incisive, etc). In case of an iss or
3536
# emulator, this variable could point to where the iss binary is located. If 'PATH variable
3637
# is missing in the config.ini we can hardcode the alternate here.
37-
self.dut_exe = os.path.join(config['PATH'] if 'PATH' in config else "","rv32emu")
38+
self.dut_exe = os.path.join(
39+
config["PATH"] if "PATH" in config else "", "rv32emu"
40+
)
3841

3942
# Number of parallel jobs that can be spawned off by RISCOF
4043
# for various actions performed in later functions, specifically to run the tests in
4144
# parallel on the DUT executable. Can also be used in the build function if required.
42-
self.num_jobs = str(config['jobs'] if 'jobs' in config else 1)
45+
self.num_jobs = str(config["jobs"] if "jobs" in config else 1)
4346

4447
# Path to the directory where this python file is located. Collect it from the config.ini
45-
self.pluginpath=os.path.abspath(config['pluginpath'])
48+
self.pluginpath = os.path.abspath(config["pluginpath"])
4649

4750
# Collect the paths to the riscv-config absed ISA and platform yaml files. One can choose
4851
# to hardcode these here itself instead of picking it from the config.ini file.
49-
self.isa_spec = os.path.abspath(config['ispec'])
50-
self.platform_spec = os.path.abspath(config['pspec'])
52+
self.isa_spec = os.path.abspath(config["ispec"])
53+
self.platform_spec = os.path.abspath(config["pspec"])
5154

5255
# We capture if the user would like the run the tests on the target or
5356
# not. If you are interested in just compiling the tests and not running
5457
# them on the target, then following variable should be set to False
55-
if 'target_run' in config and config['target_run']=='0':
58+
if "target_run" in config and config["target_run"] == "0":
5659
self.target_run = False
5760
else:
5861
self.target_run = True
@@ -72,36 +75,57 @@ def initialise(self, suite, work_dir, archtest_env):
7275
# Note the march is not hardwired here, because it will change for each
7376
# test. Similarly the output elf name and compile macros will be assigned later in the
7477
# runTests function
75-
self.compile_cmd = os.getenv("CROSS_COMPILE") + 'gcc -march={0}\
78+
self.compile_cmd = (
79+
os.getenv("CROSS_COMPILE")
80+
+ "gcc -march={0}\
7681
-static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -g\
77-
-T '+self.pluginpath+'/env/link.ld\
78-
-I '+self.pluginpath+'/env/\
79-
-I ' + archtest_env + ' {2} -o {3} {4}'
82+
-T "
83+
+ self.pluginpath
84+
+ "/env/link.ld\
85+
-I "
86+
+ self.pluginpath
87+
+ "/env/\
88+
-I "
89+
+ archtest_env
90+
+ " {2} -o {3} {4}"
91+
)
8092

8193
def build(self, isa_yaml, platform_yaml):
8294
# load the isa yaml as a dictionary in python.
83-
ispec = utils.load_yaml(isa_yaml)['hart0']
95+
ispec = utils.load_yaml(isa_yaml)["hart0"]
8496

8597
# capture the XLEN value by picking the max value in 'supported_xlen' field of isa yaml. This
8698
# will be useful in setting integer value in the compiler string (if not already hardcoded);
87-
self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32')
88-
89-
if 'E' not in ispec['ISA']:
90-
self.compile_cmd = self.compile_cmd+' -mabi='+('lp64 ' if 64 in ispec['supported_xlen'] else 'ilp32 ')
99+
self.xlen = "64" if 64 in ispec["supported_xlen"] else "32"
100+
101+
if "E" not in ispec["ISA"]:
102+
self.compile_cmd = (
103+
self.compile_cmd
104+
+ " -mabi="
105+
+ ("lp64 " if 64 in ispec["supported_xlen"] else "ilp32 ")
106+
)
91107
else:
92-
self.compile_cmd = self.compile_cmd+' -mabi='+('lp64e ' if 64 in ispec['supported_xlen'] else 'ilp32e ')
93-
self.compile_cmd += '-D RV32E '
108+
self.compile_cmd = (
109+
self.compile_cmd
110+
+ " -mabi="
111+
+ ("lp64e " if 64 in ispec["supported_xlen"] else "ilp32e ")
112+
)
113+
self.compile_cmd += "-D RV32E "
94114

95115
def runTests(self, testList):
96116
# Delete Makefile if it already exists.
97-
if os.path.exists(self.work_dir+ "/Makefile." + self.name[:-1]):
98-
os.remove(self.work_dir+ "/Makefile." + self.name[:-1])
117+
if os.path.exists(self.work_dir + "/Makefile." + self.name[:-1]):
118+
os.remove(self.work_dir + "/Makefile." + self.name[:-1])
99119
# create an instance the makeUtil class that we will use to create targets.
100-
make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + self.name[:-1]))
120+
make = utils.makeUtil(
121+
makefilePath=os.path.join(
122+
self.work_dir, "Makefile." + self.name[:-1]
123+
)
124+
)
101125

102126
# set the make command that will be used. The num_jobs parameter was set in the __init__
103127
# function earlier
104-
make.makeCommand = 'make -j' + self.num_jobs
128+
make.makeCommand = "make -j" + self.num_jobs
105129

106130
# we will iterate over each entry in the testList. Each entry node will be refered to by the
107131
# variable testname.
@@ -110,14 +134,14 @@ def runTests(self, testList):
110134
testentry = testList[testname]
111135

112136
# we capture the path to the assembly file of this test
113-
test = testentry['test_path']
137+
test = testentry["test_path"]
114138

115139
# capture the directory where the artifacts of this test will be dumped/created. RISCOF is
116140
# going to look into this directory for the signature files
117-
test_dir = testentry['work_dir']
141+
test_dir = testentry["work_dir"]
118142

119143
# name of the elf file after compilation of the test
120-
elf = 'my.elf'
144+
elf = "my.elf"
121145

122146
# name of the signature file as per requirement of RISCOF. RISCOF expects the signature to
123147
# be named as DUT-<dut-name>.signature. The below variable creates an absolute path of
@@ -127,22 +151,26 @@ def runTests(self, testList):
127151
# for each test there are specific compile macros that need to be enabled. The macros in
128152
# the testList node only contain the macros/values. For the gcc toolchain we need to
129153
# prefix with "-D". The following does precisely that.
130-
compile_macros = ' -D' + " -D".join(testentry['macros'])
154+
compile_macros = " -D" + " -D".join(testentry["macros"])
131155

132156
# substitute all variables in the compile command that we created in the initialize function
133-
cmd = self.compile_cmd.format(testentry['isa'].lower(), self.xlen, test, elf, compile_macros)
157+
cmd = self.compile_cmd.format(
158+
testentry["isa"].lower(), self.xlen, test, elf, compile_macros
159+
)
134160

135-
# if the user wants to disable running the tests and only compile the tests, then
161+
# if the user wants to disable running the tests and only compile the tests, then
136162
# the "else" clause is executed below assigning the sim command to simple no action
137163
# echo statement.
138164
if self.target_run:
139-
# set up the simulation command. Template is for spike. Please change.
140-
simcmd = self.dut_exe + ' -a {0} {1}'.format(sig_file, elf)
165+
# set up the simulation command. Template is for spike. Please change.
166+
simcmd = self.dut_exe + " -a {0} {1}".format(sig_file, elf)
141167
else:
142168
simcmd = 'echo "NO RUN"'
143169

144170
# concatenate all commands that need to be executed within a make-target.
145-
execute = '@cd {0}; {1}; {2};'.format(testentry['work_dir'], cmd, simcmd)
171+
execute = "@cd {0}; {1}; {2};".format(
172+
testentry["work_dir"], cmd, simcmd
173+
)
146174

147175
# create a target. The makeutil will create a target with the name "TARGET<num>" where num
148176
# starts from 0 and increments automatically for each new target that is added

0 commit comments

Comments
 (0)