From 1a70ce49fc0acc0d0850353573c6a442bc8565ab Mon Sep 17 00:00:00 2001 From: Florian Rau Date: Fri, 15 Sep 2023 17:23:40 +0200 Subject: [PATCH] move to pyproject.toml (#500) * move to pyproject.toml * add pre-commit * fix CI * Update README.md * limit pre-commit to python, yaml, json & toml * add more pre-commit hooks * Update installation.rst * update versioning scheme * Update conf.py * Update developer_guide.rst * move some paragraphs from README.MD to sphinx docu * Update pyproject.toml * Update developer_guide.rst * Update developer_guide.rst * Update pyproject.toml * Update __init__.py * Update main.yaml * update suggested git-command for updating * add upgrade_iblrig command * some minor fixes * Update version_management.py * rescue lost changes from #502 * when the number of expected devices is undefined, do not raise but warn and exit * Copy (#503) * WIP recover from partial copy multiple devices * copier hierarchy * Update transfer_experiments.py * Update transfer_experiments.py * modify acquisition description aggregation level from copiers * add the script to copy video data / purge sessions older than 2 weeks * Fix chained protocols bug * bugfix: the criteria to end the task should not factor in the delay period --------- Co-authored-by: Florian Rau --------- Co-authored-by: olivier --- .flake8 | 2 +- .github/workflows/documentation.yaml | 2 +- .github/workflows/main.yaml | 7 +- .pre-commit-config.yaml | 31 +++++ LICENSE | 2 +- README.md | 25 +--- docs/source/conf.py | 14 +-- docs/source/developer_guide.rst | 61 +++++++++ docs/source/installation.rst | 118 ++++++++++-------- iblrig/__init__.py | 9 +- iblrig/base_tasks.py | 7 +- .../neuropixel/wirings/3A.wiring.json | 2 +- .../neuropixel/wirings/3B.wiring.json | 1 - iblrig/device_descriptions/sync/bpod.yaml | 2 +- iblrig/device_descriptions/sync/nidq.yaml | 2 +- iblrig/gui/wizard.py | 15 ++- iblrig/misc.py | 3 +- iblrig/version_management.py | 108 ++++++++++++---- .../task_parameters.yaml | 2 +- .../task_parameters.yaml | 2 +- .../task_parameters.yaml | 2 +- .../task_parameters.yaml | 2 +- pybpod_fixtures/IBL/IBL.json | 2 +- .../_iblrig_testlab_behavior_1.json | 2 +- .../_iblrig_calibration.json | 2 +- .../_iblrig_test_mouse.json | 2 +- .../_iblrig_test_user/_iblrig_test_user.json | 2 +- pyproject.toml | 69 ++++++++++ requirements-dev.txt | 7 -- requirements.txt | 20 --- setup.py | 50 -------- 31 files changed, 357 insertions(+), 218 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 pyproject.toml delete mode 100644 requirements-dev.txt delete mode 100644 requirements.txt delete mode 100644 setup.py diff --git a/.flake8 b/.flake8 index 67087e873..a2162126a 100644 --- a/.flake8 +++ b/.flake8 @@ -5,4 +5,4 @@ exclude = .git, scratch_*.py, Bonsai, - venv* \ No newline at end of file + venv* diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml index 58fb4cb50..cdc1094d3 100644 --- a/.github/workflows/documentation.yaml +++ b/.github/workflows/documentation.yaml @@ -22,4 +22,4 @@ jobs: publish_branch: gh-pages github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: docs/build/html - force_orphan: true \ No newline at end of file + force_orphan: true diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index f4f5358f6..1572c1412 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -17,13 +17,15 @@ jobs: steps: - name: Checkout iblrig repo uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Setup Python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: 'pip' - cache-dependency-path: '**/requirements*.txt' + cache-dependency-path: 'pyproject.toml' - name: flake8 run: | @@ -34,8 +36,7 @@ jobs: - name: iblrig and iblpybpod requirements shell: bash -l {0} run: | - pip install --editable iblrig - pip install -r requirements.txt + pip install --editable . - name: Install audio library (Ubuntu only) if: matrix.os == 'ubuntu-latest' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..d7cb1012e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,31 @@ +repos: + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: end-of-file-fixer + types_or: [python, yaml, json, toml] + - id: trailing-whitespace + types_or: [python, yaml, json, toml] + - id: check-case-conflict + - id: check-docstring-first + - id: check-ast + - id: check-yaml + - id: check-json + - id: check-toml + - id: check-xml + - id: check-vcs-permalinks + - id: detect-private-key + +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: python-check-blanket-type-ignore + - id: python-use-type-annotations + - id: python-no-log-warn + - id: text-unicode-replacement-char + +- repo: https://github.com/pycqa/flake8 + rev: 6.1.0 + hooks: + - id: flake8 diff --git a/LICENSE b/LICENSE index fbda26d61..73262d336 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 60b6c21c1..8c15bac99 100644 --- a/README.md +++ b/README.md @@ -2,31 +2,8 @@ Welcome to the International Brain Laboratory decision making task implementation. -The task is implemented in the Bpod system (Sanworks) and uses the Bonsai visual programming language (on Windows) for the visual stimuli and Python for all other purposes. +The task is implemented in the Bpod system (Sanworks) and uses the Bonsai visual programming language (on Windows) for the visual stimuli and Python for all other purposes. ## Documentation and installation steps here: https://int-brain-lab.github.io/iblrig - - - -### Run tests locally -```shell -flake8 -python -m unittest discover ./iblrig/test -``` - -### Build the documentation -```shell -# make sure pre-requisites are installed -pip install -r requirements-dev.txt -# create the static directory -rm -rf ./docs/build -mkdir -p ./docs/build/html/_static -# unit tests generate task diagrams -python -m unittest discover ./iblrig/test -# generate class diagrams -pyreverse -o png -m y --ignore iblrig.test -A --output-directory ./docs/build/html/_static ./iblrig_tasks -# build and serve the docs locally -sphinx-autobuild ./docs/source ./docs/build/html/ -``` diff --git a/docs/source/conf.py b/docs/source/conf.py index 0d50b7e59..2d2d42ba6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,15 +1,11 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information +from importlib.metadata import version +from datetime import date project = 'iblrig' -copyright = '2018, International Brain Laboratory' +copyright = f'2018 – {date.today().year} International Brain Laboratory' author = 'International Brain Laboratory' -release = '8.x' +release = version('iblrig') +version = '.'.join(release.split('.')[:3]) # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/docs/source/developer_guide.rst b/docs/source/developer_guide.rst index eae18d97d..94b86fb6b 100644 --- a/docs/source/developer_guide.rst +++ b/docs/source/developer_guide.rst @@ -1,3 +1,64 @@ +=============== +Developer Guide +=============== + +Versioning Scheme +================= + +IBLRIG v8 uses `Semantic Versioning 2.0.0 `_. +Its version string (currently "|version|") is a combination of three fields, separated by dots: + +.. centered:: ``MAJOR`` . ``MINOR`` . ``PATCH`` + +* The ``MAJOR`` field is only incremented for breaking changes, i.e., changes that are not backward compatible with previous changes. + Releases of IBLRIG v8, for instance, are generally incompatible with IBLRIG v7. +* The ``MINOR`` field will be incremented upon adding new, backwards compatible features. +* The ``PATCH`` field will be incremented with each new, backwards compatible bugfix release that does not implement a new feature. + +On the developer side, these 3 fields are manually controlled by adding the respective version string to a commit as a `git tag `_, for instance: + +.. code-block:: console + + git tag 8.8.4 + git push origin --tags + +The version string displayed by IBLRIG *may* include additional fields, such as in "|version|.post3+dirty". +Here, + +* ``.post3`` indicates the third unversioned commit after the latest versioned release, and +* ``+dirty`` indicates the presence of uncommited changes in your local repository of IBLRIG. + +Both of these fields are inferred by `setuptools_scm `_ and do not require manual interaction from the developer. + + +Running Tests Locally +===================== + +.. code-block:: console + + flake8 + python -m unittest discover ./iblrig/test + + +Building the documentation +========================== + +.. code-block:: console + + # make sure pre-requisites are installed + pip install --upgrade -e .[DEV] + # create the static directory + rm -rf ./docs/build + mkdir -p ./docs/build/html/_static + # unit tests generate task diagrams + python -m unittest discover ./iblrig/test + # generate class diagrams + pyreverse -o png -m y --ignore iblrig.test -A --output-directory ./docs/build/html/_static ./iblrig_tasks + # build and serve the docs locally + sphinx-autobuild ./docs/source ./docs/build/html/ + + + Guide to Creating Your Own Task =============================== diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 87500096f..79a52e960 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -3,9 +3,9 @@ Installation guide .. prerequisites:: - * Windows OS - * git installation. - * Recommended: Notepad++ or a decent text file editor + * Windows OS + * git installation. + * Recommended: Notepad++ or a decent text file editor Install Python 3.10 @@ -15,24 +15,24 @@ Open an Administrator: Windows Powershell prompt and run the following: .. code-block:: powershell - Set-ExecutionPolicy RemoteSigned + Set-ExecutionPolicy RemoteSigned Then exit the admin window and open a new Windows Powershell prompt (no admin mode) and run the following: .. code-block:: powershell - New-Item -ItemType Directory -Force -Path C:\Temp - Invoke-WebRequest -Uri https://www.python.org/ftp/python/3.10.11/python-3.10.11-amd64.exe -OutFile C:\Temp\python-3.10.11-amd64.exe - Start-Process -NoNewWindow -Wait -FilePath C:\Temp\python-3.10.11-amd64.exe -ArgumentList "/passive", "InstallAllUsers=0", "Include_launcher=0", "Include_test=0" + New-Item -ItemType Directory -Force -Path C:\Temp + Invoke-WebRequest -Uri https://www.python.org/ftp/python/3.10.11/python-3.10.11-amd64.exe -OutFile C:\Temp\python-3.10.11-amd64.exe + Start-Process -NoNewWindow -Wait -FilePath C:\Temp\python-3.10.11-amd64.exe -ArgumentList "/passive", "InstallAllUsers=0", "Include_launcher=0", "Include_test=0" .. exercise:: You can check that everything worked by running the following command: - .. code-block:: powershell + .. code-block:: powershell - C:\Users\IBLuser\AppData\Local\Programs\Python\Python310\.\python.exe --version + C:\Users\IBLuser\AppData\Local\Programs\Python\Python310\.\python.exe --version - Should return `Python 3.10.11` + Should return `Python 3.10.11` .. warning:: Make sure you exit the Administrator Powershell prompt before going to the next steps. @@ -40,53 +40,61 @@ Then exit the admin window and open a new Windows Powershell prompt (no admin mo Install iblrigv8 ---------------- -1. From the Powershell command line, clone iblrig to the ‘iblrigv8’ directory, and switch to iblrigv8 branch +1. From the Powershell command line, clone iblrig to the ‘iblrigv8’ directory, and switch to iblrigv8 branch - .. code-block:: powershell + .. code-block:: powershell - cd C:\ - git clone https://github.com/int-brain-lab/iblrig.git iblrigv8 - cd iblrigv8 - git checkout iblrigv8 + cd C:\ + git clone https://github.com/int-brain-lab/iblrig.git iblrigv8 + cd iblrigv8 + git checkout iblrigv8 -2. Install a new virtual environment and update pip (modify the value if needed) +2. Install a new virtual environment and update pip (modify the value if needed) - .. code-block:: powershell + .. code-block:: powershell - C:\Users\IBLuser\AppData\Local\Programs\Python\Python310\.\python.exe -m venv C:\iblrigv8\venv - C:\iblrigv8\venv\scripts\python.exe -m pip install --upgrade pip wheel + C:\Users\IBLuser\AppData\Local\Programs\Python\Python310\.\python.exe -m venv C:\iblrigv8\venv + C:\iblrigv8\venv\scripts\python.exe -m pip install --upgrade pip wheel -3. Install iblrig in editable mode +3. Install iblrig in editable mode - .. code-block:: powershell + .. code-block:: powershell - C:\iblrigv8\venv\scripts\Activate.ps1 - cd C:\iblrigv8 - pip install -e . - pip install -r requirements.txt + C:\iblrigv8\venv\scripts\Activate.ps1 + cd C:\iblrigv8 + pip install -e . -4. Install additional tasks and extractors for personal projects (optional) +4. Install additional tasks and extractors for personal projects (optional) - .. code-block:: powershell + .. code-block:: powershell - cd C:\ - git clone https://github.com/int-brain-lab/project_extraction.git - cd project_extraction - pip install -e . + cd C:\ + git clone https://github.com/int-brain-lab/project_extraction.git + cd project_extraction + pip install -e . -5. Install Bonsai in portable mode +5. Install Bonsai in portable mode - .. code-block:: powershell + .. code-block:: powershell - cd C:\iblrigv8\Bonsai - powershell.exe .\install.ps1 - cd .. + cd C:\iblrigv8\Bonsai + powershell.exe .\install.ps1 + cd .. +Update iblrigv8 +--------------- + + .. code-block:: powershell + + C:\iblrigv8\venv\scripts\Activate.ps1 + cd C:\iblrigv8 + pip install --upgrade -e . + Configuration instructions -------------------------- @@ -99,16 +107,16 @@ Copy template settings files. .. code-block:: - cd C:\iblrigv8\settings - cp hardware_settings_template.yaml hardware_settings.yaml - cp iblrig_settings_template.yaml iblrig_settings.yaml - explorer C:\iblrigv8\settings + cd C:\iblrigv8\settings + cp hardware_settings_template.yaml hardware_settings.yaml + cp iblrig_settings_template.yaml iblrig_settings.yaml + explorer C:\iblrigv8\settings Update the 2 settings files, these values can likely be found in the `C:\iblrig_params\.iblrig_params.json` file if working with a existing rig -* iblrig_settings.yaml -* hardware_settings.yaml +* iblrig_settings.yaml +* hardware_settings.yaml Setup ONE @@ -122,26 +130,26 @@ See instructions for that here: https://int-brain-lab.github.io/iblenv/notebooks .. exercise:: Make sure you can connect to Alyx ! - Open a Python shell in the environment and connect to Alyx (you may have to setup ONE) + Open a Python shell in the environment and connect to Alyx (you may have to setup ONE) - .. code-block:: + .. code-block:: - C:\iblrigv8\venv\scripts\Activate.ps1 - ipython + C:\iblrigv8\venv\scripts\Activate.ps1 + ipython - Then at the Ipython prompt + Then at the Ipython prompt - .. code-block:: python + .. code-block:: python - from one.api import ONE - one = ONE(username='your_username', password='your_password', base_url='https://alyx.internationalbrainlab.org') + from one.api import ONE + one = ONE(username='your_username', password='your_password', base_url='https://alyx.internationalbrainlab.org') .. exercise:: You can check that everything went fine by running the test suite: - .. code-block:: powershell + .. code-block:: powershell - cd C:\iblrigv8 - python -m unittest discover + cd C:\iblrigv8 + python -m unittest discover - The tests should pass to completion after around 40 seconds + The tests should pass to completion after around 40 seconds diff --git a/iblrig/__init__.py b/iblrig/__init__.py index 1d69de198..751b1ba4d 100644 --- a/iblrig/__init__.py +++ b/iblrig/__init__.py @@ -1 +1,8 @@ -__version__ = "8.8.4" +from pathlib import Path +from setuptools_scm import get_version +from importlib.metadata import version + +if Path('.github').exists(): + __version__ = get_version(version_scheme='post-release', local_scheme='dirty-tag') +else: + __version__ = version('iblrig') diff --git a/iblrig/base_tasks.py b/iblrig/base_tasks.py index 2414cd587..5816f3bf8 100644 --- a/iblrig/base_tasks.py +++ b/iblrig/base_tasks.py @@ -53,7 +53,7 @@ class BaseSession(ABC): def __init__(self, subject=None, task_parameter_file=None, file_hardware_settings=None, hardware_settings=None, file_iblrig_settings=None, iblrig_settings=None, - one=None, interactive=True, projects=None, procedures=None, subject_weight_grams=None, stub=None, + one=None, interactive=True, projects=None, procedures=None, stub=None, subject_weight_grams=None, append=False, log_level='INFO', wizard=False): """ :param subject: The subject nickname. Required. @@ -81,8 +81,9 @@ def __init__(self, subject=None, task_parameter_file=None, file_hardware_setting BaseSession.checked_for_update = True update_status, remote_version = check_for_updates() if update_status is True: - print(f"\nUpdate to iblrig {remote_version} is available! Please update using 'git pull'.\n") - + print(f"\nUpdate to iblrig {remote_version} is available!\n" + f"Please update by issuing:\n\n" + f" upgrade_iblrig\n") while True: print("- Press [Enter] to exit IBL Rig and perform the update right away.\n" "- Enter 'I will update later' to continue without updating.") diff --git a/iblrig/device_descriptions/neuropixel/wirings/3A.wiring.json b/iblrig/device_descriptions/neuropixel/wirings/3A.wiring.json index 029c0556a..f78035859 100644 --- a/iblrig/device_descriptions/neuropixel/wirings/3A.wiring.json +++ b/iblrig/device_descriptions/neuropixel/wirings/3A.wiring.json @@ -15,4 +15,4 @@ "pin20": "audio_ground", "pin23": "rotary_encoder_ground" } -} \ No newline at end of file +} diff --git a/iblrig/device_descriptions/neuropixel/wirings/3B.wiring.json b/iblrig/device_descriptions/neuropixel/wirings/3B.wiring.json index d9401500b..80ea598ae 100644 --- a/iblrig/device_descriptions/neuropixel/wirings/3B.wiring.json +++ b/iblrig/device_descriptions/neuropixel/wirings/3B.wiring.json @@ -4,4 +4,3 @@ "P0.6": "imec_sync" } } - diff --git a/iblrig/device_descriptions/sync/bpod.yaml b/iblrig/device_descriptions/sync/bpod.yaml index 16c73b381..e449ce84b 100644 --- a/iblrig/device_descriptions/sync/bpod.yaml +++ b/iblrig/device_descriptions/sync/bpod.yaml @@ -1,4 +1,4 @@ sync: bpod: collection: raw_behavior_data - extension: bins \ No newline at end of file + extension: bins diff --git a/iblrig/device_descriptions/sync/nidq.yaml b/iblrig/device_descriptions/sync/nidq.yaml index 2650cb77a..a055dc974 100644 --- a/iblrig/device_descriptions/sync/nidq.yaml +++ b/iblrig/device_descriptions/sync/nidq.yaml @@ -2,4 +2,4 @@ sync: nidq: collection: raw_ephys_data extension: bin - acquisition_software: spikeglx \ No newline at end of file + acquisition_software: spikeglx diff --git a/iblrig/gui/wizard.py b/iblrig/gui/wizard.py index 19a21b7f5..93b897560 100644 --- a/iblrig/gui/wizard.py +++ b/iblrig/gui/wizard.py @@ -8,6 +8,7 @@ import yaml import traceback import webbrowser +from random import choice from PyQt5 import QtWidgets, QtCore, uic from PyQt5.QtWidgets import QStyle @@ -173,16 +174,21 @@ def closeEvent(self, event): def check_for_update(self): update_available, remote_version = check_for_updates() if update_available == 1: + cmdBox = QtWidgets.QLineEdit('upgrade_iblrig') + cmdBox.setReadOnly(True) msgBox = QtWidgets.QMessageBox(parent=self) msgBox.setWindowTitle("Update Notice") - msgBox.setText(f"Update toiblrig {remote_version} is available.") - msgBox.setInformativeText("Please update using 'git pull'.") + msgBox.setText(f"Update to iblrig {remote_version} is available.\n\n" + f"Please update iblrig by issuing:") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.setIcon(QtWidgets.QMessageBox().Information) - msgBox.findChild(QtWidgets.QPushButton).setText('Yes, I promise!') + msgBox.layout().addWidget(cmdBox, 1, 2) + msgBox.findChild(QtWidgets.QPushButton).setText( + choice(['Yes, I promise!', 'I will do so immediately!', + 'Straight away!', 'Of course I will!'])) msgBox.exec_() self.setDisabled(False) - self.statusbar.showMessage(f"iblrig v{remote_version}") + self.statusbar.showMessage(f"iblrig v{iblrig.__version__}") self.update() def model2view(self): @@ -262,6 +268,7 @@ def startstop(self): cmd.append('--wizard') if self.uiCheckAppend.isChecked(): cmd.append('--append') + cmd.append('--wizard') if self.running_task_process is None: self.running_task_process = subprocess.Popen(cmd) self.uiPushStart.setText('Stop') diff --git a/iblrig/misc.py b/iblrig/misc.py index c09b7b342..27037945a 100644 --- a/iblrig/misc.py +++ b/iblrig/misc.py @@ -46,7 +46,8 @@ def _get_task_argument_parser(parents=None): help="long description of what is occurring, something like 'Ephys recording with acute probe(s)'; " "be sure to use the double quote characters to encapsulate the description and a space to separate " "multiple procedures") - parser.add_argument('-w', '--weight', type=float, dest='subject_weight_grams', required=False, default=None) + parser.add_argument('-w', '--weight', type=float, dest='subject_weight_grams', + required=False, default=None) parser.add_argument('--no-interactive', dest='interactive', action='store_false', default=True) parser.add_argument('--append', dest='append', action='store_true', default=False) parser.add_argument('--stub', type=Path, help="Path to _ibl_experiment.description.yaml stub file.") diff --git a/iblrig/version_management.py b/iblrig/version_management.py index 730702098..05e0463b2 100644 --- a/iblrig/version_management.py +++ b/iblrig/version_management.py @@ -2,6 +2,7 @@ from pathlib import Path from re import sub from subprocess import check_output, check_call, SubprocessError +import sys import iblrig from iblutil.util import setup_logger @@ -12,35 +13,92 @@ def check_for_updates(): log.info('Checking for updates ...') - # assert that git is being used - dir_base = Path(iblrig.__file__).parents[1] - if not dir_base.joinpath('.git').exists(): - log.debug('iblrig does not seem to be managed through git') - return -1, '' - - # get newest remote tag try: - branch = check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"], - cwd=dir_base) - branch = sub(r'\n', '', branch.decode()) - check_call(["git", "fetch", "origin", branch, "-q"], cwd=dir_base, timeout=5) - version_remote_str = check_output(["git", "describe", "--tags", "--abbrev=0"], - cwd=dir_base) - version_remote_str = sub(r'[^\d\.]', '', version_remote_str.decode()) - except (SubprocessError, FileNotFoundError): - log.debug('Could not fetch remote tags') + v_local = version.parse(iblrig.__version__) + except (version.InvalidVersion, TypeError): + log.debug('Could not parse local version string') return -1, '' - # parse version information - try: - version_local = version.parse(iblrig.__version__) - version_remote = version.parse(version_remote_str) - except version.InvalidVersion: - log.debug('Invalid version string') + v_remote = Remote().version() + if v_remote is None: + log.debug('Could not parse remote version string') return -1, '' - if version_remote > version_local: - log.info(f'Update to iblrig {version_remote_str} found.') + if v_remote > v_local: + log.info(f'Update to iblrig {v_remote} found.') else: log.info('No update found.') - return version_remote > version_local, version_remote_str if version_remote > version_local else version_local + return v_remote > v_local, str(v_remote) + + +def update_available(): + version_local = version.parse(iblrig.__version__) + version_remote = version.parse(Remote().version_str()) + return version_remote > version_local + + +class Remote(object): + _version = None + + @staticmethod + def version(): + + if Remote._version: + return Remote._version + + if not is_git(): + return None + try: + dir_base = Path(iblrig.__file__).parents[1] + branch = check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"], + cwd=dir_base) + branch = sub(r'\n', '', branch.decode()) + check_call(["git", "fetch", "origin", branch, "-q"], + cwd=dir_base, timeout=5) + version_str = check_output(["git", "describe", "--tags", "--abbrev=0"], + cwd=dir_base) + except (SubprocessError, FileNotFoundError): + return None + + version_str = sub(r'[^\d\.]', '', version_str.decode()) + try: + Remote._version = version.parse(version_str) + return Remote._version + except (version.InvalidVersion, TypeError): + return None + + +def is_git(): + return Path(iblrig.__file__).parents[1].joinpath('.git').exists() + + +def upgrade(): + if not is_git(): + raise Exception('This installation of IBLRIG is not managed through git.') + if sys.base_prefix == sys.prefix: + raise Exception('You need to be in the IBLRIG venv in order to upgrade.') + if not Remote.version(): + raise Exception('Could not obtain remote version.') + + local_version = version.parse(iblrig.__version__) + remote_version = Remote.version() + + print(f'Local version: {local_version}') + print(f'Remote version: {remote_version}') + + if local_version >= remote_version: + print('No need to upgrade.') + return 0 + + if iblrig.__version__.endswith('+dirty'): + print('There are changes in your local copy of IBLRIG that will be lost when ' + 'upgrading.') + while True: + user_input = input('Do you want to proceed? [y, N] ') + if user_input.lower() in ['n', 'no', '']: + return + if user_input.lower() in ['y', 'yes']: + check_call([sys.executable, "-m", "pip", "reset", "--hard"]) + break + + check_call([sys.executable, "-m", "pip", "install", "-U", "-e", "."]) diff --git a/iblrig_tasks/_iblrig_tasks_advancedChoiceWorld/task_parameters.yaml b/iblrig_tasks/_iblrig_tasks_advancedChoiceWorld/task_parameters.yaml index c2333162d..13fe5537b 100644 --- a/iblrig_tasks/_iblrig_tasks_advancedChoiceWorld/task_parameters.yaml +++ b/iblrig_tasks/_iblrig_tasks_advancedChoiceWorld/task_parameters.yaml @@ -1,2 +1,2 @@ 'CONTRAST_SET': [1.0, 0.5, 0.25, 0.125, 0.0625, 0.0] -'CONTRAST_SET_PROBABILITY_TYPE': skew_zero # uniform, skew_zero \ No newline at end of file +'CONTRAST_SET_PROBABILITY_TYPE': skew_zero # uniform, skew_zero diff --git a/iblrig_tasks/_iblrig_tasks_spontaneous/task_parameters.yaml b/iblrig_tasks/_iblrig_tasks_spontaneous/task_parameters.yaml index edf191bbe..92a83150d 100644 --- a/iblrig_tasks/_iblrig_tasks_spontaneous/task_parameters.yaml +++ b/iblrig_tasks/_iblrig_tasks_spontaneous/task_parameters.yaml @@ -1 +1 @@ -'POOP_COUNT': true \ No newline at end of file +'POOP_COUNT': true diff --git a/iblrig_tasks/_iblrig_tasks_trainingChoiceWorld/task_parameters.yaml b/iblrig_tasks/_iblrig_tasks_trainingChoiceWorld/task_parameters.yaml index 653eb7627..2ebeeacd2 100644 --- a/iblrig_tasks/_iblrig_tasks_trainingChoiceWorld/task_parameters.yaml +++ b/iblrig_tasks/_iblrig_tasks_trainingChoiceWorld/task_parameters.yaml @@ -3,4 +3,4 @@ 'AG_MIN_VALUE': 4.0 # (azimuth_degree/mm) 'CONTRAST_SET': [1.0, 0.5, 0.25, 0.125, 0.0625, 0.0] 'CONTRAST_SET_PROBABILITY_TYPE': skew_zero # uniform, skew_zero -'DEBIAS': True # Whether to use debiasing rule or not by repeating error trials \ No newline at end of file +'DEBIAS': True # Whether to use debiasing rule or not by repeating error trials diff --git a/iblrig_tasks/_iblrig_tasks_trainingPhaseChoiceWorld/task_parameters.yaml b/iblrig_tasks/_iblrig_tasks_trainingPhaseChoiceWorld/task_parameters.yaml index de81fddbb..2311e8713 100644 --- a/iblrig_tasks/_iblrig_tasks_trainingPhaseChoiceWorld/task_parameters.yaml +++ b/iblrig_tasks/_iblrig_tasks_trainingPhaseChoiceWorld/task_parameters.yaml @@ -4,4 +4,4 @@ 'CONTRAST_SET': [1.0, 0.5, 0.25, 0.125, 0.0625, 0.0] 'CONTRAST_SET_PROBABILITY_TYPE': skew_zero # uniform, skew_zero 'DEBIAS': True # Whether to use debiasing rule or not by repeating error trials -'TRAINING_PHASE': 0 \ No newline at end of file +'TRAINING_PHASE': 0 diff --git a/pybpod_fixtures/IBL/IBL.json b/pybpod_fixtures/IBL/IBL.json index 598ecd2aa..fa2df2174 100644 --- a/pybpod_fixtures/IBL/IBL.json +++ b/pybpod_fixtures/IBL/IBL.json @@ -6,4 +6,4 @@ "__UPDATED-ON__": "2019-09-24 16:16:31.383939", "__UUID4__": "160e30f3-de99-47ab-9554-93ba04508da3", "name": "IBL" -} \ No newline at end of file +} diff --git a/pybpod_fixtures/IBL/boards/_iblrig_testlab_behavior_1/_iblrig_testlab_behavior_1.json b/pybpod_fixtures/IBL/boards/_iblrig_testlab_behavior_1/_iblrig_testlab_behavior_1.json index f27c6548a..762dfb7e1 100644 --- a/pybpod_fixtures/IBL/boards/_iblrig_testlab_behavior_1/_iblrig_testlab_behavior_1.json +++ b/pybpod_fixtures/IBL/boards/_iblrig_testlab_behavior_1/_iblrig_testlab_behavior_1.json @@ -18,4 +18,4 @@ "enabled-wiredports": null, "net-port": 36000, "serial-port": "COM12" -} \ No newline at end of file +} diff --git a/pybpod_fixtures/IBL/subjects/_iblrig_calibration/_iblrig_calibration.json b/pybpod_fixtures/IBL/subjects/_iblrig_calibration/_iblrig_calibration.json index d3da4da21..fe24e731a 100644 --- a/pybpod_fixtures/IBL/subjects/_iblrig_calibration/_iblrig_calibration.json +++ b/pybpod_fixtures/IBL/subjects/_iblrig_calibration/_iblrig_calibration.json @@ -6,4 +6,4 @@ "__UPDATED-ON__": "2022-10-10 10:56:29.007339", "__UUID4__": "c57ab029-6f9c-4f62-ad89-7dcce8cde91a", "setup": "None" -} \ No newline at end of file +} diff --git a/pybpod_fixtures/IBL/subjects/_iblrig_test_mouse/_iblrig_test_mouse.json b/pybpod_fixtures/IBL/subjects/_iblrig_test_mouse/_iblrig_test_mouse.json index b98b3cdb6..50df2759b 100644 --- a/pybpod_fixtures/IBL/subjects/_iblrig_test_mouse/_iblrig_test_mouse.json +++ b/pybpod_fixtures/IBL/subjects/_iblrig_test_mouse/_iblrig_test_mouse.json @@ -6,4 +6,4 @@ "__UPDATED-ON__": "2022-10-10 10:56:29.055075", "__UUID4__": "dcb676f0-ce8c-4be8-a501-804169c7416b", "setup": "None" -} \ No newline at end of file +} diff --git a/pybpod_fixtures/IBL/users/_iblrig_test_user/_iblrig_test_user.json b/pybpod_fixtures/IBL/users/_iblrig_test_user/_iblrig_test_user.json index 29ed9dfc6..82c635efe 100644 --- a/pybpod_fixtures/IBL/users/_iblrig_test_user/_iblrig_test_user.json +++ b/pybpod_fixtures/IBL/users/_iblrig_test_user/_iblrig_test_user.json @@ -5,4 +5,4 @@ "__SOFTWARE__": "PyBpod GUI API v1.2.2", "__UPDATED-ON__": "2019-09-24 16:16:31.456241", "__UUID4__": "180b8647-ddb1-4e7f-85a3-0831ad928878" -} \ No newline at end of file +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..00b7f219f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,69 @@ +[build-system] +requires = [ + "setuptools>=64", + "setuptools_scm[toml]>=7.1", + "wheel" +] +build-backend = "setuptools.build_meta" + +[project] +name = "iblrig" +description = "The International Brain Laboratory's decision making task" +dynamic = ["version", "readme"] +keywords = ["IBL", "neuro-science"] +requires-python = "~=3.10" +license = {file = "LICENSE"} +dependencies = [ + "colorlog", + "cython", + "graphviz", + "ibllib@git+https://github.com/int-brain-lab/ibllib.git@iblrigv8", + "ipython", + "iblpybpod@git+https://github.com/int-brain-lab/iblpybpod.git@no-gui", + "iblscripts@git+https://github.com/int-brain-lab/iblscripts.git@iblrigv8", + "project_extraction@git+https://github.com/int-brain-lab/project_extraction.git", + "numpy", + "ONE-api", + "packaging", + "pandas", + "pyOpenSSL", + "python-dateutil", + "python-osc", + "pywin32; sys_platform == 'win32'", + "PySocks", + "PyYAML", + "scipy", + "setuptools_scm", + "sounddevice", +] + +[project.optional-dependencies] +DEV = [ + "coverage", + "myst-parser", + "pylint", + "sphinx", + "sphinx-autobuild", + "sphinx_lesson", + "sphinx_rtd_theme", + "pre-commit" +] + +[project.scripts] +viewsession = "iblrig.commands:viewsession" +transfer_data = "iblrig.commands:transfer_data" +transfer_video_data = "iblrig.commands:transfer_video_data" +flush = "iblrig.commands:flush" +remove-old-sessions = "iblrig.commands:remove_local_sessions" +iblrig = "iblrig.gui.wizard:main" +upgrade_iblrig = "iblrig.version_management:upgrade" + +[tool.setuptools.dynamic] +readme = {file = "README.md", content-type = "text/markdown"} + +[tool.setuptools_scm] +version_scheme = "post-release" +local_scheme = "dirty-tag" + +[tool.setuptools.packages] +find = {} diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index f410dd4b9..000000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,7 +0,0 @@ -coverage -myst-parser -pylint -sphinx -sphinx-autobuild -sphinx_lesson -sphinx_rtd_theme \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index c668bdd11..000000000 --- a/requirements.txt +++ /dev/null @@ -1,20 +0,0 @@ -colorlog -cython -graphviz -git+https://github.com/int-brain-lab/ibllib.git@iblrigv8 -ipython -git+https://github.com/int-brain-lab/iblpybpod.git@no-gui -git+https://github.com/int-brain-lab/iblscripts.git@iblrigv8 -git+https://github.com/int-brain-lab/project_extraction.git -numpy -ONE-api -packaging -pandas -pyOpenSSL -python-dateutil -python-osc -pywin32; sys_platform == "win32" -PySocks -PyYAML -scipy -sounddevice diff --git a/setup.py b/setup.py deleted file mode 100644 index a4c83ec74..000000000 --- a/setup.py +++ /dev/null @@ -1,50 +0,0 @@ -from pathlib import Path - -from setuptools import setup, find_packages - -PYTHON_REQUIRES = ">=3.10" # python version requirement -LONG_DESCRIPTION = "Software used to interact with various pieces of specialized hardware for neuroscience data acquisition." - -with open("requirements.txt") as f: - require = [x.strip() for x in f.readlines() if not x.startswith("git+")] - - -def read(rel_path): - here = Path(__file__).parent.absolute() - with open(here.joinpath(rel_path), "r") as fp: - return fp.read() - - -def get_version(rel_path): - for line in read(rel_path).splitlines(): - if line.startswith("__version__"): - delim = '"' if '"' in line else "'" - return line.split(delim)[1] - else: - raise RuntimeError("Unable to find version string.") - - -setup( - name="iblrig", - version=get_version(Path("iblrig").joinpath("__init__.py")), - python_requires=PYTHON_REQUIRES, - description="IBL libraries", - license="MIT", - long_description=LONG_DESCRIPTION, - author="IBL Staff", - url="https://www.internationalbrainlab.com/", - packages=find_packages(exclude=["scratch"]), # same as name - # external packages as dependencies - install_requires=require, - scripts=[], - entry_points={ - 'console_scripts': [ - 'viewsession=iblrig.commands:viewsession', - 'transfer_data=iblrig.commands:transfer_data', - 'transfer_video_data=iblrig.commands:transfer_video_data', - 'flush=iblrig.commands:flush', - 'remove-old-sessions=iblrig.commands:remove_local_sessions', - 'iblrig=iblrig.gui.wizard:main', - ], - }, -)