diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bd8b22a9..353726e6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,12 +3,12 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }} cancel-in-progress: true -on: +on: # yamllint disable-line rule:truthy push: branches: - main tags: - - '*' + - '*' pull_request: branches: - main diff --git a/.github/workflows/outdated.yml b/.github/workflows/outdated.yml index 5dbe114e..12163520 100644 --- a/.github/workflows/outdated.yml +++ b/.github/workflows/outdated.yml @@ -3,7 +3,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }} cancel-in-progress: true -on: +on: # yamllint disable-line rule:truthy # We only do this on PRs to avoid the (admittedly unlikely) scenario that # we run, green, wait, merge, then the build on `main` could fail because conda # has updated during the "green" and then the "build on `main`" steps @@ -14,9 +14,9 @@ jobs: deps: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - run: pip install packaging requests pyyaml - - run: python tests/test_outdated.py + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + - run: pip install packaging requests pyyaml + - run: python tests/test_outdated.py diff --git a/.github/workflows/purge_artifacts.yml b/.github/workflows/purge_artifacts.yml index 1fecd4cf..b1d4f87e 100644 --- a/.github/workflows/purge_artifacts.yml +++ b/.github/workflows/purge_artifacts.yml @@ -1,6 +1,6 @@ name: 'Remove all artifacts' -on: +on: # yamllint disable-line rule:truthy schedule: - cron: '0 3 * * *' # every night at 3 am UTC diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..87cc0a83 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,15 @@ +repos: + - repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black + args: [--quiet] + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.284 + hooks: + - id: ruff + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.32.0 + hooks: + - id: yamllint + args: [--strict, -c, .yamllint.yml] diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 00000000..64a7e7fc --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,9 @@ +extends: default + +ignore: | + recipes/mne-python_0.23/mne-python_0.23.0_env.yml + +rules: + line-length: disable + document-start: disable + key-duplicates: disable diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..23b1fd91 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,13 @@ +[tool.ruff] +select = ["E", "F", "W", "D"] +ignore = [ + "D100", # Missing docstring in public module + "D104", # Missing docstring in public package +] + +[tool.ruff.pydocstyle] +convention = "numpy" + +[tool.ruff.per-file-ignores] +"tests/test_imports.py" = ["E402", "F401"] +"tests/test_gui.py" = ["E402"] diff --git a/recipes/mne-python_0.23/construct.yaml b/recipes/mne-python_0.23/construct.yaml index 1d004be8..4333cbf0 100644 --- a/recipes/mne-python_0.23/construct.yaml +++ b/recipes/mne-python_0.23/construct.yaml @@ -5,8 +5,8 @@ company: MNE-Python Developers environment_file: mne-python_0.23.0_env.yml license_file: ../../LICENSE -initialize_by_default: False -register_python_default: False +initialize_by_default: false +register_python_default: false default_prefix: ${HOME}/mne-python/0.23.0 # [unix] default_prefix: ${USERPROFILE}\mne-python\0.23.0 # [win] uninstall_name: MNE-Python 0.23.0 (${VERSION}, Python ${PYVERSION}) @@ -17,9 +17,9 @@ installer_filename: MNE-Python-0.23.0_installer-2021.1-Windows.exe # [win] installer_filename: MNE-Python-0.23.0_installer-2021.1-Linux.sh # [linux] channels: -- conda-forge + - conda-forge conda_default_channels: -- conda-forge + - conda-forge -write_condarc: True +write_condarc: true diff --git a/recipes/mne-python_0.24/construct.yaml b/recipes/mne-python_0.24/construct.yaml index db02336f..15dc5f42 100644 --- a/recipes/mne-python_0.24/construct.yaml +++ b/recipes/mne-python_0.24/construct.yaml @@ -15,8 +15,8 @@ welcome_file: ../../assets/welcome.rtf readme_text: "" conclusion_file: ../../assets/conclusion.rtf -initialize_by_default: False -register_python_default: False +initialize_by_default: false +register_python_default: false # default_prefix will be ignored by macOS .pkg installer! default_prefix: ${HOME}/mne-python/0.24.1_2 # [unix] @@ -83,5 +83,5 @@ condarc: # default_channels: # - conda-forge channel_priority: strict - allow_other_channels: False + allow_other_channels: false env_prompt: (mne-0.24.1_2) diff --git a/recipes/mne-python_1.0/construct.yaml b/recipes/mne-python_1.0/construct.yaml index 4c3d3e79..b4b528bb 100644 --- a/recipes/mne-python_1.0/construct.yaml +++ b/recipes/mne-python_1.0/construct.yaml @@ -14,8 +14,8 @@ welcome_file: ../../assets/welcome.rtf readme_text: "" conclusion_file: ../../assets/conclusion.rtf -initialize_by_default: False -register_python_default: False +initialize_by_default: false +register_python_default: false # default_prefix will be ignored by macOS .pkg installer! default_prefix: ${HOME}/mne-python/1.0.3_1 # [linux] @@ -128,5 +128,5 @@ condarc: channels: - conda-forge channel_priority: strict - allow_other_channels: False + allow_other_channels: false env_prompt: "(mne-1.0.3_1) " diff --git a/recipes/mne-python_1.1/construct.yaml b/recipes/mne-python_1.1/construct.yaml index b6e35706..df602b03 100644 --- a/recipes/mne-python_1.1/construct.yaml +++ b/recipes/mne-python_1.1/construct.yaml @@ -14,8 +14,8 @@ welcome_file: ../../assets/welcome.rtf readme_text: "" conclusion_file: ../../assets/conclusion.rtf -initialize_by_default: False -register_python_default: False +initialize_by_default: false +register_python_default: false # default_prefix will be ignored by macOS .pkg installer! default_prefix: ${HOME}/mne-python/1.1.1_0 # [linux] @@ -131,5 +131,5 @@ condarc: channels: - conda-forge channel_priority: strict - allow_other_channels: False + allow_other_channels: false env_prompt: "(mne-1.1.1_0) " diff --git a/recipes/mne-python_1.2/construct.yaml b/recipes/mne-python_1.2/construct.yaml index f2ffe7d7..51aca717 100644 --- a/recipes/mne-python_1.2/construct.yaml +++ b/recipes/mne-python_1.2/construct.yaml @@ -14,8 +14,8 @@ welcome_file: ../../assets/welcome.rtf readme_text: "" conclusion_file: ../../assets/conclusion.rtf -initialize_by_default: False -register_python_default: False +initialize_by_default: false +register_python_default: false # default_prefix will be ignored by macOS .pkg installer! default_prefix: ${HOME}/mne-python/1.2.3_0 # [linux] @@ -136,5 +136,5 @@ condarc: channels: - conda-forge channel_priority: strict - allow_other_channels: False + allow_other_channels: false env_prompt: "(mne-1.2.3_0) " diff --git a/recipes/mne-python_1.3/construct.yaml b/recipes/mne-python_1.3/construct.yaml index fa78bd7f..cd3b0aad 100644 --- a/recipes/mne-python_1.3/construct.yaml +++ b/recipes/mne-python_1.3/construct.yaml @@ -14,8 +14,8 @@ welcome_file: ../../assets/welcome.rtf readme_text: "" conclusion_file: ../../assets/conclusion.rtf -initialize_by_default: False -register_python_default: False +initialize_by_default: false +register_python_default: false # default_prefix will be ignored by macOS .pkg installer! default_prefix: ${HOME}/mne-python/1.3.1_0 # [linux] @@ -140,5 +140,5 @@ condarc: # - pytorch # [win] - conda-forge channel_priority: strict - allow_other_channels: False + allow_other_channels: false env_prompt: "(mne-1.3.1_0) " diff --git a/recipes/mne-python_1.4/construct.yaml b/recipes/mne-python_1.4/construct.yaml index 9eabbb9d..f18c7679 100644 --- a/recipes/mne-python_1.4/construct.yaml +++ b/recipes/mne-python_1.4/construct.yaml @@ -14,8 +14,8 @@ welcome_file: ../../assets/welcome.rtf readme_text: "" conclusion_file: ../../assets/conclusion.rtf -initialize_by_default: False -register_python_default: False +initialize_by_default: false +register_python_default: false # default_prefix will be ignored by macOS .pkg installer! default_prefix: ${HOME}/mne-python/1.4.2_0 # [linux] @@ -145,5 +145,5 @@ condarc: # - pytorch # [win] - conda-forge channel_priority: strict - allow_other_channels: False + allow_other_channels: false env_prompt: "(mne-1.4.2_0) " diff --git a/recipes/mne-python_1.5/construct.yaml b/recipes/mne-python_1.5/construct.yaml index ce5909eb..6008df1a 100644 --- a/recipes/mne-python_1.5/construct.yaml +++ b/recipes/mne-python_1.5/construct.yaml @@ -15,8 +15,8 @@ welcome_file: ../../assets/welcome.rtf readme_text: "" conclusion_file: ../../assets/conclusion.rtf -initialize_by_default: False -register_python_default: False +initialize_by_default: false +register_python_default: false default_prefix: ${HOME}/mne-python/1.5.0_0 # [linux] default_prefix: "%USERPROFILE%\\mne-python\\1.5.0_0" # [win] @@ -62,11 +62,11 @@ specs: # - mne-bids =0.11dev0=*_20221007 # TODO: ⛔️ ⛔️ ⛔️ DEV BUILDS STOP: CHANGE BEFORE RELEASE! ⛔️ ⛔️ ⛔️ - - mne =1.4.2=*_0 - - mne-installer-menus =1.4.2=*_0 + - mne =1.5.0=*_0 + - mne-installer-menus =1.5.0=*_0 - mne-bids =0.12.0 - mne-bids-pipeline =1.4.0 - - mne-qt-browser =0.5.1 + - mne-qt-browser =0.5.2 - mne-connectivity =0.5.0 - mne-faster =1.1.0 - mne-nirs =0.5.0 @@ -89,8 +89,8 @@ specs: - mamba =1.4.9 - openblas =0.3.23 - jupyter =1.0.0 - - jupyterlab =4.0.3 - - ipykernel =6.24.0 + - jupyterlab =4.0.5 + - ipykernel =6.25.1 - nb_conda_kernels =2.3.1 - spyder-kernels =2.4.4 - spyder =5.4.4 @@ -128,22 +128,22 @@ specs: # GitHub client, https://cli.github.com - gh =2.32.1 # NeuroSpin needs the following - - questionary =1.10.0 + - questionary =2.0.0 - pqdm =0.2.0 # Viz - matplotlib =3.7.2 - ipympl =0.9.3 - seaborn =0.12.2 - - plotly =5.15.0 + - plotly =5.16.0 - vtk =9.2.6 - git =2.41.0 # [win] - make =4.3 # [win] - - ipywidgets =8.0.7 + - ipywidgets =8.1.0 condarc: channels: # - pytorch # [win] - conda-forge channel_priority: strict - allow_other_channels: False + allow_other_channels: false env_prompt: "(mne-1.5.0_0) " diff --git a/tests/test_gui.py b/tests/test_gui.py index 94308223..b0d42735 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -1,4 +1,4 @@ -print('Running imports') +print("Running imports") import faulthandler faulthandler.enable() @@ -8,7 +8,6 @@ from pathlib import Path import numpy as np -import matplotlib import matplotlib.pyplot as plt import pyvista @@ -17,17 +16,17 @@ this_path = Path(__file__).parent # Matplotlib -print('Running matplotlib tests') +print("Running matplotlib tests") fig, _ = plt.subplots() -want = 'QTAgg' +want = "QTAgg" assert want in repr(fig.canvas), repr(fig.canvas) -plt.close('all') +plt.close("all") # pyvistaqt -print('Running pyvistaqt tests (except Windows)') -if not sys.platform.startswith('win'): - fname = this_path / 'test.png' - mne.viz.set_3d_backend('pyvista') +print("Running pyvistaqt tests (except Windows)") +if not sys.platform.startswith("win"): + fname = this_path / "test.png" + mne.viz.set_3d_backend("pyvista") fig = mne.viz.create_3d_figure((400, 400), scene=False, show=True) fig._process_events() fig._process_events() @@ -42,21 +41,22 @@ assert fname.is_file() os.remove(fname) mne.viz.close_3d_figure(fig) - assert 'BackgroundPlotter' in repr(plotter), repr(plotter) + assert "BackgroundPlotter" in repr(plotter), repr(plotter) # mne-qt-browser -print('Running mne-qt-browser tests') -mne.viz.set_browser_backend('qt') -raw = mne.io.RawArray(np.zeros((1, 1000)), mne.create_info(1, 1000., 'eeg')) +print("Running mne-qt-browser tests") +mne.viz.set_browser_backend("qt") +raw = mne.io.RawArray(np.zeros((1, 1000)), mne.create_info(1, 1000.0, "eeg")) fig = raw.plot() fig.close() -assert 'MNEQtBrowser' in repr(fig), repr(fig) +assert "MNEQtBrowser" in repr(fig), repr(fig) # mne-kit-gui -print('Running mne-kit-gui tests') +print("Running mne-kit-gui tests") from pyface.api import GUI # noqa import mne_kit_gui # noqa -os.environ['_MNE_GUI_TESTING_MODE'] = 'true' + +os.environ["_MNE_GUI_TESTING_MODE"] = "true" gui = GUI() gui.process_events() ui, frame = mne_kit_gui.kit2fiff() diff --git a/tests/test_imports.py b/tests/test_imports.py index 7df766b1..ec031742 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -2,27 +2,35 @@ def check_min_version(package, min_version): + """Check a minimum version.""" from packaging.version import parse - assert parse(package.__version__) >= parse(min_version), f'{package}: got {package.__version__} wanted {min_version}' + + assert parse(package.__version__) >= parse( + min_version + ), f"{package}: got {package.__version__} wanted {min_version}" import mne -check_min_version(mne, '1.4') + +check_min_version(mne, "1.4") import mne_bids import mne_bids_pipeline -check_min_version(mne_bids_pipeline, '1.2') + +check_min_version(mne_bids_pipeline, "1.2") import mne_connectivity import mne_faster import mne_nirs import mne_qt_browser -check_min_version(mne_qt_browser, '0.5.0') + +check_min_version(mne_qt_browser, "0.5.0") import mne_realtime import mne_features import mne_rsa import mne_microstates import mne_ari import mne_kit_gui -if platform.system() != 'Windows': + +if platform.system() != "Windows": import mne_icalabel import autoreject import pyprep diff --git a/tests/test_json_versions.py b/tests/test_json_versions.py index 4a87e711..afb1de69 100644 --- a/tests/test_json_versions.py +++ b/tests/test_json_versions.py @@ -9,52 +9,48 @@ dir_ = pathlib.Path(__file__).parent.parent sys_name = dict( - linux='Linux', - darwin='macOS_Intel', - win32='Windows', + linux="Linux", + darwin="macOS_Intel", + win32="Windows", )[sys.platform] -sys_ext = dict( - linux='.sh', - darwin='.pkg', - win32='.exe' -)[sys.platform] -ver = (dir_ / 'assets' / 'current_version.txt').read_text().strip() -with open(dir_ / 'recipes' / f'mne-python_{ver}' / 'construct.yaml', - encoding='utf-8') as fid: +sys_ext = dict(linux=".sh", darwin=".pkg", win32=".exe")[sys.platform] +ver = (dir_ / "assets" / "current_version.txt").read_text().strip() +with open( + dir_ / "recipes" / f"mne-python_{ver}" / "construct.yaml", encoding="utf-8" +) as fid: params = yaml.safe_load(fid) -installer_version = params['version'] -specs = params['specs'] +installer_version = params["version"] +specs = params["specs"] del params # Extract versions from construct.yaml -mne_package_names = ('mne', 'mne-installer-menus') # the most important ones! +mne_package_names = ("mne", "mne-installer-menus") # the most important ones! want_versions = {} for spec in specs: - if not ' ' in spec: # only include those where we specify a version + if " " not in spec: # only include those where we specify a version continue - package_name, package_version_and_build = spec.split(' ') + package_name, package_version_and_build = spec.split(" ") if package_name in mne_package_names: - package_version = package_version_and_build.split('=')[1] - package_build = (package_version_and_build - .split('=')[-1] - .replace('*', '') - .replace('_', '')) + package_version = package_version_and_build.split("=")[1] + package_build = ( + package_version_and_build.split("=")[-1].replace("*", "").replace("_", "") + ) want_versions[package_name] = { - 'version': package_version, - 'build_number': package_build, + "version": package_version, + "build_number": package_build, } # Extract versions from created environment -fname = dir_ / f'MNE-Python-{installer_version}-{sys_name}{sys_ext}.env.json' +fname = dir_ / f"MNE-Python-{installer_version}-{sys_name}{sys_ext}.env.json" assert fname.is_file(), (fname, os.listdir(os.getcwd())) -env_json = json.loads(fname.read_text(encoding='utf-8')) +env_json = json.loads(fname.read_text(encoding="utf-8")) got_versions = dict() for package in env_json: - if package['name'] in mne_package_names: - got_versions[package['name']] = { - 'version': str(package['version']), - 'build_number': str(package['build_number']), + if package["name"] in mne_package_names: + got_versions[package["name"]] = { + "version": str(package["version"]), + "build_number": str(package["build_number"]), } assert len(got_versions) == 2, got_versions @@ -63,5 +59,5 @@ got = got_versions[package_name] want = want_versions[package_name] - msg = f'{package_name}: got {repr(got)} != want {repr(want)}' + msg = f"{package_name}: got {repr(got)} != want {repr(want)}" assert got == want, msg diff --git a/tests/test_notebook.py b/tests/test_notebook.py index 8ddabe5a..8c198d48 100644 --- a/tests/test_notebook.py +++ b/tests/test_notebook.py @@ -7,7 +7,8 @@ import ipyvtklink # noqa km = AsyncKernelManager(config=None) -nb = nbformat.reads(""" +nb = nbformat.reads( + """ { "cells": [ { @@ -33,7 +34,9 @@ }, "nbformat": 4, "nbformat_minor": 4 -}""", as_version=4) +}""", + as_version=4, +) _nbclient = NotebookClient(nb, km=km) _nbclient.reset_execution_trackers() code = """\ @@ -50,7 +53,7 @@ """ with _nbclient.setup_kernel(): assert _nbclient.kc is not None - cell = Bunch(cell_type='code', metadata={}, source=code) + cell = Bunch(cell_type="code", metadata={}, source=code) _nbclient.execute_cell(cell, 0, execution_count=0) _nbclient.set_widgets_metadata() _nbclient._cleanup_kernel() diff --git a/tests/test_outdated.py b/tests/test_outdated.py index e9b8ad35..de710a58 100644 --- a/tests/test_outdated.py +++ b/tests/test_outdated.py @@ -7,53 +7,43 @@ import packaging.version import requests -recipes_dir = Path(__file__).parents[1] / 'recipes' -recipies = sorted([ - str(recipe) for recipe in recipes_dir.iterdir() - if recipe.is_dir() -]) +recipes_dir = Path(__file__).parents[1] / "recipes" +recipies = sorted([str(recipe) for recipe in recipes_dir.iterdir() if recipe.is_dir()]) latest_recipe_dir = Path(recipies[-1]) -construct_yaml_path = latest_recipe_dir / 'construct.yaml' +construct_yaml_path = latest_recipe_dir / "construct.yaml" -construct_yaml = yaml.safe_load( - construct_yaml_path.read_text(encoding='utf-8') -) -specs = construct_yaml['specs'] +construct_yaml = yaml.safe_load(construct_yaml_path.read_text(encoding="utf-8")) +specs = construct_yaml["specs"] + +print(f"Analyzing spec file: {construct_yaml_path}\n") -print(f'Analyzing spec file: {construct_yaml_path}\n') @dataclass() -class Package: +class Package: # noqa: D101 name: str version_spec: str | None version_conda_forge: str | None = None allowed_outdated: set[str] = { + "conda", # As of 2023/08/15, v 23.7.2 creates an issue when installing (with mamba) } packages: list[Package] = [] for spec in specs: - if ' ' in spec: - assert spec.count(' ') == 1, f'Wrong number of spaces in spec: {spec}' - name, version = spec.split(' ') - version = ( - version - .lstrip('~') - .lstrip('=') - .split('=') # build number - )[0] - if version == '!': # this is "a !=something", we can skip it + if " " in spec: + assert spec.count(" ") == 1, f"Wrong number of spaces in spec: {spec}" + name, version = spec.split(" ") + version = (version.lstrip("~").lstrip("=").split("="))[0] # build number + if version == "!": # this is "a !=something", we can skip it version = None - elif version.startswith(('<', '>')): # "a =something" + elif version.startswith(("<", ">")): # "a =something" version = None else: name = spec version = None - packages.append( - Package(name=name, version_spec=version) - ) + packages.append(Package(name=name, version_spec=version)) del name, version outdated = [] @@ -62,49 +52,50 @@ class Package: if package.version_spec is None: continue - anaconda_url = f'https://api.anaconda.org/package/conda-forge/{package.name}' - r = requests.get(anaconda_url) + anaconda_url = f"https://api.anaconda.org/package/conda-forge/{package.name}" + r = requests.get(anaconda_url) if r.status_code == 404: - print(f'{package.name} not found on conda-forge') + print(f"{package.name} not found on conda-forge") not_found.append(package) continue json = r.json() - version = json['latest_version'] + version = json["latest_version"] package.version_conda_forge = version del json, version - comp = { - - } - if ( - packaging.version.parse(package.version_spec) < - packaging.version.parse(package.version_conda_forge) + comp = {} + if packaging.version.parse(package.version_spec) < packaging.version.parse( + package.version_conda_forge ): - mismatch = f'{package.version_spec} < {package.version_conda_forge}' + mismatch = f"{package.version_spec} < {package.version_conda_forge}" if package.name in allowed_outdated: - print(f' {package.name.ljust(20)} ✓ allowed {mismatch}') + print(f" {package.name.ljust(20)} ✓ allowed {mismatch}") else: - print(f'* {package.name.ljust(20)} ✗ OUTDATED {mismatch}') + print(f"* {package.name.ljust(20)} ✗ OUTDATED {mismatch}") outdated.append(package) else: - print(f' {package.name.ljust(20)} ✓') + print(f" {package.name.ljust(20)} ✓") exit_code = 0 if not_found: - print(f'\n{len(not_found)} packages not found on conda-forge:\n') - print('\n'.join(f' * {package.name}' for package in not_found)) + print(f"\n{len(not_found)} packages not found on conda-forge:\n") + print("\n".join(f" * {package.name}" for package in not_found)) exit_code = 1 if outdated: - print(f'\n{len(outdated)} packages outdated:\n') - print('\n'.join([ - f' * {package.name} ' - f'({package.version_spec} < {package.version_conda_forge})' - for package in outdated - ])) + print(f"\n{len(outdated)} packages outdated:\n") + print( + "\n".join( + [ + f" * {package.name} " + f"({package.version_spec} < {package.version_conda_forge})" + for package in outdated + ] + ) + ) exit_code = 1 else: - print('\nEverything is up to date.') -if __name__ == '__main__': + print("\nEverything is up to date.") +if __name__ == "__main__": sys.exit(exit_code)