Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pre-commit workflow including ruff #207

Merged
merged 14 commits into from
Nov 26, 2024
21 changes: 21 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Pre-Commit

on:
pull_request:

jobs:
pre-commit:
name: Pre-commit
runs-on: ubuntu-latest
container:
image: ghcr.io/ptb-mr/mrpro_py311:latest
options: --user runner
steps:
- uses: actions/checkout@v4
- uses: pre-commit/[email protected]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}

# Cancel in-progress runs when a new workflow with the same group name is triggered
cancel-in-progress: true
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,6 @@ venv.bak/

# PyCharm config folder
\.idea/

# VS Code config folder
\.vscode/
28 changes: 28 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
default_language_version:
python: python3

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-added-large-files
# - id: check-docstring-first
- id: check-merge-conflict
- id: check-yaml
- id: check-toml
- id: check-json
exclude: ^.vscode/
- id: mixed-line-ending

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff # linter
args: [--fix]
- id: ruff-format # formatter

- repo: https://github.com/crate-ci/typos
rev: v1.25.0
hooks:
- id: typos
exclude: ^pypulseq/seq_examples/|paper.md
5 changes: 2 additions & 3 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#
import os
import sys

sys.path.insert(0, os.path.abspath('../../'))


Expand All @@ -30,9 +31,7 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'recommonmark'
]
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'recommonmark']

