diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 2c72f2e..0c2adc8 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -8,7 +8,7 @@ permissions: jobs: autobot: runs-on: ubuntu-latest - if: (github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.user.login == 'pre-commit-ci[bot]') && github.repository == 'mne-tools/mne-installers' + if: (github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.user.login == 'pre-commit-ci[bot]') && github.repository == 'scientific-python/installer' steps: - name: Enable auto-merge for bot PRs run: gh pr merge --auto --squash "$PR_URL" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0b5ca7b..b84c311 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,6 +50,7 @@ jobs: # Replace a set of _name variables (that have # [osx] comments with # an particular string (is this the certificate ID?) sed -i "" "s/_name: *# \[osx\]/_name: 9779L28NP8 # \[osx\]/" ${RECIPE_DIR}/construct.yaml + - run: conda-build sp-installer-menu --no-anaconda-upload --croot conda-bld - run: ./tools/run_constructor.sh timeout-minutes: 20 - run: ./tools/macos_check_installer_signature.sh @@ -61,13 +62,13 @@ jobs: APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - run: ./tools/calculate_installer_hash.sh - run: | - installer -verbose -pkginfo -pkg ./${MNE_INSTALLER_NAME} - installer -verbose -dominfo -pkg ./${MNE_INSTALLER_NAME} - installer -verbose -volinfo -pkg ./${MNE_INSTALLER_NAME} + installer -verbose -pkginfo -pkg ./${SP_INSTALLER_NAME} + installer -verbose -dominfo -pkg ./${SP_INSTALLER_NAME} + installer -verbose -volinfo -pkg ./${SP_INSTALLER_NAME} - uses: actions/upload-artifact@v4 with: - name: ${{ env.MNE_INSTALLER_ARTIFACT_ID }} - path: MNE-Python-*.* + name: ${{ env.SP_INSTALLER_ARTIFACT_ID }} + path: Scientific-Python-*.* retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }} build_linux: @@ -86,13 +87,14 @@ jobs: with: environment-file: environment.yml - run: ./tools/extract_version.sh + - run: conda-build sp-installer-menu --no-anaconda-upload --croot conda-bld - run: ./tools/run_constructor.sh timeout-minutes: 20 - run: ./tools/calculate_installer_hash.sh - uses: actions/upload-artifact@v4 with: - name: ${{ env.MNE_INSTALLER_ARTIFACT_ID }} - path: MNE-Python-*.* + name: ${{ env.SP_INSTALLER_ARTIFACT_ID }} + path: Scientific-Python-*.* retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }} build_windows: @@ -101,7 +103,7 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-2019] + os: [windows-2022] defaults: run: shell: bash -el {0} @@ -113,13 +115,14 @@ jobs: # https://nsis.sourceforge.io/Download - run: conda install -c conda-forge -y "nsis==3.08" - run: ./tools/extract_version.sh + - run: conda-build sp-installer-menu --no-test --no-anaconda-upload --croot conda-bld - run: ./tools/run_constructor.sh timeout-minutes: 20 - run: ./tools/calculate_installer_hash.sh - uses: actions/upload-artifact@v4 with: - name: ${{ env.MNE_INSTALLER_ARTIFACT_ID }} - path: MNE-Python-*.* + name: ${{ env.SP_INSTALLER_ARTIFACT_ID }} + path: Scientific-Python-*.* retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }} # Test @@ -145,17 +148,17 @@ jobs: - run: ./tools/extract_version.sh - uses: actions/download-artifact@v4 with: - name: ${{ env.MNE_INSTALLER_ARTIFACT_ID }} + name: ${{ env.SP_INSTALLER_ARTIFACT_ID }} - name: Run installer run: | - sudo installer -verbose -pkg ${MNE_INSTALLER_NAME} -target / \ + sudo installer -verbose -pkg ${SP_INSTALLER_NAME} -target / \ || ( tail -n 30 /var/log/install.log && exit 1 ) # display last log messages on error # Check the list of packages that were actually installed by the installer. - run: ./tools/export_frozen_env_def.sh - uses: actions/upload-artifact@v4 with: - name: ${{ env.MNE_INSTALLER_ARTIFACT_ID }}-json - path: MNE-Python-*.env.json + name: ${{ env.SP_INSTALLER_ARTIFACT_ID }}-json + path: Scientific-Python-*.env.json # upload just one for each installer version if: matrix.os == 'macos-13' || matrix.os == 'macos-14' - uses: pyvista/setup-headless-display-action@main @@ -183,13 +186,13 @@ jobs: - run: ./tools/extract_version.sh - uses: actions/download-artifact@v4 with: - name: ${{ env.MNE_INSTALLER_ARTIFACT_ID }} - - run: sh ./${MNE_INSTALLER_NAME} -b + name: ${{ env.SP_INSTALLER_ARTIFACT_ID }} + - run: sh ./${SP_INSTALLER_NAME} -b - run: ./tools/export_frozen_env_def.sh - uses: actions/upload-artifact@v4 with: - name: ${{ env.MNE_INSTALLER_ARTIFACT_ID }}-json - path: MNE-Python-*.env.json + name: ${{ env.SP_INSTALLER_ARTIFACT_ID }}-json + path: Scientific-Python-*.env.json # upload just one for each installer version if: matrix.os == 'ubuntu-24.04' - uses: pyvista/setup-headless-display-action@main @@ -205,7 +208,7 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-2019, windows-2022] + os: [windows-2022, windows-2025] runs-on: ${{ matrix.os }} defaults: run: @@ -215,16 +218,16 @@ jobs: - run: ./tools/extract_version.sh - uses: actions/download-artifact@v4 with: - name: ${{ env.MNE_INSTALLER_ARTIFACT_ID }} + name: ${{ env.SP_INSTALLER_ARTIFACT_ID }} # https://docs.anaconda.com/anaconda/install/silent-mode.html#windows - - run: .\%MNE_INSTALLER_NAME% /S /InstallationType=JustMe /AddToPath=1 + - run: .\%SP_INSTALLER_NAME% /S /InstallationType=JustMe /AddToPath=1 timeout-minutes: 30 shell: cmd - run: ./tools/export_frozen_env_def.sh - uses: actions/upload-artifact@v4 with: - name: ${{ env.MNE_INSTALLER_ARTIFACT_ID }}-json - path: MNE-Python-*.env.json + name: ${{ env.SP_INSTALLER_ARTIFACT_ID }}-json + path: Scientific-Python-*.env.json # upload just one for each installer version if: matrix.os == 'windows-2022' - uses: pyvista/setup-headless-display-action@main @@ -244,15 +247,15 @@ jobs: run: shell: bash -el {0} steps: - # These names should correspond to MNE_INSTALLER_ARTIFACT_ID in tools/extract_version.sh + # These names should correspond to SP_INSTALLER_ARTIFACT_ID in tools/extract_version.sh - uses: actions/download-artifact@v4 with: - pattern: MNE-Python-* + pattern: Scientific-Python-* merge-multiple: true - run: ls -al ./ - uses: ncipollo/release-action@v1 with: - artifacts: "MNE-Python-*.*" + artifacts: "Scientific-Python-*.*" token: ${{ secrets.GITHUB_TOKEN }} draft: true prerelease: true diff --git a/.gitignore b/.gitignore index 7fb70c4..a257efb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ tests/.joblib_cache .pytest_cache .DS_Store __pycache__ +conda-bld/ +.ipynb_checkpoints/ diff --git a/LICENSE b/LICENSE index c5b42b4..bb4094d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ BSD 3-Clause License -Copyright (c) 2021, MNE-Python Developers +Copyright (c) 2021, MNE-Python Developers and +Copyright (c) 2025, Scientific-Python Developers All rights reserved. Redistribution and use in source and binary forms, with or without @@ -13,7 +14,7 @@ modification, are permitted provided that the following conditions are met: this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its +3. Neither the name of the copyright holders nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d8f71a6 --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +# Makefile targets for local build steps on Mac. +SHELL := bash +MENU_PKG_NAME=sp-installer-menu +ROOT_PREFIX=$(shell conda config --show root_prefix | cut -d ' ' -f 2) +ENV_PKGS=$(ROOT_PREFIX)/pkgs + +UNAME := "$(shell uname -s)" +ifeq ($(findstring Linux,$(UNAME)),Linux) + MACHINE=Linux +else ifeq ($(findstring Darwin,$(UNAME)),Darwin) + MACHINE=macOS +else ifeq ($(findstring MINGW64_NT,$(UNAME)),MINGW64_NT) + MACHINE=Windows +else ifeq ($(FINDSTRING MSYS_NT,$(UNAME)),MSYS_NT) + MACHINE=Windows +else + MACHINE="UNKNOWN:$(UNAME)" +endif + +all: menu-pkg installer + +menu-pkg: + conda-build $(MENU_PKG_NAME) --no-anaconda-upload --croot conda-bld + +installer: + constructor recipes/scientific-python + +install: + @if [[ $(MACHINE) == "macOS" ]]; then \ + installer -pkg Scientific-Python-*.pkg -target CurrentUserHomeDirectory; \ + elif [[ $(MACHINE) == "Linux" ]]; then \ + sh ./Scientific-Python-*-Linux.sh; \ + elif [[ $(MACHINE) == "Windows" ]]; then \ + echo "TODO add install command for Windows"; \ + fi + +clean: + @rm -rf conda-bld + @rm -rf ~/.conda/constructor/*/$(MENU_PKG_NAME)* + @if [[ $(MACHINE) == "macOS" ]]; then \ + rm -rf ~/Applications/*Scientific*; \ + rm -f Scientific-Python-*.pkg; \ + elif [[ $(MACHINE) == "Linux" ]]; then \ + rm -rf $(HOME)/Scientific-Python; \ + rm -f ./Scientific-Python-*.sh; \ + rm -f $(HOME)/.local/share/applications/scientific-python-*.desktop; \ + elif [[ $(MACHINE) == "Windows" ]]; then \ + echo "TODO add command to cleanup icons on Windows"; \ + fi diff --git a/README.md b/README.md index 94abc5f..66e0653 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Installers for MNE-Python +# Installers for core Scientific Python packages -Installers for MNE-Python for macOS, Windows, and Linux. +Installers for the core Scientific Python packages for macOS, Windows, and Linux. -Installer running on macOS + -Please visit [the installers section of the MNE documentation](https://mne.tools/dev/install/installers.html) for instructions on how to use them. +Please visit [the release page](https://github.com/scientific-python/installer/releases/latest) to download an installer for your OS. ## Development diff --git a/assets/conclusion.rtf b/assets/conclusion.rtf index f7166bb..bbce55e 100644 --- a/assets/conclusion.rtf +++ b/assets/conclusion.rtf @@ -7,17 +7,17 @@ \deftab720 \pard\pardeftab720\partightenfactor0 -\f0\b\fs40 \cf2 \up0 \nosupersub \ulnone MNE-Python has been successfully installed!\ +\f0\b\fs40 \cf2 \up0 \nosupersub \ulnone The Scientific Python environment has been successfully installed!\ \pard\pardeftab720\partightenfactor0 \f1\b0\fs22 \cf2 \ \ \pard\pardeftab720\partightenfactor0 -\f2\fs32 \cf2 \uc0\u55357 \u56514 -\f1 You can find MNE-Python in your Applications folder.\ +\f2\fs32 \cf2 \uc0\u55357 \u56514 +\f1 You can find Scientific Python in your Applications folder.\ \ -\f2 \uc0\u55358 \u56599 -\f1 Thank you for choosing MNE-Python!\ +\f2 \uc0\u55358 \u56599 +\f1 Thank you for choosing Scientific Python!\ } \ No newline at end of file diff --git a/assets/header.png b/assets/header.png index 1d63208..2b36e3a 100644 Binary files a/assets/header.png and b/assets/header.png differ diff --git a/assets/icon.png b/assets/icon.png index 5bea83f..9002fbd 100644 Binary files a/assets/icon.png and b/assets/icon.png differ diff --git a/assets/license.rtf b/assets/license.rtf index 0668be4..411e4ab 100644 --- a/assets/license.rtf +++ b/assets/license.rtf @@ -14,7 +14,7 @@ \pard\pardeftab720\partightenfactor0 \f1\b0 \cf2 \ -Copyright \'a9 MNE-Python Developers\ +Copyright \'a9 Scientific Python Developers\ All rights reserved.\ \ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\ diff --git a/assets/license.txt b/assets/license.txt index dda5574..628e7b8 100644 --- a/assets/license.txt +++ b/assets/license.txt @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) MNE-Python Developers +Copyright (c) Scientific Python Developers All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/assets/post_install_linux.sh b/assets/post_install_linux.sh index 35d6afe..15e4c15 100755 --- a/assets/post_install_linux.sh +++ b/assets/post_install_linux.sh @@ -13,5 +13,5 @@ ${PREFIX}/bin/conda env config vars set MAMBA_NO_BANNER=1 echo "ℹ️ Pinning BLAS implementation to OpenBLAS." echo "libblas=*=*openblas" >>${PREFIX}/conda-meta/pinned -echo "ℹ️ Running mne sys_info." -${PREFIX}/bin/conda run mne sys_info || true +echo "ℹ️ Running spi_sys_info." +${PREFIX}/bin/conda run -p ${PREFIX} ${PREFIX}/Menu/spi_sys_info.py nohtml || true diff --git a/assets/post_install_macOS.sh b/assets/post_install_macOS.sh index 2d04f5b..464fa37 100755 --- a/assets/post_install_macOS.sh +++ b/assets/post_install_macOS.sh @@ -4,7 +4,7 @@ set -eo pipefail -logger -p 'install.info' "ℹ️ Running the custom MNE-Python post-install script." +logger -p 'install.info' "ℹ️ Running the custom Scientific Python post-install script." # This doesn't appear to be working: even when the installer is run through # sudo, SUDO_USER is unset. Leave it here for reference: @@ -14,11 +14,11 @@ logger -p 'install.info' "ℹ️ Running the custom MNE-Python post-install scri # ☠️ This is ugly and bound to break, but seems to do the job for now. ☠️ # Don't name the variable USER, as this one is already set. USER_FROM_HOMEDIR=`basename $HOME` -MNE_VERSION=`basename "$(dirname $PREFIX)"` +SPI_VERSION=`basename "$(dirname $PREFIX)"` logger -p 'install.info' "📓 USER_FROM_HOMEDIR=$USER_FROM_HOMEDIR" logger -p 'install.info' "📓 DSTROOT=$DSTROOT" logger -p 'install.info' "📓 PREFIX=$PREFIX" -logger -p 'install.info' "📓 MNE_VERSION=$MNE_VERSION" +logger -p 'install.info' "📓 SPI_VERSION=$SPI_VERSION" # Guess whether it's a system-wide or only-me install if [[ "$PREFIX" == "/Applications/"* ]]; then @@ -28,23 +28,22 @@ else APP_DIR="$HOME"/Applications PERMS="" fi -MNE_APP_DIR_ROOT="${APP_DIR}/MNE-Python" -MNE_APP_DIR="${MNE_APP_DIR_ROOT}/${MNE_VERSION}" -logger -p 'install.info' "📓 MNE_APP_DIR=$MNE_APP_DIR" +SPI_APP_DIR="${APP_DIR}/Scientific-Python" +logger -p 'install.info' "📓 SPI_APP_DIR=$SPI_APP_DIR" -logger -p 'install.info' "ℹ️ Moving root MNE .app bundles from $APP_DIR to $MNE_APP_DIR." -$PERMS mv "$APP_DIR"/*\(MNE\).app "$MNE_APP_DIR"/ +logger -p 'install.info' "ℹ️ Moving root SP .app bundles from $APP_DIR to $SPI_APP_DIR." +$PERMS mv "$APP_DIR"/Scientific\ Python\ *.app "$SPI_APP_DIR"/ -logger -p 'install.info' "ℹ️ Fixing permissions of MNE .app bundles in $MNE_APP_DIR: new owner will be ${USER_FROM_HOMEDIR}." -$PERMS chown -R "$USER_FROM_HOMEDIR" "$MNE_APP_DIR" +logger -p 'install.info' "ℹ️ Fixing permissions of SP .app bundles in $SPI_APP_DIR: new owner will be ${USER_FROM_HOMEDIR}." +$PERMS chown -R "$USER_FROM_HOMEDIR" "$SPI_APP_DIR" -MNE_ICON_PATH="$PREFIX/Menu/mne.png" -logger -p 'install.info' "ℹ️ Setting custom folder icon for $MNE_APP_DIR and $MNE_APP_DIR_ROOT to $MNE_ICON_PATH." -for destPath in "$MNE_APP_DIR" "$MNE_APP_DIR_ROOT"; do - logger -p 'install.info' "ℹ️ Setting custom folder icon for $destPath to $MNE_ICON_PATH." +SPI_ICON_PATH="${PREFIX}/Menu/spi_mac_folder_icon.png" +logger -p 'install.info' "ℹ️ Setting custom folder icon for $SPI_APP_DIR and $SPI_APP_DIR_ROOT to $SPI_ICON_PATH." +for destPath in "$SPI_APP_DIR" "$SPI_APP_DIR_ROOT"; do + logger -p 'install.info' "ℹ️ Setting custom folder icon for $destPath to $SPI_ICON_PATH." osascript \ -e 'set destPath to "'"${destPath}"'"' \ - -e 'set iconPath to "'"${MNE_ICON_PATH}"'"' \ + -e 'set iconPath to "'"${SPI_ICON_PATH}"'"' \ -e 'use framework "Foundation"' \ -e 'use framework "AppKit"' \ -e "set imageData to (current application's NSImage's alloc()'s initWithContentsOfFile:iconPath)" \ @@ -68,17 +67,14 @@ ${DSTBIN}/conda env config vars set PYTHONNOUSERSITE=1 logger -p 'install.info' "ℹ️ Disabling mamba package manager banner." ${DSTBIN}/conda env config vars set MAMBA_NO_BANNER=1 -logger -p 'install.info' "ℹ️ Configuring Matplotlib to use the Qt backend by default." -sed -i '.bak' "s/##backend: Agg/backend: qtagg/" ${PREFIX}/lib/python${PYSHORT}/site-packages/matplotlib/mpl-data/matplotlibrc - logger -p 'install.info' "ℹ️ Pinning BLAS implementation to OpenBLAS." echo "libblas=*=*openblas" >> ${PREFIX}/conda-meta/pinned logger -p 'install.info' "ℹ️ Fixing permissions of entire conda environment for user=${USER_FROM_HOMEDIR}." chown -R "$USER_FROM_HOMEDIR" "${PREFIX}" -logger -p 'install.info' "ℹ️ Running mne sys_info." -${DSTBIN}/conda run mne sys_info || true +logger -p 'install.info' "ℹ️ Running spi_sys_info." +${DSTBIN}/conda run -p ${PREFIX} ${PREFIX}/Menu/spi_sys_info.py nohtml || true -logger -p 'install.info' "ℹ️ Opening in Finder ${MNE_APP_DIR}/." -open -R "${MNE_APP_DIR}/" +logger -p 'install.info' "ℹ️ Opening in Finder ${SPI_APP_DIR}/." +open -R "${SPI_APP_DIR}/" diff --git a/assets/post_install_windows.bat b/assets/post_install_windows.bat index e4d3b7c..4924ff9 100755 --- a/assets/post_install_windows.bat +++ b/assets/post_install_windows.bat @@ -12,5 +12,5 @@ echo Disabling mamba package manager banner. echo Pinning BLAS implementation to OpenBLAS. echo libblas=*=*openblas >> "%PREFIX%\conda-meta\pinned" -echo Running mne sys_info. -"%PREFIX%\Scripts\conda" run mne sys_info || echo +echo Running spi_sys_info. +"%PREFIX%\Scripts\conda" run -p "%PREFIX%" "%PREFIX%\Menu\spi_sys_info.py" "nohtml" || echo diff --git a/assets/welcome.png b/assets/welcome.png index cf4966a..dc667e2 100644 Binary files a/assets/welcome.png and b/assets/welcome.png differ diff --git a/assets/welcome.rtf b/assets/welcome.rtf index 25045d1..33a159c 100644 --- a/assets/welcome.rtf +++ b/assets/welcome.rtf @@ -7,21 +7,21 @@ \deftab720 \pard\pardeftab720\partightenfactor0 -\f0\b\fs40 \cf2 \up0 \nosupersub \ulnone Welcome to the MNE-Python installer!\ +\f0\b\fs40 \cf2 \up0 \nosupersub \ulnone Welcome to the Scientific Python installer!\ \pard\pardeftab720\partightenfactor0 \f1\b0\fs22 \cf2 \ \ \pard\pardeftab720\partightenfactor0 -\f2\fs32 \cf2 \uc0\u55357 \u56446 -\f1 This installer will guide you through the installation of MNE-Python.\ +\f2\fs32 \cf2 \uc0\u55357 \u56446 +\f1 This installer will guide you through the installation of a Scientific Python environment.\ \ -\f2 \uc0\u9203 +\f2 \uc0\u9203 \f1 Installation might take several minutes to complete, even if the installer says it's less than one minute. Please be patient!\ \ -\f2 \uc0\u55356 \u57101 -\f1 For more information on MNE-Python, please visit our website at {\field{\*\fldinst{HYPERLINK "https://mne.tools"}}{\fldrslt \cf3 \ul \ulc3 mne.tools}}.\cf3 \ +\f2 \uc0\u55356 \u57101 +\f1 For more information on Scientific Python, please visit our website at {\field{\*\fldinst{HYPERLINK "https://scientific-python.org"}}{\fldrslt \cf3 \ul \ulc3 scientific-python.org}}.\cf3 \ } \ No newline at end of file diff --git a/assets/welcome_macOS.png b/assets/welcome_macOS.png index 6321746..2c8ec94 100644 Binary files a/assets/welcome_macOS.png and b/assets/welcome_macOS.png differ diff --git a/environment.yml b/environment.yml index c7a494a..d219642 100644 --- a/environment.yml +++ b/environment.yml @@ -10,3 +10,4 @@ dependencies: - menuinst - conda >=23.11.0 - fmt !=10.2.0 + - conda-build diff --git a/notes.md b/notes.md index ef1963a..e31ac82 100644 --- a/notes.md +++ b/notes.md @@ -10,7 +10,7 @@ argument. This directory needs to contain a file construct.yaml, which specifies the name of the installer, the conda channels to pull packages from, the conda packages included in the installer, etc. -In this repo, that file is `recipes/mne-python/construct.yaml`. It specifies, +In this repo, that file is `recipes/scientific-python/construct.yaml`. It specifies, among other things, that `conda-forge` is the only `channel` for fetching packages to provide installer. ```yaml @@ -27,7 +27,7 @@ condarc: - conda-forge channel_priority: strict allow_other_channels: false - env_prompt: "(mne-1.9.0_0) " + env_prompt: "(sp-0.1.0_0) " ``` From [the contructor README](https://github.com/conda/constructor): diff --git a/recipes/mne-python/construct.yaml b/recipes/mne-python/construct.yaml deleted file mode 100644 index c1c37be..0000000 --- a/recipes/mne-python/construct.yaml +++ /dev/null @@ -1,245 +0,0 @@ -version: 1.9.0_0 -name: MNE-Python -company: MNE-Python Developers -# When the version above changes to a new major/minor, it needs to be updated -# many places here - -# https://github.com/conda/constructor/blob/main/CONSTRUCT.md -license_file: ../../assets/license.txt # [linux] -license_file: ../../assets/license.rtf # [not linux] -welcome_image: ../../assets/welcome_macOS.png # [osx] -welcome_image: ../../assets/welcome.png # [not osx] -header_image: ../../assets/header.png -icon_image: ../../assets/icon.png -welcome_file: ../../assets/welcome.rtf -readme_text: "" -conclusion_file: ../../assets/conclusion.rtf # [not win] - -initialize_by_default: false -register_python_default: false - -default_prefix: ${HOME}/mne-python/1.9.0_0 # [linux] -default_prefix: "%USERPROFILE%\\mne-python\\1.9.0_0" # [win] -default_prefix_domain_user: "%LOCALAPPDATA%\\mne-python\\1.9.0_0" # [win] -default_prefix_all_users: "%ALLUSERSPROFILE%\\mne-python\\1.9.0_0" # [win] -default_location_pkg: "Applications/MNE-Python/1.9.0_0" # [osx] -pkg_name: ".mne-python" # [osx] -progress_notifications: true # [osx] -install_path_exists_error_text: | - {CHOSEN_PATH} already exists. Relaunch the installer and choose another location in the Destination Select step, or remove the existing directory and try again. - -uninstall_name: MNE-Python ${VERSION} (Python ${PYVERSION}) - -installer_filename: MNE-Python-1.9.0_0-macOS_Intel.pkg # [osx and not arm64] -installer_filename: MNE-Python-1.9.0_0-macOS_M1.pkg # [osx and arm64] -installer_filename: MNE-Python-1.9.0_0-Windows.exe # [win] -installer_filename: MNE-Python-1.9.0_0-Linux.sh # [linux] - -post_install: ../../assets/post_install_macOS.sh # [osx] -post_install: ../../assets/post_install_linux.sh # [linux] -post_install: ../../assets/post_install_windows.bat # [win] - -# Create signed macOS .pkg installer -installer_type: pkg # [osx] -signing_identity_name: # [osx] Used for productsign -notarization_identity_name: # [osx] Used for codesign -reverse_domain_identifier: tools.mne # [osx] Used for productbuild --identifier $reverse_domain_identifier.$name - -# Only create menus for mne-installer-menus -menu_packages: - - mne-installer-menus - -channels: - - conda-forge - # TODO: ⛔️ ⛔️ ⛔️ DEV BUILDS START: COMMENT OUT BEFORE RELEASE! ⛔️ ⛔️ ⛔️ - # - conda-forge/label/mne_dev - # - conda-forge/label/mne-bids_dev - # TODO: ⛔️ ⛔️ ⛔️ DEV BUILDS STOP: COMMENT OUT BEFORE RELEASE! ⛔️ ⛔️ ⛔️ -specs: - # Python - - python =3.13.3 # [not (osx and arm64)] - - python =3.13.2 # [osx and arm64] # allow_outdated - - pip =25.1.1 - - wheel =0.45.1 - - conda =25.3.1 - - mamba =2.1.0 - # MNE ecosystem - # TODO: ⛔️ ⛔️ ⛔️ DEV BUILDS START: CHANGE BEFORE RELEASE! ⛔️ ⛔️ ⛔️ - # - mne-base =1.4dev0=*_20230503 - # - mne-installer-menus =1.4dev0=*_20230503 - # - mne-bids =0.11dev0=*_20221007 - # TODO: ⛔️ ⛔️ ⛔️ DEV BUILDS STOP: CHANGE BEFORE RELEASE! ⛔️ ⛔️ ⛔️ - - mne =1.9.0=pyside6_*_100 - - mne-installer-menus =1.9.0=*_100 - # For testing purposes with build_local.sh, you can comment out all deps - # below for speed, and change mne to mne-base above - - # vvv this line is special and used by MNE-Python, do not change it! - # <<< BEGIN RELATED SOFTWARE LIST >>> - - mne-bids =0.16.0 - - mne-bids-pipeline =1.9.0 - - mne-qt-browser =0.7.1 - - mne-connectivity =0.7.0=*_1 - - mne-faster =1.2.2 - - mne-nirs =0.7.1 - - mne-features =0.3 - - mne-rsa =0.9.1 - - mne-ari =0.1.2 - - mne-icalabel =0.7.0 - - mne-gui-addons =0.1=*_1 - - mne-lsl =1.9.0 - - mne-kit-gui =1.3.0 - - autoreject =0.4.3 - - antio =0.5.0 - - wfdb =4.3.0 - - meegkit =0.1.9 - - eeg_positions =2.1.2 - # MRI - - fsleyes =1.14.2 - - dcm2niix =1.0.20241211 - - dipy =1.11.0 - # Time-frequency analysis - - pactools =0.3.1 - - tensorpac =0.6.5 - - emd =0.8.1 - - neurodsp =2.3.0 - - bycycle =1.1.0 - - fooof =1.1.1 - # Microstates - - mne-microstates =0.3.0 - - pycrostates =0.6.1 - # OpenNeuro.org data access - - openneuro-py =2024.2.0 - # sleep staging - - sleepecg =0.5.9=*_2 - - yasa =0.6.5 - # various biological signals (ECG, EOG, EMG, …) - - neurokit2 =0.2.10 - - mnelab =1.0.0 - # other - - pyriemann =0.8 - - pyprep =0.4.3=*_1 - - python-picard =0.8 - - pybv =0.7.6 - - edfio =0.4.7 # allow_outdated, for some reason 0.4.8 won't download - - eeglabio =0.0.3 - - mffpy =0.10.0 - - openmeeg =2.5.15 - - python-neo =0.14.1 - - nitime =0.11 - - snirf =0.8.0 - - alphacsc =0.4.1 - # Not on PyPI or no wheels on PyPI - # conpy: https://aaltoimaginglanguage.github.io/conpy/ - # posthoc: https://users.aalto.fi/~vanvlm1/posthoc/python/ - # eelbrain: https://eelbrain.readthedocs.io/en/stable/index.html - # Not on conda-forge - # niseq: https://github.com/john-veillette/niseq - # sesameeg: https://pybees.github.io/sesameeg - # invertmeeg: https://github.com/LukeTheHecker/invert - # Meggie: https://github.com/cibr-jyu/meggie - # MEM: https://github.com/multifunkim/best-python - # <<< END RELATED SOFTWARE LIST >>> - # ^^^ this line is special and used by MNE-Python, do not change it! - - - antropy =0.1.9 - - mayavi =4.8.2=pyside6_* - - traitsui =8.0.0 - - pyface =8.0.0=*_1 - - imageio-ffmpeg =0.6.0 - - pandas =2.2.3 - - polars =1.27.1 - - scipy =1.15.2 - - numpy =2.1.3 # allow_outdated, each new version has to wait for numba - - openblas =0.3.28 # allow_outdated, NumPy etc. need to update - - libblas =3.9.0=*openblas - - jupyter =1.1.1 - - jupyterlab =4.4.1 - - ipykernel =6.29.5 - - spyder-kernels =3.0.3 - # TODO: Needs to not require pyqt - # https://github.com/spyder-ide/spyder/issues/20201 - # spyder =6.1.0 - - darkdetect =0.8.0 - - qdarkstyle =3.2.3 - - numba =0.61.2 - - cython =3.0.12 - # I/O - - pyxdf =1.17.0 - - openpyxl =3.1.5 - - xlrd =2.0.1 - # Statistics - - pingouin =0.5.5 - # NeuroSpin needs the following - - questionary =2.1.0 - - pqdm =0.2.0 - - astropy =7.0.1 - # Viz - # matplotilb is just matplotlib-base, tornado, and pyqt - # https://github.com/conda-forge/matplotlib-feedstock/blob/main/recipe/meta.yaml - - matplotlib-base =3.10.1 - - tornado =6.4.2 - - pyside6 =6.8.2 # allow_outdated, PyVista (VTK 9.3.1) and Mayavi - - qt6-main =6.8.2 # allow_outdated - - ipympl =0.9.7 - - qtpy =2.4.3 - - seaborn =0.13.2 - - plotly =6.0.1 - # PyVista doesn't yet support VTK 9.4 - - vtk =9.3.1=qt* # allow_outdated - - ipywidgets =8.1.7 - - pyvista =0.45.0 - - pyvistaqt =0.11.2 - - trame =3.9.0 - - trame-vtk =2.8.15 - - trame-vuetify =3.0.1 - - termcolor =3.1.0 - - defusedxml =0.7.1 - # Development - - gh =2.72.0 - - setuptools_scm =8.3.1 - - pytest =8.3.5 - - pytest-cov =6.1.1 - - pytest-qt =4.4.0 - - pytest-timeout =2.3.1 - - pre-commit =4.2.0 - - ruff =0.11.8 - - uv =0.7.2 - - check-manifest =0.50 - - codespell =2.4.1 - - py-spy =0.4.0 - - line_profiler =4.2.0 - - memory_profiler =0.61.0 - - twine =6.1.0 - - hatchling =1.27.0 - - hatch-vcs =0.4.0 - - mypy =1.15.0 - - towncrier =24.8.0 - - vulture =2.14 - # Doc building - - numpydoc =1.8.0 - - pydata-sphinx-theme =0.16.1 - - graphviz =12.2.1 - - python-graphviz =0.20.3 - - selenium =4.32.0 - - sphinx =8.2.3 - - sphinx-design =0.6.1 - - sphinx-gallery =0.19.0 - - sphinxcontrib-bibtex =2.6.3 - - sphinx-copybutton =0.5.2 - - sphinxcontrib-youtube =1.4.1 - - intersphinx-registry =0.2501.23 - # OS-specific - - git =2.49.0 # [win] - - make =4.4.1 # [win] - - pyobjc-core =11.0 # [osx] - # https://github.com/conda-forge/libffi-feedstock/issues/52 - - libffi =3.4.6 # [osx and arm64] # allow_outdated - - pyobjc-framework-Cocoa =11.0 # [osx] - - pyobjc-framework-FSEvents =11.0 # [osx] -condarc: - channels: - - conda-forge - channel_priority: strict - allow_other_channels: false - env_prompt: "(mne-1.9.0_0) " diff --git a/recipes/scientific-python/construct.yaml b/recipes/scientific-python/construct.yaml new file mode 100644 index 0000000..b2bc25e --- /dev/null +++ b/recipes/scientific-python/construct.yaml @@ -0,0 +1,119 @@ +version: 0.1.0_0 +name: Scientific-Python +company: Scientific Python Developers +# When the version above changes to a new major/minor, it needs to be updated +# in many places below. + +# https://github.com/conda/constructor/blob/main/CONSTRUCT.md +license_file: ../../assets/license.txt # [linux] +license_file: ../../assets/license.rtf # [not linux] +welcome_image: ../../assets/welcome_macOS.png # [osx] +welcome_image: ../../assets/welcome.png # [not osx] +header_image: ../../assets/header.png +icon_image: ../../assets/icon.png +welcome_file: ../../assets/welcome.rtf +readme_text: "Installer for the Scientific Python data science packages" +conclusion_file: ../../assets/conclusion.rtf # [not win] + +initialize_by_default: false +register_python_default: false + +default_prefix: ${HOME}/Scientific-Python # [linux] +default_prefix: "%USERPROFILE%\\Scientific-Python" # [win] +default_prefix_domain_user: "%LOCALAPPDATA%\\Scientific-Python" # [win] +default_prefix_all_users: "%ALLUSERSPROFILE%\\Scientific-Python" # [win] +default_location_pkg: "Applications/Scientific-Python" # [osx] +pkg_name: ".Scientific-Python" # [osx] +progress_notifications: true # [osx] +install_path_exists_error_text: | + {CHOSEN_PATH} already exists. Relaunch the installer and choose another location in the Destination Select step, or remove the existing directory and try again. + +uninstall_name: Scientific-Python 0.1.0_0 (Python ${PYVERSION}) + +installer_filename: Scientific-Python-0.1.0_0-macOS_Intel.pkg # [osx and not arm64] +installer_filename: Scientific-Python-0.1.0_0-macOS_M1.pkg # [osx and arm64] +installer_filename: Scientific-Python-0.1.0_0-Windows.exe # [win] +installer_filename: Scientific-Python-0.1.0_0-Linux.sh # [linux] + +post_install: ../../assets/post_install_macOS.sh # [osx] +post_install: ../../assets/post_install_linux.sh # [linux] +post_install: ../../assets/post_install_windows.bat # [win] + +# Create signed macOS .pkg installer +installer_type: pkg # [osx] +signing_identity_name: # [osx] Used for productsign +notarization_identity_name: # [osx] Used for codesign +reverse_domain_identifier: org.scientific-python # [osx] Used for productbuild --identifier $reverse_domain_identifier.$name + +menu_packages: + - sp-installer-menu + +channels: + - conda-forge + - ./conda-bld +specs: + # Python + - python =3.13.3 # [not (osx and arm64)] + - python =3.13.2 # [osx and arm64] # allow_outdated + - pip =25.1.1 + - wheel =0.45.1 + - conda =25.5.0 + - mamba =2.1.1 + - threadpoolctl =3.6.0 # for our sysinfo menu command + # Menus + - sp-installer-menu =0.1.0 + # Scientific Python + - scipy =1.15.2 + - numpy =2.1.3 # allow_outdated, each new version has to wait for numba + - openblas =0.3.28 # allow_outdated, NumPy etc. need to update + - libblas =3.9.0=*openblas + # Web + - requests =2.32.3 + - pooch =1.8.2 + # Data science and statistics. + - pandas =2.2.3 + - polars =1.30.0 + - scikit-learn =1.6.1 + - statsmodels =0.14.4 + - pingouin =0.5.5 # https://pingouin-stats.org + # Jupyter + - jupyter =1.1.1 + - jupyterlab =4.4.3 + - nbclassic =1.3.1 + - ipykernel =6.29.5 + # I/O + - openpyxl =3.1.5 + - xlrd =2.0.1 + - pyreadstat =1.2.9 # https://github.com/Roche/pyreadstat + # Image processing + - scikit-image =0.25.2 + - pillow =11.2.1 + # Symbolic math + - sympy =1.14.0 + # Viz + - matplotlib =3.10.3 + - ipympl =0.9.7 + - seaborn =0.13.2 + - plotly =6.1.2 + - ipywidgets =8.1.7 + - termcolor =3.1.0 + # Security + - defusedxml =0.7.1 # https://github.com/tiran/defusedxml + # Development + - cython =3.1.1 + - pytest =8.3.5 + - pytest-timeout =2.4.0 + - pre-commit =4.2.0 + - ruff =0.11.12 + - uv =0.7.8 + # Doc building + - numpydoc =1.8.0 + # OS-specific + - git =2.49.0 # [win] + - make =4.4.1 # [win] +condarc: + channels: + - conda-forge + channel_priority: strict + allow_other_channels: false + env_prompt: "(sp-0.1.0_0) " diff --git a/sp-installer-menu/LICENSE.txt b/sp-installer-menu/LICENSE.txt new file mode 100644 index 0000000..521d2b9 --- /dev/null +++ b/sp-installer-menu/LICENSE.txt @@ -0,0 +1,6 @@ +The Scientific Python logos are used under a CC-BY license. + +"Jupyter" and the Jupyter logos are trademarks or registered trademarks of LF Charities, used by Scientific Python with permission. + +The other icons were taken from https://github.com/paomedia/small-n-flat and +are placed in the public domain. diff --git a/sp-installer-menu/make_menu.py b/sp-installer-menu/make_menu.py new file mode 100755 index 0000000..fa4d7ad --- /dev/null +++ b/sp-installer-menu/make_menu.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +"""Replace some markers in the JSON and other files; copy and rename others. + +Note that there are various {{ VAR }} Jinja template markers left in the JSON file, +but these are for processing by ``menuinst`` - see: +https://github.com/conda/menuinst/blob/1866363f197e0633fae0db8569119d102ca8d9cc/menuinst/platforms/base.py#L80 +""" + +from os import environ +from pathlib import Path +from shutil import copy2 + +# https://docs.conda.io/projects/conda-build/en/latest/user-guide/environment-variables.html +in_path = Path(environ["RECIPE_DIR"]) / "menu" +prefix = environ["PREFIX"] +out_path = Path(prefix) / "Menu" +pkg_name = environ["PKG_NAME"] +pkg_version = environ["PKG_VERSION"] + +if not out_path.is_dir(): + out_path.mkdir(parents=True) + + +def txt_replace(txt): + """Apply our own markup for search / replace. + + We use ``#MY_VAR#`` as indication to insert static text. + """ + for start, end in ( + ("#PREFIX#", prefix), + ("#PKG_NAME#", pkg_name), + ("#PKG_VERSION#", pkg_version), + ): + txt = txt.replace(start, end) + return txt + + +menu_txt = (in_path / "menu.json").read_text() +(out_path / f"{pkg_name}.json").write_text(txt_replace(menu_txt)) + + +for fstem in ("console", "info", "web", "forum", 'jupyter'): + for ext in ("icns", "ico", "png"): + copy2(in_path / f"{fstem}.{ext}", out_path / f"{pkg_name}_{fstem}.{ext}") + +for ext in ("sh", "applescript", "bat"): + for fpath in in_path.glob(f'*.{ext}'): + (out_path / f"{pkg_name}_{fpath.name}").write_text( + txt_replace(fpath.read_text())) + +for fname in ("spi_sys_info.py", "spi_mac_folder_icon.png"): + copy2(in_path / fname, out_path / fname) diff --git a/sp-installer-menu/menu/activate.bat b/sp-installer-menu/menu/activate.bat new file mode 100644 index 0000000..c123b61 --- /dev/null +++ b/sp-installer-menu/menu/activate.bat @@ -0,0 +1,11 @@ +:: Activate environment and change to workdir. +@ECHO OFF +SET "SP_SCRIPTS_DIR=%~dp0..\Scripts" +SET "SP_WORKDIR=%USERPROFILE%\Documents\scientific-python" +:: Workaround for +:: https://github.com/conda/conda/issues/14884 +SET "CONDA_EXE=%SP_SCRIPTS_DIR%\conda.exe" +CALL "%SP_SCRIPTS_DIR%\Activate.bat" +if not exist "%SP_WORKDIR%" mkdir "%SP_WORKDIR%" +pushd "%SP_WORKDIR%" + diff --git a/sp-installer-menu/menu/console.icns b/sp-installer-menu/menu/console.icns new file mode 100644 index 0000000..3c7896f Binary files /dev/null and b/sp-installer-menu/menu/console.icns differ diff --git a/sp-installer-menu/menu/console.ico b/sp-installer-menu/menu/console.ico new file mode 100644 index 0000000..cf824a4 Binary files /dev/null and b/sp-installer-menu/menu/console.ico differ diff --git a/sp-installer-menu/menu/console.png b/sp-installer-menu/menu/console.png new file mode 100644 index 0000000..836099c Binary files /dev/null and b/sp-installer-menu/menu/console.png differ diff --git a/sp-installer-menu/menu/forum.icns b/sp-installer-menu/menu/forum.icns new file mode 100644 index 0000000..70026f2 Binary files /dev/null and b/sp-installer-menu/menu/forum.icns differ diff --git a/sp-installer-menu/menu/forum.ico b/sp-installer-menu/menu/forum.ico new file mode 100644 index 0000000..66f945f Binary files /dev/null and b/sp-installer-menu/menu/forum.ico differ diff --git a/sp-installer-menu/menu/forum.png b/sp-installer-menu/menu/forum.png new file mode 100644 index 0000000..f85b575 Binary files /dev/null and b/sp-installer-menu/menu/forum.png differ diff --git a/sp-installer-menu/menu/info.icns b/sp-installer-menu/menu/info.icns new file mode 100644 index 0000000..635cde1 Binary files /dev/null and b/sp-installer-menu/menu/info.icns differ diff --git a/sp-installer-menu/menu/info.ico b/sp-installer-menu/menu/info.ico new file mode 100644 index 0000000..24a4118 Binary files /dev/null and b/sp-installer-menu/menu/info.ico differ diff --git a/sp-installer-menu/menu/info.png b/sp-installer-menu/menu/info.png new file mode 100644 index 0000000..fc976b6 Binary files /dev/null and b/sp-installer-menu/menu/info.png differ diff --git a/sp-installer-menu/menu/jupyter.icns b/sp-installer-menu/menu/jupyter.icns new file mode 100644 index 0000000..06da654 Binary files /dev/null and b/sp-installer-menu/menu/jupyter.icns differ diff --git a/sp-installer-menu/menu/jupyter.ico b/sp-installer-menu/menu/jupyter.ico new file mode 100644 index 0000000..07259fe Binary files /dev/null and b/sp-installer-menu/menu/jupyter.ico differ diff --git a/sp-installer-menu/menu/jupyter.png b/sp-installer-menu/menu/jupyter.png new file mode 100644 index 0000000..caf031e Binary files /dev/null and b/sp-installer-menu/menu/jupyter.png differ diff --git a/sp-installer-menu/menu/menu.json b/sp-installer-menu/menu/menu.json new file mode 100644 index 0000000..a601ba1 --- /dev/null +++ b/sp-installer-menu/menu/menu.json @@ -0,0 +1,181 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "$id": "https://schemas.conda.io/menuinst-1.schema.json", + "menu_name": "Scientific Python (#PKG_VERSION#)", + "menu_items": [{ + "name": "Scientific Python Environment Info", + "description": "Information on the Scientific Python runtime environment", + "icon": "{{ MENU_DIR }}/#PKG_NAME#_info.{{ ICON_EXT }}", + "command": [ + "{{ PYTHON }}", + "{{ MENU_DIR }}/spi_sys_info.py" + ], + "activate": true, + "terminal": false, + "platforms": { + "win": { + "desktop": false + }, + "linux": { + "Categories": [ + "Science" + ] + }, + "osx": { + "CFBundleName": "SP Sys Info", + "CFBundleDisplayName": "System Information (Scientific Python #PKG_VERSION#)", + "CFBundleVersion": "#PKG_VERSION#" + } + } + }, + { + "name": "Scientific Python Terminal", + "description": "Scientific Python Terminal Console", + "icon": "{{ MENU_DIR }}/#PKG_NAME#_console.{{ ICON_EXT }}", + "activate": true, + "command": ["will be overridden in platforms section"], + "platforms": { + "win": { + "command": [ + "%SystemRoot%\\system32\\cmd.exe", + "/K", + "{{ MENU_DIR }}\\#PKG_NAME#_open_prompt.bat" + ], + "desktop": false, + "terminal": true + }, + "linux": { + "command": [ + "exec", + "bash", + "--init-file", + "{{ MENU_DIR }}/#PKG_NAME#_open_prompt.sh" + ], + "Categories": [ + "Science" + ], + "terminal": true + }, + "osx": { + "command": [ + "osascript", + "{{ MENU_DIR }}/#PKG_NAME#_open_prompt.applescript" + ], + "CFBundleName": "SP Terminal", + "CFBundleDisplayName": "Terminal (Scientific Python #PKG_VERSION#)", + "CFBundleVersion": "#PKG_VERSION#", + "terminal": false + } + } + }, + { + "name": "Scientific Python Notebooks", + "description": "Scientific Python JupyterLab Notebooks", + "icon": "{{ MENU_DIR }}/#PKG_NAME#_jupyter.{{ ICON_EXT }}", + "activate": true, + "command": ["will be overridden in platforms section"], + "platforms": { + "win": { + "command": [ + "%SystemRoot%\\system32\\cmd.exe", + "/K", + "{{ MENU_DIR }}\\#PKG_NAME#_open_jupyterlab.bat" + ], + "desktop": false, + "terminal": true + }, + "linux": { + "command": [ + "exec", + "bash", + "--init-file", + "{{ MENU_DIR }}/#PKG_NAME#_open_jupyterlab.sh" + ], + "Categories": [ + "Science" + ], + "terminal": true + }, + "osx": { + "command": [ + "osascript", + "{{ MENU_DIR }}/#PKG_NAME#_open_jupyterlab.applescript" + ], + "CFBundleName": "SP Notebooks", + "CFBundleDisplayName": "JupyterLab Notebooks (Scientific Python #PKG_VERSION#)", + "CFBundleVersion": "#PKG_VERSION#", + "terminal": false + } + } + }, + { + "name": "Scientific Python Lectures", + "description": "Scientific Python Lecture Notes", + "icon": "{{ MENU_DIR }}/#PKG_NAME#_web.{{ ICON_EXT }}", + "activate": false, + "terminal": false, + "command": ["will be overridden in platforms section"], + "platforms": { + "win": { + "command": [ + "%SystemRoot%\\system32\\WindowsPowerShell\\v1.0\\powershell.exe", + "\"start https://lectures.scientific-python.org -WindowStyle hidden\"" + ], + "desktop": false + }, + "linux": { + "command": [ + "xdg-open", + "https://lectures.scientific-python.org" + ], + "Categories": [ + "Science" + ] + }, + "osx": { + "command": [ + "open", + "https://lectures.scientific-python.org" + ], + "CFBundleName": "SP Lectures", + "CFBundleDisplayName": "Lecture Notes (Scientific Python)", + "CFBundleVersion": "#PKG_VERSION#" + } + } + }, + { + "name": "Scientific Python Forum", + "description": "Scientific Python forum for discussions, problem solving, and information exchange", + "icon": "{{ MENU_DIR }}/#PKG_NAME#_forum.{{ ICON_EXT }}", + "activate": false, + "terminal": false, + "command": ["will be overridden in platforms section"], + "platforms": { + "win": { + "command": [ + "%SystemRoot%\\system32\\WindowsPowerShell\\v1.0\\powershell.exe", + "\"start https://discuss.scientific-python.org -WindowStyle hidden\"" + ], + "desktop": false + }, + "linux": { + "command": [ + "xdg-open", + "https://discuss.scientific-python.org" + ], + "Categories": [ + "Science" + ] + }, + "osx": { + "command": [ + "open", + "https://discuss.scientific-python.org" + ], + "CFBundleName": "SP Forum", + "CFBundleDisplayName": "Forum (Scientific Python)", + "CFBundleVersion": "#PKG_VERSION#" + } + } + }] +} diff --git a/sp-installer-menu/menu/open_jupyterlab.applescript b/sp-installer-menu/menu/open_jupyterlab.applescript new file mode 100644 index 0000000..d7cc0bd --- /dev/null +++ b/sp-installer-menu/menu/open_jupyterlab.applescript @@ -0,0 +1,4 @@ +tell application "Terminal" + do script "source #PREFIX#/Menu/#PKG_NAME#_open_jupyterlab.sh" + activate +end tell diff --git a/sp-installer-menu/menu/open_jupyterlab.bat b/sp-installer-menu/menu/open_jupyterlab.bat new file mode 100644 index 0000000..518c877 --- /dev/null +++ b/sp-installer-menu/menu/open_jupyterlab.bat @@ -0,0 +1,4 @@ +:: Start JupyterLab. +@ECHO OFF +call "%~dp0#PKG_NAME#_activate.bat" +jupyter lab diff --git a/sp-installer-menu/menu/open_jupyterlab.sh b/sp-installer-menu/menu/open_jupyterlab.sh new file mode 100644 index 0000000..3d59ece --- /dev/null +++ b/sp-installer-menu/menu/open_jupyterlab.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# This is used to start JupyterLab on macOS and Linux. + +. "#PREFIX#/Menu/#PKG_NAME#_open_prompt.sh" +jupyter lab diff --git a/sp-installer-menu/menu/open_prompt.applescript b/sp-installer-menu/menu/open_prompt.applescript new file mode 100644 index 0000000..39211c8 --- /dev/null +++ b/sp-installer-menu/menu/open_prompt.applescript @@ -0,0 +1,4 @@ +tell application "Terminal" + do script "source #PREFIX#/Menu/#PKG_NAME#_open_prompt.sh" + activate +end tell diff --git a/sp-installer-menu/menu/open_prompt.bat b/sp-installer-menu/menu/open_prompt.bat new file mode 100755 index 0000000..824b52c --- /dev/null +++ b/sp-installer-menu/menu/open_prompt.bat @@ -0,0 +1,12 @@ +:: Start a CMD command prompt. +@ECHO OFF +CALL "%~dp0#PKG_NAME#_activate.bat" +:: Find first Python on path. +FOR /F "tokens=*" %%g IN ('where python') do ( + SET PYPATH=%%g + goto :endloop +) +:endloop +FOR /F "tokens=*" %%g IN ('python --version') do (SET PYVER=%%g) +ECHO Using %PYVER% from %PYPATH% +ECHO This is Scientific Python #PKG_VERSION# diff --git a/sp-installer-menu/menu/open_prompt.sh b/sp-installer-menu/menu/open_prompt.sh new file mode 100755 index 0000000..0c94f72 --- /dev/null +++ b/sp-installer-menu/menu/open_prompt.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# This is used to initialize the bash prompt on macOS and Linux. + +if [[ -f ~/.bashrc ]] && [[ ${OSTYPE} != 'darwin'* ]]; then + source ~/.bashrc +fi +source "#PREFIX#/bin/activate" base +export JUPYTER_CONFIG_DIR="#PREFIX#/etc/jupyter" +export JUPYTER_DATA_DIR="#PREFIX#/share/jupyter" +echo "Using $(python --version) from $(which python)" +echo "This is Scientific Python version #PKG_VERSION#" +workdir="$HOME/Documents/scientific-python" +mkdir -p $workdir +pushd $workdir diff --git a/sp-installer-menu/menu/spi_mac_folder_icon.png b/sp-installer-menu/menu/spi_mac_folder_icon.png new file mode 100644 index 0000000..f93eb26 Binary files /dev/null and b/sp-installer-menu/menu/spi_mac_folder_icon.png differ diff --git a/sp-installer-menu/menu/spi_sys_info.py b/sp-installer-menu/menu/spi_sys_info.py new file mode 100755 index 0000000..00486a6 --- /dev/null +++ b/sp-installer-menu/menu/spi_sys_info.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +"""Show system information as a web page, or as plain text on stdout. + +This script will be invoked by the System Information menu entry. +""" + +import multiprocessing +import platform +import subprocess +import sys +import tempfile +import webbrowser + +from importlib import import_module +from importlib.metadata import metadata +from os import sep +from xml.etree import ElementTree + + +class UnknownPlatformError(Exception): + """Exception raised for unknown platforms.""" + + +# TODO: find a way to vendor the things that `pyvista.GPUInfo()` can give you +# (renderer name, renderer version...). + + +def _get_numpy_libs(): + bad_lib = "unknown linalg bindings" + try: + from threadpoolctl import threadpool_info + except Exception as exc: + return bad_lib + f" (threadpoolctl module not found: {exc})" + pools = threadpool_info() + rename = dict(openblas="OpenBLAS", mkl="MKL") + for pool in pools: + if pool["internal_api"] in ("openblas", "mkl"): + plural = "s" if (pool["num_threads"]) > 1 else "" + return ( + f"{rename[pool['internal_api']]} " + f"{pool['version']} with " + f"{pool['num_threads']} thread{plural}" + ) + return bad_lib + + +def _get_total_memory(): + """Return the total memory of the system in bytes.""" + if platform.system() == "Windows": + o = subprocess.check_output( + [ + "powershell.exe", + "(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory", + ] + ).decode() + total_memory = int(o) + elif platform.system() == "Linux": + o = subprocess.check_output(["free", "-b"]).decode() + total_memory = int(o.splitlines()[1].split()[1]) + elif platform.system() == "Darwin": + o = subprocess.check_output(["sysctl", "hw.memsize"]).decode() + total_memory = int(o.split(":")[1].strip()) + else: + raise UnknownPlatformError("Could not determine total memory") + return total_memory + + +def _get_cpu_brand(): + """Return the CPU brand string.""" + if platform.system() == "Windows": + o = subprocess.check_output( + ["powershell.exe", "(Get-CimInstance Win32_Processor).Name"] + ).decode() + cpu_brand = o.strip().splitlines()[-1] + elif platform.system() == "Linux": + o = subprocess.check_output(["grep", "model name", "/proc/cpuinfo"]).decode() + cpu_brand = o.splitlines()[0].split(": ")[1] + elif platform.system() == "Darwin": + o = subprocess.check_output(["sysctl", "machdep.cpu"]).decode() + cpu_brand = o.split("brand_string: ")[1].strip() + else: + cpu_brand = "?" + return cpu_brand + + +def main(): + """Print system information in a browser window.""" + # initialize output file + outfile = tempfile.NamedTemporaryFile( + mode="w", prefix="SP_sys_info", suffix=".html", delete=False + ) + out = list() + + # Platform / Python / Executable + pyversion = str(sys.version).replace("\n", " ") + out.append(f"Platform: {platform.platform()}") + out.append(f"Python: {pyversion}") + out.append(f"Executable: {sys.executable}") + + # CPU + try: + cpu_brand = _get_cpu_brand() + except Exception: + cpu_brand = "?" + out.append(f"CPU: {cpu_brand} ({multiprocessing.cpu_count()} cores)") + + # Memory + try: + total_memory = _get_total_memory() + except UnknownPlatformError: + total_memory = "?" + else: + total_memory = f"{total_memory / 1024**3:.1f}" # convert to GiB + out.append(f"Memory: {total_memory} GiB") + + # Packages + out.append("Key Packages:") + mod_names = ( + "ipykernel", + "jupyter", + "jupyterlab", + "matplotlib", + "numpy", + "pandas", + "pillow", + "pingouin", + "plotly", + "polars", + "pooch", + "scikit-image", + "scikit-learn", + "scipy", + "seaborn", + "statsmodels", + "sympy", + ) + lookup = {"pillow": "PIL", "scikit-learn": "sklearn", "scikit-image": "skimage"} + for mod_name in mod_names: + try: + mod = import_module(mod_name.replace("-", "_")) + except Exception: + mod = import_module(lookup[mod_name]) + line = f"{mod_name} {metadata(mod_name).get('version')}" + if mod_name == "numpy": + line += f" ({_get_numpy_libs()})" + elif mod_name == "matplotlib": + line += f" (backend={mod.get_backend()})" + out.append(line) + + if len(sys.argv) > 1 and sys.argv[1] == "nohtml": + print() # blank line + print(sep.join(out), file=sys.stdout) + return + + # build the output tree + html = ElementTree.Element("html") + body = ElementTree.Element("body", attrib={"style": "font-size: x-large;"}) + div = ElementTree.Element("div") + ul = ElementTree.Element("ul") + html.append(body) + body.append(div) + body.append(ul) + for line in out: + is_mod = line.split(" ", maxsplit=1)[0] in mod_names + node = ElementTree.Element("li" if is_mod else "p") + node.text = line + parent = ul if is_mod else div + parent.append(node) + + ElementTree.ElementTree(html).write(outfile.name, encoding="unicode", method="html") + webbrowser.open(f"file://{outfile.name}", new=2) + + +if __name__ == "__main__": + main() diff --git a/sp-installer-menu/menu/web.icns b/sp-installer-menu/menu/web.icns new file mode 100644 index 0000000..7cecbfc Binary files /dev/null and b/sp-installer-menu/menu/web.icns differ diff --git a/sp-installer-menu/menu/web.ico b/sp-installer-menu/menu/web.ico new file mode 100644 index 0000000..6f3ddb5 Binary files /dev/null and b/sp-installer-menu/menu/web.ico differ diff --git a/sp-installer-menu/menu/web.png b/sp-installer-menu/menu/web.png new file mode 100644 index 0000000..bb5e8ba Binary files /dev/null and b/sp-installer-menu/menu/web.png differ diff --git a/sp-installer-menu/meta.yaml b/sp-installer-menu/meta.yaml new file mode 100644 index 0000000..5effbe5 --- /dev/null +++ b/sp-installer-menu/meta.yaml @@ -0,0 +1,58 @@ +{% set name = "sp-installer-menu" %} +{% set version = "0.1.0" %} +{% set build = 1 %} +{% set python_min = "3.9" %} + +package: + name: {{ name|lower }} + version: {{ version }} + +build: + number: {{ build }} + # skip: true # [not linux] + noarch: python + script: + - pushd {{ RECIPE_DIR }} + - python make_menu.py + +requirements: + host: + - python {{ python_min }} + run: + - python >={{ python_min }} + +test: + requires: + - python {{ python_min }} + commands: + - test -f ${CONDA_PREFIX}/Menu/sp-installer-menu.json + - test -f ${CONDA_PREFIX}/Menu/spi_sys_info.py + - test -f ${CONDA_PREFIX}/Menu/sp-installer-menu_open_prompt.applescript + - test -f ${CONDA_PREFIX}/Menu/sp-installer-menu_open_prompt.sh + - test -f ${CONDA_PREFIX}/Menu/sp-installer-menu_open_prompt.bat + - test -f ${CONDA_PREFIX}/Menu/sp-installer-menu_activate.bat + - test -f ${CONDA_PREFIX}/Menu/sp-installer-menu_open_jupyterlab.applescript + - test -f ${CONDA_PREFIX}/Menu/sp-installer-menu_open_jupyterlab.sh + - test -f ${CONDA_PREFIX}/Menu/sp-installer-menu_open_jupyterlab.bat + # Check we didn't forget any icons + - test `ls ${CONDA_PREFIX}/Menu/sp-installer-menu_console.* | wc -l` -eq 3 + - test `ls ${CONDA_PREFIX}/Menu/sp-installer-menu_info.* | wc -l` -eq 3 + - test `ls ${CONDA_PREFIX}/Menu/sp-installer-menu_web.* | wc -l` -eq 3 + - test `ls ${CONDA_PREFIX}/Menu/sp-installer-menu_forum.* | wc -l` -eq 3 + - test -f ${CONDA_PREFIX}/Menu/spi_mac_folder_icon.png + +about: + home: http://learn.scientific-python.org + license: BSD-3-Clause + license_family: BSD + license_file: LICENSE.txt + summary: Menu entries for the Scientific Python installer + description: | + This package provides various icons, configuration files and scripts for + use with the Scientific Python environment installer. + doc_url: http://learn.scientific-python.org + dev_url: https://github.com/scientific-python/installer +extra: + recipe-maintainers: + - matthew-brett + - drammock diff --git a/tests/test_dev_installed.py b/tests/test_dev_installed.py deleted file mode 100644 index 30c94f1..0000000 --- a/tests/test_dev_installed.py +++ /dev/null @@ -1,45 +0,0 @@ -import platform -import re -import subprocess - -would = "Would install " -out = subprocess.check_output( - [ - "pip", - "install", - "--no-build-isolation", - "--dry-run", - # This can be used to speed up local testing - # "../mne-python[full,test,test_extra,doc]", - "mne[full-no-qt,test,test_extra,doc] @ git+https://github.com/mne-tools/mne-python.git@main", - ] -) -out = out.decode("utf-8") -deps = [line for line in out.split("\n") if line.startswith(would)] -assert len(deps) == 1, len(deps) -deps = deps[0] -print(f"Found pip install line:\n{deps}") -deps = deps[len(would) :] -# Remove PyQt6 and some other stuff -deps = [ - dep - for dep in deps.split() - if not re.match("mne-[0-9]+", dep) - # TODO because we don't install neo - and not dep.startswith("neo-") - and not dep.startswith("numpy-") - and not dep.startswith("pyxdf-") - and not dep.startswith("quantities-") - # Qt-related stuff - and not dep.startswith("sip-") - # and not on conda-forge yet - and not dep.startswith("sphinxcontrib-towncrier") - and not dep.startswith("antio") - and not dep.startswith("toml-sort") - and not dep.startswith("tomlkit") - and not dep == "pyarrow-15.0.0" # not tensorflow compatible - # for some reason vtk is not detected properly on Windows even though it imports - and not (platform.system() == "Windows" and dep.startswith("vtk")) -] -deps = "\n".join(deps) -assert deps == "", f"Unexpected unmet dependencies:\n{deps}" diff --git a/tests/test_gui.py b/tests/test_gui.py index 84e6ddf..9a4835d 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -1,68 +1,24 @@ print("Running imports") import faulthandler +from platform import system faulthandler.enable() -import os from pathlib import Path -import numpy as np import matplotlib.pyplot as plt -import pyvista -import mne +plat2backend = { + 'Darwin': 'NSView', + 'Windows': 'QTAgg', + 'Linux': 'QTAgg' +} this_path = Path(__file__).parent # Matplotlib print("Running matplotlib tests") fig, _ = plt.subplots() -want = "QTAgg" +want = plat2backend[system()] assert want in repr(fig.canvas), repr(fig.canvas) plt.close("all") - -# pyvistaqt -if os.getenv("SKIP_PYVISTAQT_TESTS", "").lower() in ("1", "true"): - print("Skipping PyVistaQt tests") -else: - print("Running pyvistaqt tests") - 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() - plotter = fig.figure.plotter - plotter.add_orientation_widget(pyvista.Cube()) # Old test without color='b' - plotter.add_mesh(pyvista.Cube(), render=True) - if fname.is_file(): - os.remove(fname) - assert not fname.is_file() - fig._process_events() - plotter.screenshot(fname) - assert fname.is_file() - os.remove(fname) - assert "BackgroundPlotter" in repr(plotter), repr(plotter) - mne.viz.close_3d_figure(fig) - -# 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.0, "eeg")) -fig = raw.plot() -fig.close() -assert "MNEQtBrowser" in repr(fig), repr(fig) - -# mne-kit-gui -if os.getenv("SKIP_MNE_KIT_GUI_TESTS", "").lower() in ("1", "true"): - print("Skipping MNE-KIT-GUI tests") -else: - print("Running MNE-KIT-GUI tests") - from pyface.api import GUI # noqa - import mne_kit_gui # noqa - - os.environ["_MNE_GUI_TESTING_MODE"] = "true" - gui = GUI() - gui.process_events() - ui, frame = mne_kit_gui.kit2fiff() - assert not frame.model.can_save - ui.dispose() diff --git a/tests/test_imports.py b/tests/test_imports.py index 68e69db..a745da2 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -1,9 +1,11 @@ import argparse import importlib -from tqdm import tqdm -from pathlib import Path import platform +from pathlib import Path +import re +from tqdm import tqdm +import yaml parser = argparse.ArgumentParser(prog="test_imports") parser.add_argument("--ignore", nargs="*", help="Modules to ignore", default=[]) @@ -24,68 +26,76 @@ def check_version_eq(package, ver): if isinstance(package, str): package = _import(package) + package_str = getattr(package, "__version__", getattr(package, "version", None)) + + if package_str is None: + raise ImportError(f"Could not get version for {package}") try: - package_ver = parse(package.__version__) - except Exception: - raise ImportError( - f"Could not parse version for {package}: {repr(package.__version__)}" - ) - assert package_ver >= parse(ver), ( - f"{package}: got {package.__version__} wanted {ver}" - ) + package_ver = parse(package_str) + except ValueError: + raise ImportError(f"Could not parse version {package_str} for {package}") + + assert package_ver >= parse(ver), f"{package}: got {package_ver}, wanted {ver}" # All related software -lines = ( - (Path(__file__).parents[1] / "recipes" / "mne-python" / "construct.yaml") - .read_text("utf-8") - .splitlines() +construct_path = ( + Path(__file__).parents[1] / "recipes" / "scientific-python" / "construct.yaml" ) -lines = [line.strip() for line in lines] -all_lines = lines -sidx = lines.index("# <<< BEGIN RELATED SOFTWARE LIST >>>") -eidx = lines.index("# <<< END RELATED SOFTWARE LIST >>>") -lines = [line for line in lines[sidx : eidx + 1] if not line.startswith("#")] -assert lines -# NB: next line assumes that there are no "less-than" pins -mods = [line[2:].split("#")[0].split(">")[0].split("=")[0].strip() for line in lines] - -# Plus some custom ones -mods += """ -darkdetect qdarkstyle numba openpyxl xlrd pingouin questionary -seaborn plotly pqdm pyvistaqt vtk PySide6 PySide6.QtCore matplotlib matplotlib.pyplot -""".strip().split() -if platform.system() == "Darwin": - mods += ["Foundation"] # pyobjc +constructs = yaml.load(construct_path.read_text(), Loader=yaml.SafeLoader) +specs = constructs["specs"] # Now do the importing and version checking -bad_ver = { - "mne-faster", # https://github.com/wmvanvliet/mne-faster/pull/7 - "mne-ari", # https://github.com/john-veillette/mne-ari/pull/7 - "pactools", # https://github.com/pactools/pactools/pull/37 - "Foundation", +# Conda packages that do not provide a Python importable module. +no_import = { + "python", + "mamba", + "openblas", + "libblas", + "qt6-main", + "uv", + "git", + "make", + "libffi", + "sp-installer-menu", } -mod_map = { # for import test, need map from conda-forge line/name to importable name - "python-neo": "neo", - "python-picard": "picard", - "openneuro-py": "openneuro", + +# PyPI name to import name map. +name_mod_map = { + # for import test, need map from conda-forge line/name to importable name + "scikit-learn": "sklearn", + "scikit-image": "skimage", + "pillow": "PIL", + "matplotlib-base": "matplotlib", + "pyside6": "PySide6", + "pytest-timeout": "pytest_timeout", + "pre-commit": "pre_commit", + "sphinxcontrib-bibtex": "sphinxcontrib.bibtex", + "sphinxcontrib-youtube": "sphinxcontrib.youtube", + "pyobjc-core": "Foundation", + "pyobjc-framework-Cocoa": "Foundation", + "pyobjc-framework-FSEvents": "Foundation", } -ver_map = { # for __version__, need map from importable name to conda-forge line/name - "matplotlib": "matplotlib-base", + +# Module imports where we cannot or should not check versions, but where we do +# want to check we can import. +bad_ver = { + "wheelconda", + "pip", + "jupyter", + "termcolor", + "pytest_timeout", + "pre_commit", + "ruff", + "Foundation", } -ignore = list(parsed.ignore) + ["dcm2niix"] -for mod in tqdm(mods, desc="Imports", unit="module"): - if mod in ignore: + +ignore = no_import.union(parsed.ignore) +for spec in tqdm(specs, desc="Imports", unit="module"): + pkg_name, version = re.split(r"[\s=]+", spec, maxsplit=1) + mod_name = name_mod_map.get(pkg_name, pkg_name) + if mod_name in ignore: continue - py_mod = _import(mod_map.get(mod, mod)) - if mod not in bad_ver and "." not in mod: - ver_lines = [ - line.split("#")[0].strip() - for line in all_lines - if line.startswith(f"- {ver_map.get(mod, mod).lower()} =") - ] - assert len(ver_lines) == 1, f"{mod}: {ver_lines}" - check_version_eq(py_mod, ver_lines[0].split("=")[1]) - if mod == "matplotlib.pyplot": - backend = py_mod.get_backend() - assert backend.lower() == "qtagg", backend + py_mod = _import(mod_name) + if mod_name not in bad_ver and "." not in mod_name: + check_version_eq(py_mod, version) diff --git a/tests/test_json_versions.py b/tests/test_json_versions.py index d85f8ef..135005a 100644 --- a/tests/test_json_versions.py +++ b/tests/test_json_versions.py @@ -27,31 +27,28 @@ case _: raise ValueError(f"Platform not recognized: {sys.platform}") -recipe_dir = pathlib.Path(__file__).parents[1] / "recipes" / "mne-python" +recipe_dir = pathlib.Path(__file__).parents[1] / "recipes" / "scientific-python" construct_yaml_path = recipe_dir / "construct.yaml" params = yaml.safe_load(construct_yaml_path.read_text(encoding="utf-8")) installer_version = params["version"] specs = params["specs"] del params -# Extract versions from construct.yaml +# Want versions apply to versions specific to this installer. want_versions = {} for spec in specs: - if " =" not in spec or spec.count("=") < 2: + if " =" not in spec or 'sp-installer-menu' not in spec: continue package_name, package_version_and_build = spec.split(" ") + print('pkg name', package_name) package_version = package_version_and_build.split("=")[1] - package_build = package_version_and_build.split("=")[-1] - want_versions[package_name] = { - "version": package_version, - "build_string": package_build, - } -for name in ("mne", "mne-installer-menus"): # the most important ones! - assert name in want_versions, f"{name} missing from want_versions (build str error)" -assert len(want_versions) > 2, len(want_versions) # more than just the two above + want_versions[package_name] = {"version": package_version} +assert 'sp-installer-menu' in want_versions, \ + "sp-installer-menu missing from want_versions (build str error)" +assert len(want_versions) == 1, len(want_versions) # more than just the one above # Extract versions from created environment -fname = dir_ / f"MNE-Python-{installer_version}-{sys_name}{sys_ext}.env.json" +fname = dir_ / f"Scientific-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")) got_versions = dict() @@ -66,4 +63,3 @@ got = got_versions[package_name] msg = f"{package_name}: got {repr(got)} != want {repr(want)}" assert got["version"] == want["version"], msg - assert fnmatch.fnmatch(got["build_string"], want["build_string"]), msg diff --git a/tests/test_notebook.py b/tests/test_notebook.py index 03152bd..2d3c025 100644 --- a/tests/test_notebook.py +++ b/tests/test_notebook.py @@ -1,6 +1,6 @@ # Create one nbclient and reuse it import os -from mne.utils import Bunch +from sklearn.utils import Bunch import nbformat from jupyter_client import AsyncKernelManager from nbclient import NotebookClient @@ -51,11 +51,6 @@ import matplotlib.pyplot as plt fig, ax = plt.subplots() assert 'CanvasAgg ' in repr(fig.canvas), repr(fig.canvas) - -import mne -mne.viz.set_3d_backend('notebook') -fig = mne.viz.create_3d_figure((400, 400), show=True) -assert '.Plotter ' in repr(fig.plotter), repr(fig.plotter) """ with _nbclient.setup_kernel(): assert _nbclient.kc is not None diff --git a/tests/test_outdated.py b/tests/test_outdated.py index f5282a1..46609f1 100644 --- a/tests/test_outdated.py +++ b/tests/test_outdated.py @@ -17,11 +17,12 @@ def _cache(fun): return fun + else: memory = Memory(Path(__file__).parent / ".joblib_cache", verbose=0) _cache = memory.cache(cache_validation_callback=expires_after(minutes=60)) -recipe_dir = Path(__file__).parents[1] / "recipes" / "mne-python" +recipe_dir = Path(__file__).parents[1] / "recipes" / "scientific-python" construct_yaml_path = recipe_dir / "construct.yaml" print(f"Analyzing spec file: {construct_yaml_path}\n") @@ -105,6 +106,10 @@ def get_conda_json(package): for package in packages: if package.version_spec is None: continue + elif package.name == "sp-installer-menu": # locally built + # TODO instead of skipping, we should get the version number from the env + # and test that it matches the version in `construct.yaml` + continue try: json = get_conda_json(package) @@ -115,7 +120,7 @@ def get_conda_json(package): # Iterate in reverse chronological order, omitting versions marked as broken and # those that are not in the main channel - # TODO We may want to make exceptions here for MNE testing versions if we need them + # TODO We may want to make exceptions here for testing versions if we need them version = None for file in json["files"][::-1]: if "broken" in file["labels"]: diff --git a/tools/build_local.sh b/tools/build_local.sh index 1adcd6b..e0d5d13 100755 --- a/tools/build_local.sh +++ b/tools/build_local.sh @@ -4,7 +4,7 @@ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && source ${SCRIPT_DIR}/extract_version.sh echo "Building installer locally" echo "--------------------------" -echo "Version: ${MNE_INSTALLER_VERSION}" +echo "Version: ${SP_INSTALLER_VERSION}" echo "Recipe: ${RECIPE_DIR}" echo "OS: ${MACHINE}" echo "Machine: ${PYMACHINE}" diff --git a/tools/calculate_installer_hash.sh b/tools/calculate_installer_hash.sh index dc41502..69a2755 100755 --- a/tools/calculate_installer_hash.sh +++ b/tools/calculate_installer_hash.sh @@ -4,12 +4,12 @@ set -eo pipefail shopt -s nullglob # Fail if the following pattern yields no results echo "Finding matches" -matches=(MNE-Python-*-*.*) +matches=(Scientific-Python-*-*.*) echo "Extracting fname" installer_fname="${matches[0]}" echo "Found name: ${installer_fname}" -echo "Want name: ${MNE_INSTALLER_NAME}" -test "$installer_fname" == "$MNE_INSTALLER_NAME" -hash_fname="${MNE_INSTALLER_NAME}.sha256.txt" -shasum -a 256 "$MNE_INSTALLER_NAME" > "$hash_fname" +echo "Want name: ${SP_INSTALLER_NAME}" +test "$installer_fname" == "$SP_INSTALLER_NAME" +hash_fname="${SP_INSTALLER_NAME}.sha256.txt" +shasum -a 256 "$SP_INSTALLER_NAME" > "$hash_fname" cat "$hash_fname" diff --git a/tools/check_installation.sh b/tools/check_installation.sh index 06702a6..db8038e 100755 --- a/tools/check_installation.sh +++ b/tools/check_installation.sh @@ -3,8 +3,8 @@ # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#grouping-log-lines set -eo pipefail -echo "Running tests for MNE_MACHINE=${MNE_MACHINE}" -source "${MNE_ACTIVATE}" +echo "Running tests for SP_MACHINE=${SP_MACHINE}" +source "${SP_ACTIVATE}" echo "::group::conda info" conda info @@ -19,10 +19,10 @@ pip list echo "::endgroup::" echo "::group::Platform specific tests" -if [[ "$MNE_MACHINE" == "macOS" ]]; then +if [[ "$SP_MACHINE" == "macOS" ]]; then echo "Testing that file permissions are set correctly (owned by "$USER", not "root".)" # https://unix.stackexchange.com/a/7733 - APP_DIR=/Applications/MNE-Python/${MNE_INSTALLER_VERSION} + APP_DIR=$(dirname $SP_INSTALL_PREFIX) [ `ls -ld ${APP_DIR} | awk 'NR==1 {print $3}'` == "$USER" ] || exit 1 echo "Checking that the installed Python is a binary for the correct CPU architecture" @@ -33,47 +33,41 @@ if [[ "$MNE_MACHINE" == "macOS" ]]; then fi echo "Checking we have all .app bundles in ${APP_DIR}:" - ls -al /Applications/ - ls -al /Applications/MNE-Python - ls -al ${APP_DIR} + ls -al $(dirname $APP_DIR) + ls -al $(APP_DIR) echo "Checking that there are 5 directories" test `ls -d ${APP_DIR}/*.app | wc -l` -eq 5 || exit 1 - echo "Checking that the custom icon was set on the MNE folder in ${APP_DIR}" - test -f /Applications/MNE-Python/Icon$'\r' || exit 1 - export SKIP_MNE_KIT_GUI_TESTS=1 -elif [[ "$MNE_MACHINE" == "Linux" ]]; then + echo "Checking that the custom icon was set on the SP folder in ${APP_DIR}" + test -f ${APP_DIR}/Icon$'\r' || exit 1 + export SKIP_SP_KIT_GUI_TESTS=1 +elif [[ "$SP_MACHINE" == "Linux" ]]; then echo "Checking that menu shortcuts were created …" pushd ~/.local/share/applications ls -l || exit 1 echo "Checking for existence of .desktop files:" - ls mne-python*.desktop || exit 1 - test `ls mne-python*.desktop | wc -l` -eq 5 || exit 1 + ls scientific-python*.desktop || exit 1 + test `ls scientific-python*.desktop | wc -l` -eq 5 || exit 1 echo "" # … and patched to work around a bug in menuinst echo "Checking that incorrect Terminal entries have been removed" - test `grep "Terminal=True" mne-python*.desktop | wc -l` -eq 0 || exit 1 - test `grep "Terminal=False" mne-python*.desktop | wc -l` -eq 0 || exit 1 + test `grep "Terminal=True" scientific-python*.desktop | wc -l` -eq 0 || exit 1 + test `grep "Terminal=False" scientific-python*.desktop | wc -l` -eq 0 || exit 1 echo "" echo "Checking that Terminal entries are correct…" - test `grep "Terminal=true" mne-python*.desktop | wc -l` -ge 1 || exit 1 - test `grep "Terminal=false" mne-python*.desktop | wc -l` -ge 1 || exit 1 + # console, notebooks, sysinfo + test `grep "Terminal=true" scientific-python*.desktop | wc -l` -eq 2 || exit 1 + test `grep "Terminal=false" scientific-python*.desktop | wc -l` -eq 3 || exit 1 # Display their contents - for f in mne-python*.desktop; do echo "📂 $f:"; cat "$f"; echo; done + for f in scientific-python*.desktop; do echo "📂 $f:"; cat "$f"; echo; done popd - if [[ `grep "24.04" /etc/lsb-release` ]] || [[ `grep "20.04" /etc/lsb-release` ]]; then - export SKIP_PYVISTAQT_TESTS=1 - export SKIP_NOTEBOOK_TESTS=1 - fi -else - export SKIP_PYVISTAQT_TESTS=1 fi echo "::endgroup::" echo "::group::Checking for pinned file..." -test -e "$MNE_INSTALL_PREFIX/conda-meta/pinned" -grep "openblas" "$MNE_INSTALL_PREFIX/conda-meta/pinned" +test -e "$SP_INSTALL_PREFIX/conda-meta/pinned" +grep "openblas" "$SP_INSTALL_PREFIX/conda-meta/pinned" echo "::endgroup::" echo "::group::Checking permissions" @@ -82,34 +76,25 @@ echo "Got OWNER=$OWNER, should be $(whoami)" test "$OWNER" == "$(whoami)" echo "::endgroup::" -echo "::group::Checking whether Qt is working" -# LD_DEBUG=libs -python -c "from qtpy.QtWidgets import QApplication, QWidget; app = QApplication([])" -echo "::endgroup::" - echo "::group::Checking the deployed environment variables were set correctly upon environment activation" conda env config vars list -if [[ "$MNE_MACHINE" == "macOS" && "$MACOS_ARCH" == "Intel" ]]; then +if [[ "$SP_MACHINE" == "macOS" && "$MACOS_ARCH" == "Intel" ]]; then python -c "import os; x = os.getenv('CONDA_SUBDIR'); assert x == 'osx-64', f'CONDA_SUBDIR ({repr(x)}) != osx-64'" || exit 1 fi # TODO: broken on Windows! -if [[ "$MNE_MACHINE" != "Windows" ]]; then +if [[ "$SP_MACHINE" != "Windows" ]]; then python -c "import os; x = os.getenv('PYTHONNOUSERSITE'); assert x == '1', f'PYTHONNOUSERSITE ({repr(x)}) != 1'" || exit 1 python -c "import os; x = os.getenv('MAMBA_NO_BANNER'); assert x == '1', f'MAMBA_NO_BANNER ({repr(x)}) != 1'" || exit 1 fi echo "::endgroup::" -echo "::group::mne sys_info" -mne sys_info +echo "::group::spi_sys_info" +python -u ${SP_INSTALL_PREFIX}/Menu/spi_sys_info.py nohtml echo "::endgroup::" -echo "::group::Trying to import MNE and all additional packages included in the installer" +echo "::group::Trying to import SP and all additional packages included in the installer" python -u tests/test_imports.py python -u tests/test_gui.py python -u tests/test_notebook.py python -u tests/test_json_versions.py echo "::endgroup::" - -echo "::group::Checking that all packages are installed that MNE-Python devs would need" -python -u tests/test_dev_installed.py -echo "::endgroup::" diff --git a/tools/export_frozen_env_def.sh b/tools/export_frozen_env_def.sh index 923b98c..9529746 100755 --- a/tools/export_frozen_env_def.sh +++ b/tools/export_frozen_env_def.sh @@ -2,6 +2,6 @@ set -eo pipefail -source "${MNE_ACTIVATE}" -conda list --json > ${MNE_INSTALLER_NAME}.env.json -cat ${MNE_INSTALLER_NAME}.env.json +source "${SP_ACTIVATE}" +conda list --json > ${SP_INSTALLER_NAME}.env.json +cat ${SP_INSTALLER_NAME}.env.json diff --git a/tools/extract_version.sh b/tools/extract_version.sh index 10161fc..ce04dad 100755 --- a/tools/extract_version.sh +++ b/tools/extract_version.sh @@ -1,8 +1,8 @@ #!/bin/bash -ef SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )"; -export RECIPE_DIR=${SCRIPT_DIR}/../recipes/mne-python -export MNE_INSTALLER_VERSION=$(grep "^version: .*$" ${RECIPE_DIR}/construct.yaml | cut -d' ' -f2) +export RECIPE_DIR=${SCRIPT_DIR}/../recipes/scientific-python +export SP_INSTALLER_VERSION=$(grep "^version: .*$" ${RECIPE_DIR}/construct.yaml | cut -d' ' -f2) export PYSHORT=$(python -c "import sys; print('.'.join(map(str, sys.version_info[:2])))") UNAME="$(uname -s)" if [[ "$1" != "" ]] && [[ "$1" != "--dry-run" ]]; then @@ -40,53 +40,53 @@ fi export MACOS_ARCH=$MACOS_ARCH if [[ "$MACHINE" == "macOS" ]]; then - MNE_INSTALL_PREFIX="/Applications/MNE-Python/${MNE_INSTALLER_VERSION}/.mne-python" - MNE_INSTALLER_NAME="MNE-Python-${MNE_INSTALLER_VERSION}-${MACHINE}_${MACOS_ARCH}.pkg" - MNE_ACTIVATE="$MNE_INSTALL_PREFIX/bin/activate" + SP_INSTALL_PREFIX="/Applications/Scientific-Python/.scientific-python" + SP_INSTALLER_NAME="Scientific-Python-${SP_INSTALLER_VERSION}-${MACHINE}_${MACOS_ARCH}.pkg" + SP_ACTIVATE="$SP_INSTALL_PREFIX/bin/activate" elif [[ "$MACHINE" == "Linux" ]]; then - MNE_INSTALL_PREFIX="$HOME/mne-python/${MNE_INSTALLER_VERSION}" - MNE_INSTALLER_NAME="MNE-Python-${MNE_INSTALLER_VERSION}-${MACHINE}.sh" - MNE_ACTIVATE="$MNE_INSTALL_PREFIX/bin/activate" + SP_INSTALL_PREFIX="$HOME/Scientific-Python" + SP_INSTALLER_NAME="Scientific-Python-${SP_INSTALLER_VERSION}-${MACHINE}.sh" + SP_ACTIVATE="$SP_INSTALL_PREFIX/bin/activate" else - MNE_INSTALL_PREFIX="$HOME/mne-python/$MNE_INSTALLER_VERSION" - MNE_INSTALLER_NAME="MNE-Python-${MNE_INSTALLER_VERSION}-${MACHINE}.exe" - MNE_ACTIVATE="$MNE_INSTALL_PREFIX/Scripts/activate" + SP_INSTALL_PREFIX="$HOME/Scientific-Python" + SP_INSTALLER_NAME="Scientific-Python-${SP_INSTALLER_VERSION}-${MACHINE}.exe" + SP_ACTIVATE="$SP_INSTALL_PREFIX/Scripts/activate" fi -export MNE_INSTALL_PREFIX="$MNE_INSTALL_PREFIX" -export MNE_INSTALLER_NAME="${MNE_INSTALLER_NAME}" -export MNE_ACTIVATE="$MNE_ACTIVATE" -export MNE_INSTALLER_ARTIFACT_ID="MNE-Python-${MACHINE}-${ARTIFACT_ID_SUFFIX}" -export MNE_MACHINE="$MACHINE" +export SP_INSTALL_PREFIX="$SP_INSTALL_PREFIX" +export SP_INSTALLER_NAME="${SP_INSTALLER_NAME}" +export SP_ACTIVATE="$SP_ACTIVATE" +export SP_INSTALLER_ARTIFACT_ID="Scientific-Python-${MACHINE}-${ARTIFACT_ID_SUFFIX}" +export SP_MACHINE="$MACHINE" -echo "Version: ${MNE_INSTALLER_VERSION}" -test "$MNE_INSTALLER_VERSION" != "" +echo "Version: ${SP_INSTALLER_VERSION}" +test "$SP_INSTALLER_VERSION" != "" echo "System Python: ${PYSHORT}" test "$PYSHORT" != "" test -d "$SCRIPT_DIR" echo "Recipe: ${RECIPE_DIR}" test "$RECIPE_DIR" != "" test -d "$RECIPE_DIR" -echo "Installer: ${MNE_INSTALLER_NAME}" -test "$MNE_INSTALLER_NAME" != "" -echo "Artifact ID: ${MNE_INSTALLER_ARTIFACT_ID}" -test "$MNE_INSTALLER_ARTIFACT_ID" != "" -echo "Prefix: ${MNE_INSTALL_PREFIX}" -test "$MNE_INSTALL_PREFIX" != "" -echo "Activate: ${MNE_ACTIVATE}" -test "$MNE_ACTIVATE" != "" -echo "Machine: ${MNE_MACHINE}" -test "$MNE_MACHINE" != "" +echo "Installer: ${SP_INSTALLER_NAME}" +test "$SP_INSTALLER_NAME" != "" +echo "Artifact ID: ${SP_INSTALLER_ARTIFACT_ID}" +test "$SP_INSTALLER_ARTIFACT_ID" != "" +echo "Prefix: ${SP_INSTALL_PREFIX}" +test "$SP_INSTALL_PREFIX" != "" +echo "Activate: ${SP_ACTIVATE}" +test "$SP_ACTIVATE" != "" +echo "Machine: ${SP_MACHINE}" +test "$SP_MACHINE" != "" if [[ "$GITHUB_ACTIONS" == "true" ]]; then - echo "MNE_INSTALLER_VERSION=${MNE_INSTALLER_VERSION}" | tee -a $GITHUB_ENV - echo "MNE_INSTALLER_NAME=${MNE_INSTALLER_NAME}" | tee -a $GITHUB_ENV - echo "MNE_INSTALLER_ARTIFACT_ID=${MNE_INSTALLER_ARTIFACT_ID}" | tee -a $GITHUB_ENV - echo "MNE_INSTALL_PREFIX=${MNE_INSTALL_PREFIX}" | tee -a $GITHUB_ENV + echo "SP_INSTALLER_VERSION=${SP_INSTALLER_VERSION}" | tee -a $GITHUB_ENV + echo "SP_INSTALLER_NAME=${SP_INSTALLER_NAME}" | tee -a $GITHUB_ENV + echo "SP_INSTALLER_ARTIFACT_ID=${SP_INSTALLER_ARTIFACT_ID}" | tee -a $GITHUB_ENV + echo "SP_INSTALL_PREFIX=${SP_INSTALL_PREFIX}" | tee -a $GITHUB_ENV echo "RECIPE_DIR=${RECIPE_DIR}" | tee -a $GITHUB_ENV - echo "MNE_ACTIVATE=${MNE_ACTIVATE}" | tee -a $GITHUB_ENV + echo "SP_ACTIVATE=${SP_ACTIVATE}" | tee -a $GITHUB_ENV echo "NSIS_SCRIPTS_RAISE_ERRORS=1" | tee -a $GITHUB_ENV - echo "MNE_MACHINE=${MNE_MACHINE}" | tee -a $GITHUB_ENV + echo "SP_MACHINE=${SP_MACHINE}" | tee -a $GITHUB_ENV echo "MACOS_ARCH=${MACOS_ARCH}" | tee -a $GITHUB_ENV if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then echo "ARTIFACT_RETENTION_DAYS=5" | tee -a $GITHUB_ENV diff --git a/tools/macos_check_installer_signature.sh b/tools/macos_check_installer_signature.sh index 7e2dd9d..0fbbedc 100755 --- a/tools/macos_check_installer_signature.sh +++ b/tools/macos_check_installer_signature.sh @@ -3,20 +3,21 @@ set -eo pipefail if [[ "$GITHUB_EVENT_NAME" != "pull_request" ]]; then - pkgutil --check-signature ${MNE_INSTALLER_NAME} || exit 1 + pkgutil --check-signature ${SP_INSTALLER_NAME} || exit 1 fi # Now extract the package and check that the _conda binary is # properly signed as well -pkgutil --expand-full ${MNE_INSTALLER_NAME} ./mne-extracted -DIR="./mne-extracted/prepare_installation.pkg/Payload/.mne-python" +pkgutil --expand-full ${SP_INSTALLER_NAME} ./sp-extracted +DIR="./sp-extracted/prepare_installation.pkg/Payload/.scientific-python" echo "Checking ${DIR} exists" test -d "$DIR" ls -al "$DIR" BINARY="${DIR}/_conda" echo "Checking ${BINARY} exists" test -e "${BINARY}" -echo "Checking ${BINARY} is signed" -codesign -vd "${BINARY}" -echo "Checking entitlements of ${BINARY}" -codesign --display --entitlements - "${BINARY}" -rm -rf ./mne-extracted +# TODO - we need to restore code signing. +# echo "Checking ${BINARY} is signed" +# codesign -vd "${BINARY}" +# echo "Checking entitlements of ${BINARY}" +# codesign --display --entitlements - "${BINARY}" +rm -rf ./sp-extracted diff --git a/tools/macos_notarize_installer.sh b/tools/macos_notarize_installer.sh index 8531136..a8313f3 100755 --- a/tools/macos_notarize_installer.sh +++ b/tools/macos_notarize_installer.sh @@ -3,10 +3,10 @@ set -eo pipefail # Notarize the installer -xcrun notarytool submit ./${MNE_INSTALLER_NAME} \ +xcrun notarytool submit ./${SP_INSTALLER_NAME} \ --wait \ --apple-id=$APPLE_ID \ --password=$APPLE_ID_PASSWORD \ --team-id=$APPLE_TEAM_ID # Staple the notarization certificate onto it -xcrun stapler staple ${MNE_INSTALLER_NAME} +xcrun stapler staple ${SP_INSTALLER_NAME}