Skip to content

Commit 5d983dd

Browse files
Update to Python 3.10, Bazel 5.4 and replace Black/Autoflake with Ruff (UBC-Thunderbots#3292)
* Update to python 3.10.0 and bazel 5.4.0 * No need to import `typing` for collections as of python 3.9 * Update black formatter to 24.4.2 * Run fix_formatting.sh * Remove PyOpenGL-accelerate dependency * Install latest pip in setup_software.sh * Install latest pip in setup_software.sh * Testing * Testing * Remove testing * Bump pyqtgraph to 0.13.3 * Update setup_software.sh * Try fixing precommit * Try fixing precommit * Try fixing precommit * Replace black/autoflake with ruff * Remove black binary * Remove git attribute * Remove unused check_formatting_ci.sh * Run fix_formatting.sh * Add simulated/field test fixtures to conftest.py * Address UBC-Thunderbots#3251 * Python code cleanup and fix Thunderscope * Enable pydocstyle rules in ruff * Add compile pip requirements to pre-commit * Formatting and fix pre-commit * Fix pre-commit * Fix pre-commit * Fix pre-commit and fsm_diagram_generator.py * Update docstrings * Formatting * Fix type errors * Nuke jetson_nano/display :( * Switch to pyqtdarktheme * Bump pytqtgraph to 0.13.7 * If build fails, download autoref from Google Drive mirror * Fix setup_software.sh * If build fails, download autoref from releases in github fork * Rename formatting_scripts to scripts * Fix docstring * FIx docstring * [pre-commit.ci lite] apply automatic fixes * Fix pyqtdarktheme dep missing * Nits * Use python dataclasses, remove unused stuff, nits * [pre-commit.ci lite] apply automatic fixes * Use dataclass * Nits --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent 3cc5a2d commit 5d983dd

File tree

197 files changed

+1996
-3651
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

197 files changed

+1996
-3651
lines changed

.gitattributes

+1-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@
77
* text=auto
88

99
# Our clang-format executable is a binary
10-
formatting_scripts/clang-format-* binary
11-
12-
# Our black executable is a binary
13-
formatting_scripts/black* binary
10+
scripts/clang-format-* binary
1411

1512
# Treat images as binaries
1613
*.svg binary

.github/workflows/pre-commit.yml

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ jobs:
2424
run: |
2525
"${GITHUB_WORKSPACE}"/environment_setup/setup_software.sh
2626
27+
- name: Install pip
28+
run: curl -sS https://bootstrap.pypa.io/get-pip.py | python
29+
2730
- uses: pre-commit/[email protected]
2831

2932
- uses: pre-commit-ci/[email protected]

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ src/config
148148
*.ipynb
149149

150150
# codespell dictionary
151-
formatting_scripts/dictionary/
151+
scripts/dictionary/
152152

153153
# clangd for nvim lsp
154154
src/.clangd

.pre-commit-config.yaml

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
repos:
22
- repo: local
33
hooks:
4-
- id: fix_formatting
4+
- id: compile_pip_requirements
5+
name: compile pip requirements
6+
language: system
7+
entry: scripts/compile_pip_requirements.sh
8+
9+
- id: lint_and_format
510
name: fix common trivial code errors
611
language: system
7-
entry: formatting_scripts/fix_formatting.sh
12+
entry: scripts/lint_and_format.sh
813

914
- id: generate_fsm_diagrams
1015
name: generate new FSM diagrams
1116
language: python
12-
entry: ./src/software/ai/fsm_diagram_generator.py
17+
entry: scripts/fsm_diagram_generator.py

docs/fsm-diagrams.md

+3-6
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,9 @@ Stop --> SetPlay : [gameStateSetupRestart]\n<i>setupSetPlay</i>
1717
Playing --> Halt : [gameStateHalted]\n<i>setupHaltPlay</i>
1818
Playing --> Stop : [gameStateStopped]\n<i>setupStopPlay</i>
1919
Playing --> SetPlay : [gameStateSetupRestart]\n<i>setupSetPlay</i>
20-
SetPlay --> SetPlay : [gameStateHalted]\n<i>(resetSetPlay</i>
21-
setupHaltPlay_A) --> Halt
22-
SetPlay --> SetPlay : [gameStateStopped]\n<i>(resetSetPlay</i>
23-
setupStopPlay_A) --> Stop
24-
SetPlay --> SetPlay : [gameStatePlaying]\n<i>(resetSetPlay</i>
25-
setupOffensePlay_A) --> Playing
20+
SetPlay --> Halt : [gameStateHalted]\n<i>resetSetPlay, setupHaltPlay</i>
21+
SetPlay --> Stop : [gameStateStopped]\n<i>resetSetPlay, setupStopPlay</i>
22+
SetPlay --> Playing : [gameStatePlaying]\n<i>resetSetPlay, setupOffensePlay</i>
2623
SetPlay --> SetPlay : [gameStateSetupRestart]\n<i>setupSetPlay</i>
2724
Terminate:::terminate --> Terminate:::terminate
2825

docs/getting-started.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,8 @@ Now that you're setup, if you can run it on the command line, you can run it in
215215
- `[--run_blue | --run_yellow]` indicate which FullSystem to run
216216
- `[--run_diagnostics]` indicates if diagnostics should be loaded as well
217217
- If FullSystem is running, the robots receive input from the AI
218-
- If Diagnostics is enabled, the robots can also receive input from Manual controls or XBox controls
219-
- This mode allows us to test and debug the robots by setting each robot's input to be either AI, Manual Control or XBox Control
218+
- If Diagnostics is enabled, the robots can also receive input from Manual controls or Xbox controls
219+
- This mode allows us to test and debug the robots by setting each robot's input to be either AI, Manual Control or Xbox Control
220220
- Control mode for each robot can be set with each one's drop down menu in the Robot View widget
221221

222222
- If we want to run it with real robots:
@@ -262,7 +262,7 @@ Now that you're setup, if you can run it on the command line, you can run it in
262262
- Manual Control
263263
- When a robot is in Manual control mode, the commands it receives depend on the radio buttons to the top-right
264264
- Diagnostics Control allows us to use the on-screen sliders and buttons to control the robot
265-
- XBox control allows us to use a connected XBox controller to control the robots
265+
- Xbox control allows us to use a connected Xbox controller to control the robots
266266
4. Run our SimulatedPlayTests in Thunderscope
267267
- This will launch the visualizer and simulate AI Plays, allowing us to visually see the robots acting according to their roles.
268268
1. For legacy C++ tests (#2581) with the visualizer:
@@ -443,7 +443,7 @@ If you do rebase or merge and get conflicts, you'll need to resolve them manuall
443443

444444
We use [clang-format](https://electronjs.org/docs/development/clang-format) to automatically format our code. Using an automatic tool helps keep things consistent across the codebase without developers having to change their personal style as they write. See the [code style guide](code-style-guide.md) for more information on exactly what it does.
445445

446-
To format the code, from the `Software` directory run `./formatting_scripts/fix_formatting.sh`.
446+
To format the code, from the `Software` directory run `./scripts/lint_and_format.sh`.
447447

448448
We recommend running the formatting script and then committing all your changes, so that your commits can more easily pass CI.
449449

environment_setup/setup_software.sh

+29-14
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ host_software_packages=(
6262
protobuf-compiler # This is required for the "NanoPb" library, which does not
6363
# properly manage this as a bazel dependency, so we have
6464
# to manually install it ourselves
65-
python3.8 # Python 3
66-
python3.8-dev # Python 3 headers
67-
python3.8-venv # Virtual Environment
68-
python3-pip # Required for bazel to install python dependencies for build targets
69-
python3-protobuf # This is required for the "NanoPb" library, which does not
70-
# properly manage this as a bazel dependency, so we have
71-
# to manually install it ourselves
72-
python3-yaml # Load dynamic parameter configuration files
65+
python3.10 # Python 3
66+
python3.10-dev # Python 3 headers
67+
python3.10-venv # Virtual Environment
68+
python3-pip # Required for bazel to install python dependencies for build targets
69+
python3-protobuf # This is required for the "NanoPb" library, which does not
70+
# properly manage this as a bazel dependency, so we have
71+
# to manually install it ourselves
72+
python3-yaml # Load dynamic parameter configuration files
7373
valgrind # Checks for memory leaks
7474
libsqlite3-dev # needed to build Python 3 with sqlite support
7575
libffi-dev # needed to use _ctypes in Python3
@@ -114,7 +114,12 @@ print_status_msg "Setting Up Virtual Python Environment"
114114
# delete tbotspython first
115115
sudo rm -rf /opt/tbotspython
116116

117-
if ! sudo /usr/bin/python3.8 -m venv /opt/tbotspython ; then
117+
if ! curl -sS https://bootstrap.pypa.io/get-pip.py | sudo /usr/bin/python3.10 ; then
118+
print_status_msg "Error: Installing pip failed"
119+
exit 1
120+
fi
121+
122+
if ! sudo /usr/bin/python3.10 -m venv /opt/tbotspython ; then
118123
print_status_msg "Error: Setting up virtual environment failed"
119124
exit 1
120125
fi
@@ -149,8 +154,15 @@ print_status_msg "Compiling TIGERS AutoRef"
149154
sudo wget -N https://github.com/TIGERs-Mannheim/AutoReferee/archive/refs/heads/autoref-ci.zip -O /tmp/autoref-ci.zip
150155
unzip -q -o -d /tmp/ /tmp/autoref-ci.zip
151156
touch /tmp/AutoReferee-autoref-ci/.git # a hacky way to make gradle happy when it tries to find a dependency
152-
/tmp/AutoReferee-autoref-ci/./gradlew installDist -p /tmp/AutoReferee-autoref-ci/ -Dorg.gradle.java.home=/usr/lib/jvm/jdk-17/
153-
cp -r /tmp/AutoReferee-autoref-ci/build/install/autoReferee/ /opt/tbotspython/autoReferee
157+
158+
if ! /tmp/AutoReferee-autoref-ci/./gradlew installDist -p /tmp/AutoReferee-autoref-ci/ -Dorg.gradle.java.home=/usr/lib/jvm/jdk-17/; then
159+
print_status_msg "Building TIGERS AutoRef failed. Downloading mirror"
160+
161+
wget https://github.com/UBC-Thunderbots/AutoReferee/releases/download/autoref-ci/autoReferee.tar.gz -O /tmp/autoReferee.tar.gz
162+
tar -xzf /tmp/autoReferee.tar.gz -C /opt/tbotspython/
163+
else
164+
cp -r /tmp/AutoReferee-autoref-ci/build/install/autoReferee/ /opt/tbotspython/autoReferee
165+
fi
154166

155167
sudo chmod +x "$CURR_DIR/../src/software/autoref/run_autoref.sh"
156168
sudo cp "$CURR_DIR/../src/software/autoref/DIV_B.txt" "/opt/tbotspython/autoReferee/config/geometry/DIV_B.txt"
@@ -160,8 +172,11 @@ print_status_msg "Finished setting up AutoRef"
160172
# Install Bazel
161173
print_status_msg "Installing Bazel"
162174

175+
# Uninstall Bazel first
176+
sudo rm -rf $HOME/.bazel
177+
163178
# Adapted from https://docs.bazel.build/versions/main/install-ubuntu.html#install-with-installer-ubuntu
164-
sudo wget -nc https://github.com/bazelbuild/bazel/releases/download/5.0.0/bazel-5.0.0-installer-linux-x86_64.sh -O /tmp/bazel-installer.sh
179+
sudo wget -nc https://github.com/bazelbuild/bazel/releases/download/5.4.0/bazel-5.4.0-installer-linux-x86_64.sh -O /tmp/bazel-installer.sh
165180
sudo chmod +x /tmp/bazel-installer.sh
166181
sudo /tmp/bazel-installer.sh --bin=/usr/bin --base=$HOME/.bazel
167182
echo "source ${HOME}/.bazel/bin/bazel-complete.bash" >> ~/.bashrc
@@ -184,8 +199,8 @@ sudo service udev restart
184199
# allow user access to serial ports
185200
sudo usermod -a -G dialout $USER
186201

187-
# installs PlatformIO to global environment
188-
if ! sudo /usr/bin/python3.8 -m pip install --prefix /usr/local platformio==6.1.13; then
202+
# install PlatformIO to global environment
203+
if ! sudo /usr/bin/python3.10 -m pip install platformio==6.1.13; then
189204
print_status_msg "Error: Installing PlatformIO failed"
190205
exit 1
191206
fi
+3-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
protobuf==3.20.2
2-
pyqtgraph==0.12.4
3-
autoflake==1.4
2+
pyqtgraph==0.13.7
3+
pyqtdarktheme==2.1.0
44
pyqt6==6.5.0
55
PyQt6-Qt6==6.5.0
66
thefuzz==0.19.0
77
iterfzf==0.5.0.20.0
8-
pyqtdarktheme==1.1.0
98
python-Levenshtein==0.12.2
109
psutil==5.9.0
11-
qt-material==2.12
1210
PyOpenGL==3.1.6
13-
PyOpenGL-accelerate==3.1.5
14-
psutil==5.9.0
1511
numpy==1.24.4
12+
ruff==0.5.5
+3-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
protobuf==3.20.2
2-
pyqtgraph==0.12.4
3-
autoflake==1.4
2+
pyqtgraph==0.13.7
3+
pyqtdarktheme==2.1.0
44
pyqt6==6.5.0
55
PyQt6-Qt6==6.5.0
66
thefuzz==0.19.0
77
iterfzf==0.5.0.20.0
8-
pyqtdarktheme==1.1.0
98
python-Levenshtein==0.12.2
109
psutil==5.9.0
11-
qt-material==2.12
1210
PyOpenGL==3.1.6
13-
psutil==5.9.0
1411
numpy==1.24.4
12+
ruff==0.5.5

formatting_scripts/black_19-10b0

-11.8 MB
Binary file not shown.

formatting_scripts/check_formatting_ci.sh

-35
This file was deleted.
File renamed without changes.

scripts/compile_pip_requirements.sh

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/bash
2+
3+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
4+
# This script runs all the `compile_pip_requirements` update targets in the repo
5+
# to generate requirements lock files from requirement.in files.
6+
#
7+
# Whenever we update requirements.in with new dependencies/versions, we need to
8+
# regenerate its associated requirements lock file and check it into git.
9+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
10+
11+
# The directory this script is in
12+
CURR_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
13+
14+
# The root bazel directory
15+
BAZEL_ROOT_DIR="$CURR_DIR/../src"
16+
17+
cd $BAZEL_ROOT_DIR
18+
bazel run //extlibs:nanopb_requirements.update
19+
bazel run //software/thunderscope:requirements.update
20+
bazel run //software/jetson_nano/ansible:requirements.update
21+
bazel run //software/simulated_tests:requirements.update

src/software/ai/fsm_diagram_generator.py scripts/fsm_diagram_generator.py

+30-27
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/opt/tbotspython/bin/python3.8
1+
#!/opt/tbotspython/bin/python3
22

33
import re
44
import os
@@ -8,17 +8,14 @@
88

99

1010
def generate_diagram(fsm):
11-
"""
12-
Generates a Mermaid text definition for a state diagram
13-
representing the FSM.
11+
"""Generates a Mermaid text definition for a state diagram representing the FSM.
1412
1513
Details on Mermaid UML syntax:
1614
https://mermaid.js.org/syntax/stateDiagram.html
1715
1816
:param fsm: the FSM code containing its transition table
19-
:returns: the mermaid.js text definition for FSM diagram
17+
:return: the mermaid.js text definition for FSM diagram
2018
"""
21-
2219
# Regex match to extract transition table from the FSM
2320
transition_table_match = re.search(r"make_transition_table\(([\s\S]*?)\);", fsm)
2421
if not transition_table_match:
@@ -33,34 +30,45 @@ def generate_diagram(fsm):
3330

3431
diagram = ""
3532

36-
transitions = transition_table.split(",")
37-
for transition in transitions:
33+
# Get the transitions in the transition table
34+
# Split by commas NOT in between parentheses (commas in parentheses separate actions)
35+
transitions = re.split(r",(?![^()]*\))", transition_table)
3836

37+
for transition in transitions:
3938
# Transitions have the following format:
4039
# src_state + event [guard] / action = dest_state
4140

4241
# Extract dest_state from transition, which is after '=' sign
43-
transition, *rest = transition.split("=")
44-
dest_state = rest[0] if rest else ""
42+
transition, _, dest_state = transition.partition("=")
4543

4644
# Extract action from transition, which is after '/' sign
47-
transition, *rest = transition.split("/")
48-
action = rest[0] if rest else ""
45+
transition, _, action = transition.partition("/")
4946

5047
# Extract guard contained between square brackets
5148
guard = next(iter(re.findall(r"\[(.*?)\]", transition)), "")
5249

5350
# Extract src_state from transition, which is before '+' sign
54-
src_state = transition.split("+")[0]
51+
src_state, _, _ = transition.partition("+")
5552

56-
def remove_suffix(str, suffix):
57-
return str[: -len(suffix)] if str.endswith(suffix) else str
53+
# There may be multiple guards, so get a list of the guards and the
54+
# operators separating them
55+
guard_list = re.split(r"(&&|\|\|)", guard)
5856

59-
# Remove suffixes from states, guard, action
60-
src_state = remove_suffix(src_state, "_S")
61-
dest_state = remove_suffix(dest_state, "_S")
62-
guard = " && ".join([remove_suffix(g, "_G") for g in guard.split("&&")])
63-
action = remove_suffix(action, "_A")
57+
# There may be multiple actions, so get a list of all of them
58+
if action.startswith("(") and action.endswith(")"):
59+
action_list = action.strip("()").split(",")
60+
else:
61+
action_list = [action]
62+
63+
# Remove suffixes from states, guards, actions
64+
src_state = src_state.removesuffix("_S")
65+
dest_state = dest_state.removesuffix("_S")
66+
guard_list = [g.removesuffix("_G") for g in guard_list]
67+
action_list = [a.removesuffix("_A") for a in action_list]
68+
69+
# Convert guard and action lists into strings
70+
guard = " ".join(guard_list)
71+
action = ", ".join(action_list)
6472

6573
# Terminate state is marked with X in transition table.
6674
# Give terminate state custom styling with the
@@ -97,17 +105,15 @@ def remove_suffix(str, suffix):
97105

98106

99107
if __name__ == "__main__":
100-
101-
root_dir = Path(os.path.abspath(__file__)).parents[3]
108+
root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
109+
ai_dir = os.path.join(root_dir, "src/software/ai")
102110

103111
fsm_diagrams = {}
104112
fsm_file_paths = {}
105113

106-
ai_dir = os.path.join(root_dir, "src/software/ai")
107114
for root, dirs, files in os.walk(ai_dir):
108115
dirs.sort()
109116
for file in sorted(files):
110-
111117
if not file.endswith("fsm.h"):
112118
continue
113119

@@ -119,7 +125,6 @@ def remove_suffix(str, suffix):
119125
diagram = generate_diagram(fsm)
120126

121127
if diagram:
122-
123128
# Regex match to extract FSM struct name
124129
fsm_name = re.findall(r"struct\s+(\w+)\s(?:\s:\s.)?{?", fsm)[0]
125130

@@ -131,11 +136,9 @@ def remove_suffix(str, suffix):
131136

132137
output_file_path = os.path.join(root_dir, OUTPUT_FILE_PATH)
133138
with open(output_file_path, "w") as output_file:
134-
135139
print("# Play and Tactic FSM Diagrams\n", file=output_file)
136140

137141
for fsm_name, diagram in fsm_diagrams.items():
138-
139142
file_path = fsm_file_paths[fsm_name]
140143

141144
# Format diagram in Markdown syntax and write to output file

0 commit comments

Comments
 (0)