source_suffix = {
'.rst': 'restructuredtext',
Expand Down
54 changes: 37 additions & 17 deletions doc/walkthrough/gre_walkthrough.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,25 @@
"outputs": [],
"source": [
"seq = Sequence()\n",
"fov = 256e-3 # field of view\n",
"Nx = 256 # number of frequency encodes\n",
"Ny = 256 # number of phase encodes\n",
"alpha = 10 # RF flip\n",
"fov = 256e-3 # field of view\n",
"Nx = 256 # number of frequency encodes\n",
"Ny = 256 # number of phase encodes\n",
"alpha = 10 # RF flip\n",
"slice_thickness = 3e-3\n",
"TE = 7.38e-3 # echo time\n",
"TR = 100e-3 # repetition time\n",
"TE = 7.38e-3 # echo time\n",
"TR = 100e-3 # repetition time\n",
"\n",
"rf_spoiling_inc = 117\n",
"\n",
"sys = Opts(max_grad=28, grad_unit='mT/m', max_slew=150, slew_unit='T/m/s', rf_ringdown_time=20e-6, rf_dead_time=100e-6,\n",
" adc_dead_time=10e-6)"
"sys = Opts(\n",
" max_grad=28,\n",
" grad_unit='mT/m',\n",
" max_slew=150,\n",
" slew_unit='T/m/s',\n",
" rf_ringdown_time=20e-6,\n",
" rf_dead_time=100e-6,\n",
" adc_dead_time=10e-6,\n",
")"
]
},
{
Expand Down Expand Up @@ -88,23 +95,36 @@
}
],
"source": [
"rf, gz, gzr = make_sinc_pulse(flip_angle=alpha * math.pi / 180, duration=4e-3, slice_thickness=slice_thickness,\n",
" apodization=0.5, time_bw_product=4, system=sys, return_gz=True)\n",
"rf, gz, gzr = make_sinc_pulse(\n",
" flip_angle=alpha * math.pi / 180,\n",
" duration=4e-3,\n",
" slice_thickness=slice_thickness,\n",
" apodization=0.5,\n",
" time_bw_product=4,\n",
" system=sys,\n",
" return_gz=True,\n",
")\n",
"\n",
"delta_k = 1 / fov\n",
"gx = make_trapezoid(channel='x', flat_area=Nx * delta_k, flat_time=6.4e-3, system=sys)\n",
"adc = make_adc(num_samples=Nx, duration=gx.flat_time, delay=gx.rise_time, system=sys)\n",
"gx_pre = make_trapezoid(channel='x', area=-gx.area / 2, duration=2e-3, system=sys)\n",
"gz_reph = make_trapezoid(channel='z', area=-gz.area / 2, duration=2e-3, system=sys)\n",
"phase_areas = (np.arange(Ny) - Ny / 2) * delta_k\n",
"phase_areas = (np.arrange(Ny) - Ny / 2) * delta_k\n",
"\n",
"gx_spoil = make_trapezoid(channel='x', area=2 * Nx * delta_k, system=sys)\n",
"gz_spoil = make_trapezoid(channel='z', area=4 / slice_thickness, system=sys)\n",
"\n",
"delay_TE = math.ceil((TE - calc_duration(gx_pre) - gz.fall_time - gz.flat_time / 2 - calc_duration(\n",
" gx) / 2) / seq.grad_raster_time) * seq.grad_raster_time\n",
"delay_TR = math.ceil((TR - calc_duration(gx_pre) - calc_duration(gz) - calc_duration(\n",
" gx) - delay_TE) / seq.grad_raster_time) * seq.grad_raster_time\n",
"delay_TE = (\n",
" math.ceil(\n",
" (TE - calc_duration(gx_pre) - gz.fall_time - gz.flat_time / 2 - calc_duration(gx) / 2) / seq.grad_raster_time\n",
" )\n",
" * seq.grad_raster_time\n",
")\n",
"delay_TR = (\n",
" math.ceil((TR - calc_duration(gx_pre) - calc_duration(gz) - calc_duration(gx) - delay_TE) / seq.grad_raster_time)\n",
" * seq.grad_raster_time\n",
")\n",
"\n",
"assert np.all(delay_TR >= calc_duration(gx_spoil, gz_spoil))"
]
Expand All @@ -128,7 +148,7 @@
"rf_phase = 0\n",
"rf_inc = 0\n",
"\n",
"for i in range(Ny): # We have Ny phase encodes\n",
"for i in range(Ny): # We have Ny phase encodes\n",
" rf.phase_offset = rf_phase / 180 * np.pi\n",
" adc.phase_offset = rf_phase / 180 * np.pi\n",
" rf_inc = divmod(rf_inc + rf_spoiling_inc, 360.0)[1]\n",
Expand All @@ -148,7 +168,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"5. Visualise the constructed pulse sequence by calling the `plot()` command. The `plot()` command visualises the ADC and RF events in one window, and the gradient events in another window. For GRE, the `plot()` command should display two plots that look like this:\n",
"5. Visualize the constructed pulse sequence by calling the `plot()` command. The `plot()` command visualizes the ADC and RF events in one window, and the gradient events in another window. For GRE, the `plot()` command should display two plots that look like this:\n",
"\n",
"![GRE Plot 1](./gre_1.png)\n",
"![GRE Plot 2](./gre_2.png)"
Expand Down
75 changes: 74 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ dependencies = [

[project.optional-dependencies]
sigpy = ["sigpy>=0.1.26"]
test = ["pytest"]
test = ["pytest", "pre-commit"]

[project.urls]
Homepage = "https://github.com/imr-framework/pypulseq"
Expand All @@ -41,6 +41,79 @@ SAR = ["QGlobal.mat"]
[tool.setuptools.dynamic]
version = { attr = "version.__version__" }

[tool.ruff]
line-length = 120
extend-exclude = ["__init__.py"]
exclude = ["doc/**", "pypulseq/seq_examples/**"]

# RUFF section
[tool.ruff.lint]
select = [
"A", # flake8-builtins
"ARG", # flake8-unused-arguments
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"COM", # flake8-commas
# "D", # pydocstyle
# "E", # pycodestyle errors
"F", # Pyflakes
# "FA", # flake8-future-annotations
"I", # isort
# "N", # pep8-naming
"NPY", # NumPy-specific rules
"RUF", # Ruff-specific rules
"S", # flake8-bandit
"SIM", # flake8-simplify
# "UP", # pyupgrade
"PIE", # flake8-pie
"PTH", # flake8-use-pathlib
"Q", # flake8-quotes
"W", # pycodestyle warnings
"YTT", # flake8-2020
# "ERA", # flake8-eradicate
]

extend-select = [
# "ANN001", # type annotation for function argument
# # "ANN201", # return type annonation public function
# # "ANN205", # return type annonation static method
# # "ANN401", # any type annotation
# # "BLE001", # blind exception
# # "D107", # missing docstring in __init__
# # "D417", # undocumented-parameter
]

ignore = [
"B028", # explicit "stacklevel" arg in warnings
"COM812", # missing-trailing-comma (conflict with formatter)
"PTH123", # use of Path.open
"S101", # use of assert
"S307", # use of possibly insecure eval function
"S311", # standard pseudo-random generators
"S324", # insecure hash function
"SIM108", # if-else-block-instead-of-if-exp
"SIM115", # use of context manager
]

[tool.ruff.lint.isort]
force-single-line = false
split-on-trailing-comma = false

[tool.ruff.lint.flake8-quotes]
docstring-quotes = "double"
inline-quotes = "single"

[tool.ruff.lint.pydocstyle]
convention = "numpy"

[tool.ruff.format]
quote-style = "single"
skip-magic-trailing-comma = false

[tool.typos.default]
locale = "en-us"
exclude = ["pypulseq/seq_examples/**"]

# PyTest section
[tool.pytest.ini_options]
testpaths = ["pypulseq/tests"]
Expand Down
Loading
Loading