diff --git a/.gitignore b/.gitignore index 099e2948..5d267208 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,8 @@ target/ # Ipython Notebook .ipynb_checkpoints + +# Offline local-install bundle artifacts (never commit wheels or installers) +local-install/whls/ +local-install/*.exe +pytentiostat-env/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e4a84d1..4f4600e2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ ci: submodules: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v6.0.0 hooks: - id: check-yaml - id: end-of-file-fixer @@ -21,45 +21,46 @@ repos: - id: check-toml - id: check-added-large-files - repo: https://github.com/psf/black - rev: 24.4.2 + rev: 26.3.1 hooks: - id: black - repo: https://github.com/pycqa/flake8 - rev: 7.0.0 + rev: 7.3.0 hooks: - id: flake8 - repo: https://github.com/pycqa/isort - rev: 5.13.2 + rev: 9.0.0a3 hooks: - id: isort args: ["--profile", "black"] - repo: https://github.com/kynan/nbstripout - rev: 0.7.1 + rev: 0.9.1 hooks: - id: nbstripout - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v6.0.0 hooks: - id: no-commit-to-branch name: Prevent Commit to Main Branch args: ["--branch", "main"] stages: [pre-commit] - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.2 hooks: - id: codespell additional_dependencies: - tomli # prettier - multi formatter for .json, .yml, and .md files - repo: https://github.com/pre-commit/mirrors-prettier - rev: f12edd9c7be1c20cfa42420fd0e6df71e42b51ea # frozen: v4.0.0-alpha.8 + rev: v4.0.0-alpha.8 hooks: - id: prettier additional_dependencies: - "prettier@^3.2.4" + types: [yaml, markdown, json] # docformatter - PEP 257 compliant docstring formatter - - repo: https://github.com/s-weigand/docformatter - rev: 5757c5190d95e5449f102ace83df92e7d3b06c6c + - repo: https://github.com/PyCQA/docformatter + rev: v1.7.8 hooks: - id: docformatter additional_dependencies: [tomli] diff --git a/local-install/instructions.rst b/local-install/instructions.rst index fa9ffd02..1534d008 100644 --- a/local-install/instructions.rst +++ b/local-install/instructions.rst @@ -1,23 +1,54 @@ -.. instructions for creating a USB that can install pytentiostat +.. instructions for creating a USB that can install pytentiostat offline ============================================================================= -Instructions for installing pytentiostat onto a *Windows* computer with a USB +Installing pytentiostat onto an offline *Windows* computer with a USB ============================================================================= -- First, copy the entire top level directory onto a USB flash drive. -- **Important:** Download the Python 3.8 installer and required wheel files onto the USB. \ - Copy the installer to the folder 'local_install' and the wheels to its subdirectory, \ - 'whls'. - - **Note:** To obtain a copy of the installer and wheel files, contact the \ - development team through Github or email. (https://github.com/juami/pytentiostat) -- Next, plug in the flash drive to the desired computer and copy \ - pytentiostat onto the computer. -- Then find the file called 'install_from_local' in the scripts folder \ - and double click it. -- Follow the prompts on the screen to install python 3.8. Be sure to select \ - 'add Python to PATH' at the bottom of the first screen. -- It will take a couple of minutes to install all the packages. If everything \ - was successful, you will see no errors and the bottom of the command window \ - will say 'press any key to continue...' -- Refer to the main documentation in the 'docs' folder to start running experiments. +These instructions create a USB flash drive that can install pytentiostat on a +lab computer that has **no internet access**. The wheels and the Python +installer are *not* stored in the repository -- you download them once on an +internet-connected machine using the helper script below. + +Part 1 -- prepare the USB (on a machine **with** internet) +---------------------------------------------------------- + +#. Install Python 3.12 and copy (or ``git clone``) this repository onto the + machine. +#. From the top level of the repository, build the offline bundle:: + + python scripts/build_local_install.py + + This builds the pytentiostat wheel and downloads every dependency wheel + into ``local-install/whls``. + + **Note:** for the most reliable result, run this on the same operating + system and Python version as the target computer (Windows + Python 3.12 for + a typical lab machine). To build for a different platform, pass + ``--platform win_amd64 --python-version 3.12``. + +#. Download a Windows Python 3.12 installer from + https://www.python.org/downloads/windows/ and place the ``python-3.12*.exe`` + file in the ``local-install`` folder. The installer script launches it + automatically if Python is missing on the target machine. +#. Copy the entire repository directory onto the USB flash drive. + +Part 2 -- install pytentiostat (on the **offline** machine) +----------------------------------------------------------- + +#. Plug in the USB flash drive and copy the pytentiostat directory onto the + computer. +#. Open the ``scripts`` folder and double-click ``install_from_local.bat``. +#. If Python is not already installed, the bundled installer launches first -- + be sure to tick **"Add Python to PATH"** on the first screen. (If Python was + just installed, close the window, open a new terminal, and run the script + again so the updated ``PATH`` takes effect.) +#. The script creates a ``pytentiostat-env`` virtual environment and installs + pytentiostat and its dependencies from the bundled wheels. When it finishes + you will see a success message. +#. Activate the environment and launch the program:: + + pytentiostat-env\Scripts\activate + pytentiostat + + Refer to the main documentation in the ``docs`` folder to start running + experiments. diff --git a/news/install-script.rst b/news/install-script.rst new file mode 100644 index 00000000..f2254295 --- /dev/null +++ b/news/install-script.rst @@ -0,0 +1,29 @@ +**Added:** + +* ``scripts/build_local_install.py`` to build an offline install bundle + (pytentiostat wheel + dependency wheels) on an internet-connected machine + for use on an air-gapped lab computer. + +**Changed:** + +* Rewrote ``scripts/install_from_local.bat`` to install pytentiostat offline + from the bundled wheels into a virtual environment via + ``pip install --no-index --find-links`` (Python 3.12, ``pip`` instead of the + removed ``setup.py install``), and updated ``local-install/instructions.rst`` + to match. + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/scripts/build_local_install.py b/scripts/build_local_install.py new file mode 100644 index 00000000..140889e1 --- /dev/null +++ b/scripts/build_local_install.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +"""Build an offline install bundle for pytentiostat. + +Run this on a machine **with** internet access to populate +``local-install/whls`` with the pytentiostat wheel and all of its +dependencies. The whole repository directory (now containing the wheels and, +optionally, a bundled Python installer) can then be copied to a USB flash +drive and used to install pytentiostat on an **offline** machine via +``scripts/install_from_local.bat``. + +For the most reliable result, run this on the same OS/Python as the target +machine (Windows + Python 3.12 for a typical lab computer). To download wheels +for a *different* platform than the one you are on, pass ``--platform`` and +``--python-version`` (this forces wheel-only downloads). + +Examples +-------- +Build for the current machine:: + + python scripts/build_local_install.py + +Build Windows wheels for Python 3.12 from another OS by adding:: + + --platform win_amd64 --python-version 3.12 +""" + +import argparse +import subprocess +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +DEFAULT_DEST = REPO_ROOT / "local-install" / "whls" +DEFAULT_REQUIREMENTS = REPO_ROOT / "requirements" / "pip.txt" + +# A recent Windows installer URL is printed as a reminder; the installer +# itself is intentionally not committed to the repository. +PYTHON_INSTALLER_HINT = ( + "https://www.python.org/downloads/windows/ " + "(pick a 3.12.x 'Windows installer (64-bit)')" +) + + +def _run(cmd): + """Run a subprocess command, echoing it first, and raise on + failure.""" + print(" $ " + " ".join(str(part) for part in cmd)) + subprocess.run(cmd, check=True) + + +def build_wheel(dest): + """Build the pytentiostat wheel (without dependencies) into + ``dest``.""" + print("Building the pytentiostat wheel...") + _run( + [ + sys.executable, + "-m", + "pip", + "wheel", + str(REPO_ROOT), + "--no-deps", + "--wheel-dir", + str(dest), + ] + ) + + +def download_dependencies(dest, requirements, platform, python_version): + """Download all runtime dependency wheels into ``dest``.""" + print("Downloading dependency wheels...") + cmd = [ + sys.executable, + "-m", + "pip", + "download", + "--requirement", + str(requirements), + "--dest", + str(dest), + ] + # Cross-platform downloads require pip to fetch pre-built wheels only. + if platform: + cmd += ["--only-binary=:all:", "--platform", platform] + if python_version: + cmd += ["--python-version", python_version] + _run(cmd) + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--dest", + type=Path, + default=DEFAULT_DEST, + help="Directory to write wheels into (default: local-install/whls).", + ) + parser.add_argument( + "--requirements", + type=Path, + default=DEFAULT_REQUIREMENTS, + help="Requirements file to download (default: requirements/pip.txt).", + ) + parser.add_argument( + "--platform", + default=None, + help="Target wheel platform, e.g. win_amd64. Omit to use this " + "machine's platform.", + ) + parser.add_argument( + "--python-version", + default=None, + help="Target Python version, e.g. 3.12. Omit to use this " + "interpreter's version.", + ) + args = parser.parse_args(argv) + + dest = args.dest.resolve() + dest.mkdir(parents=True, exist_ok=True) + + build_wheel(dest) + download_dependencies( + dest, args.requirements, args.platform, args.python_version + ) + + wheels = sorted(p.name for p in dest.glob("*.whl")) + archives = sorted(p.name for p in dest.glob("*.tar.gz")) + print() + print(f"Done. {len(wheels) + len(archives)} package(s) staged in {dest}") + print() + print("Next steps to finish the USB bundle:") + print( + " 1. Download a Windows Python installer into 'local-install/'\n" + f" {PYTHON_INSTALLER_HINT}" + ) + print(" 2. Copy the whole repository directory onto the USB flash drive.") + print( + " 3. On the offline machine, run " "'scripts/install_from_local.bat'." + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/install_from_local.bat b/scripts/install_from_local.bat index d80d891f..982e0029 100644 --- a/scripts/install_from_local.bat +++ b/scripts/install_from_local.bat @@ -1,45 +1,90 @@ -:: This batch script will install python 3.8, pytentiostat and it's necessary packages +:: Offline installer for pytentiostat. +:: +:: Run this on the target (offline) Windows machine after the USB flash drive +:: has been prepared on an internet-connected machine with: +:: python scripts/build_local_install.py +:: It installs Python (from a bundled installer if needed), creates a virtual +:: environment, and installs pytentiostat plus its dependencies from the +:: bundled wheels in local-install/whls -- no internet access required. @echo off -cd ../local_install -echo Installing python -START /WAIT python-3.8.1-amd64 -python --version -if errorlevel 1 goto NoPython -pip install whls/pytz-2019.3-py2.py3-none-any.whl -pip install whls/kiwisolver-1.1.0-cp38-none-win_amd64.whl -pip install whls/six-1.14.0-py2.py3-none-any.whl -pip install whls/pyparsing-2.4.6-py2.py3-none-any.whl -pip install whls/python_dateutil-2.8.1-py2.py3-none-any.whl -pip install whls/pyserial-3.4-py2.py3-none-any.whl -pip install whls/pyFirmata-1.1.0-py2.py3-none-any.whl -pip install whls/numpy-1.18.1-cp38-cp38-win_amd64.whl -pip install whls/pandas-0.25.3-cp38-cp38-win_amd64.whl -pip install whls/PyYAML-5.3-cp38-cp38-win_amd64.whl -pip install whls/urllib3-1.25.8-py2.py3-none-any.whl -pip install whls/py-1.8.1-py2.py3-none-any.whl -pip install whls/idna-2.8-py2.py3-none-any.whl -pip install whls/chardet-3.0.4-py2.py3-none-any.whl -pip install whls/certifi-2019.11.28-py2.py3-none-any.whl -pip install whls/pluggy-0.13.1-py2.py3-none-any.whl -pip install whls/packaging-20.0-py2.py3-none-any.whl -pip install whls/more_itertools-8.1.0-py3-none-any.whl -pip install whls/wcwidth-0.1.8-py2.py3-none-any.whl -pip install whls/atomicwrites-1.3.0-py2.py3-none-any.whl -pip install whls/attrs-19.3.0-py2.py3-none-any.whl -pip install whls/cycler-0.10.0-py2.py3-none-any.whl -pip install whls/matplotlib-3.1.2-cp38-cp38-win_amd64.whl -pip install whls/coverage-5.0.3.tar.gz -pip install whls/colorama-0.4.3-py2.py3-none-any.whl -pip install whls/requests-2.22.0-py2.py3-none-any.whl -pip install whls/pytest-5.3.4-py3-none-any.whl -pip install whls/mock-3.0.5-py2.py3-none-any.whl -pip install whls/codecov-2.0.15-py2.py3-none-any.whl -cd .. -python setup.py install -@pause -goto:eof - -:NoPython -echo Python installation failed. Please retry. -@pause -goto:eof +setlocal enabledelayedexpansion + +set "HERE=%~dp0" +set "WHLS=%HERE%..\local-install\whls" +set "VENV=%HERE%..\pytentiostat-env" + +echo ================================================== +echo Installing pytentiostat (offline) +echo ================================================== + +:: 1. Make sure Python is available; offer the bundled installer if not. +python --version >nul 2>&1 +if not errorlevel 1 goto have_python + +echo Python was not found on PATH. +set "PYEXE=" +for %%f in ("%HERE%..\local-install\python-*.exe") do set "PYEXE=%%f" +if not defined PYEXE goto no_python +echo Launching bundled Python installer: !PYEXE! +echo IMPORTANT: tick "Add Python to PATH" on the first screen. +start /wait "" "!PYEXE!" + +python --version >nul 2>&1 +if errorlevel 1 goto path_refresh + +:have_python +:: 2. Check that the wheels were staged onto the USB. +if not exist "%WHLS%" goto no_wheels + +:: 3. Create an isolated virtual environment for pytentiostat. +echo Creating virtual environment at "%VENV%"... +python -m venv "%VENV%" +if errorlevel 1 goto venv_failed + +:: 4. Install pytentiostat and all dependencies offline from local wheels. +echo Installing pytentiostat and dependencies from local wheels... +"%VENV%\Scripts\python" -m pip install --no-index --find-links "%WHLS%" pytentiostat +if errorlevel 1 goto install_failed + +echo. +echo ================================================== +echo Success! pytentiostat is installed. +echo Activate the environment with: +echo "%VENV%\Scripts\activate" +echo then run: pytentiostat +echo ================================================== +pause +goto end + +:no_python +echo No bundled Python installer (local-install\python-*.exe) was found. +echo Please install Python 3.12 manually, then re-run this script. +pause +goto end + +:path_refresh +echo Python was installed but is not on PATH yet. +echo Close this window, open a new terminal, and run this script again. +pause +goto end + +:no_wheels +echo Could not find wheels at "%WHLS%". +echo Run "python scripts\build_local_install.py" on an internet-connected +echo machine first to populate the USB, then try again. +pause +goto end + +:venv_failed +echo Failed to create the virtual environment. +pause +goto end + +:install_failed +echo Offline install failed. Check that the wheels match this machine's +echo Python version and architecture. +pause +goto end + +:end +endlocal diff --git a/src/GUI/code/GUI_CA_exp_creator.py b/src/GUI/code/GUI_CA_exp_creator.py index 9bf4f45f..c175caac 100644 --- a/src/GUI/code/GUI_CA_exp_creator.py +++ b/src/GUI/code/GUI_CA_exp_creator.py @@ -20,7 +20,6 @@ def load_folder(CA): ------ CA: the Ui_CA object """ - CA.experiment_file_path.setText( CA.load_folder_name() ) # CA.load_folder_name() returns folder path name @@ -230,7 +229,6 @@ def config_writer_CA( """ - config_path_name = os.path.join( data_out_path, data_out_name + "_" + exp_type + "_config.yml" ) @@ -269,7 +267,6 @@ def check(x): ------ boolean: True if x is a valid number, else False """ - if "-" in x: x = x.lstrip("-") if "." in x: @@ -337,7 +334,6 @@ def write_to_file_CA(ui, CA, CA_window, AP_params): AP_parameters: list that contains the advanced parameters """ - config_params = get_parameters(CA, "CA", AP_params) data_out_name = config_params[3] data_out_path = config_params[4] @@ -420,7 +416,6 @@ def CA_main(ui, config_data=None): ------ CA: the Ui_ca object """ - CA, CA_window = ( ui.show_CAwindow() ) # Initialize the CA window - CA window pops up diff --git a/src/GUI/code/GUI_CV_exp_creator.py b/src/GUI/code/GUI_CV_exp_creator.py index 693d1247..0ccbcc09 100644 --- a/src/GUI/code/GUI_CV_exp_creator.py +++ b/src/GUI/code/GUI_CV_exp_creator.py @@ -479,7 +479,6 @@ def CV_main(ui, config_data=None): ------ CA: the Ui_ca object """ - CV, CV_window = ui.show_CVwindow() grid = QGridLayout() CV.plot_area.setLayout(grid) diff --git a/src/GUI/code/GUI_LSV_exp_creator.py b/src/GUI/code/GUI_LSV_exp_creator.py index 35898248..60fb2020 100644 --- a/src/GUI/code/GUI_LSV_exp_creator.py +++ b/src/GUI/code/GUI_LSV_exp_creator.py @@ -394,7 +394,6 @@ def LSV_main(ui, config_data=None): ------ CA: the Ui_ca object """ - LSV, LSV_window = ui.show_LSVwindow() grid = QGridLayout() LSV.plot_area.setLayout(grid) diff --git a/src/GUI/code/GUI_file_operation.py b/src/GUI/code/GUI_file_operation.py index 20ab88b3..e134b9c0 100644 --- a/src/GUI/code/GUI_file_operation.py +++ b/src/GUI/code/GUI_file_operation.py @@ -30,8 +30,9 @@ def add_exp(ui): def load_file(ui): - """Initializes the 'Load config file' window. Once loaded, the - filename will show up in the experiment queue window. + """Initializes the 'Load config file' window. + + Once loaded, the filename will show up in the experiment queue window. Parameters ------ @@ -64,7 +65,6 @@ def edit_file(ui): ui: the Ui_mainwindow object Instance is created in the main.py """ - if ui.experiment_queue.currentItem(): filename = ui.experiment_queue.currentItem().data(3) config_data = parse_config_file(filename) diff --git a/src/GUI/code/GUI_operator.py b/src/GUI/code/GUI_operator.py index 06f1469e..673ab418 100644 --- a/src/GUI/code/GUI_operator.py +++ b/src/GUI/code/GUI_operator.py @@ -23,7 +23,6 @@ def start_exp(d9, normalized_start, data): ------ start_time: float , Starting time of the experiment """ - d9.write(normalized_start) rest_time = cr.get_rest(data) time.sleep(rest_time) @@ -258,7 +257,6 @@ def experiment( currents : list , List of floats containing the corrected currents at each data point """ - # Constants for every experiment ( conversion_factor, diff --git a/src/GUI/code/main.py b/src/GUI/code/main.py index 15516159..e21ad3de 100644 --- a/src/GUI/code/main.py +++ b/src/GUI/code/main.py @@ -36,7 +36,7 @@ def find_port(): def disconnect_port(): - global com, board, d9 + global com com = None disconnect_port_main(ui, board, d9) diff --git a/src/GUI/code/warning_GUI.py b/src/GUI/code/warning_GUI.py index 7a0201a8..7484d1c4 100644 --- a/src/GUI/code/warning_GUI.py +++ b/src/GUI/code/warning_GUI.py @@ -12,7 +12,6 @@ def warning(text): ------ Boolean : True if 'OK' is clicked, False if 'cancel'. """ - msg = QtWidgets.QMessageBox() icon = QtGui.QIcon() icon.addPixmap( diff --git a/src/pytentiostat/config_reader.py b/src/pytentiostat/config_reader.py index 722fe846..238dbd82 100644 --- a/src/pytentiostat/config_reader.py +++ b/src/pytentiostat/config_reader.py @@ -159,26 +159,24 @@ def param_checker(config_data): for i in [data_out_name, data_out_path]: val = i["value"] if not isinstance(val, str): - sys.exit( - """Warning! \nThe value {value} for {name} in - config.yml is not valid. \nPlease enter a - new value for {name} avoiding unusual characters. - \nExiting...""".format( - **i - ) - ) + message = """Warning! + + \nThe value {value} for {name} in config.yml is not + valid. \nPlease enter a new value for {name} + avoiding unusual characters. \nExiting... + """ + sys.exit(message.format(**i)) for i in [average_number, cycle_number, step_number]: val = i["value"] if not isinstance(val, int): - sys.exit( - """Warning! \nThe value {value} for - {name} in config.yml is not valid. - \nPlease change the entry to a positive integer. - \nExiting...""".format( - **i - ) - ) + message = """Warning! + + \nThe value {value} for {name} in config.yml is not + valid. \nPlease change the entry to a positive + integer. \nExiting... + """ + sys.exit(message.format(**i)) for i in [ conversion_factor, @@ -199,14 +197,13 @@ def param_checker(config_data): ]: val = i["value"] if not isinstance(val, (float, int)): - sys.exit( - """Warning! \nThe value {value} for - {name} in config.yml is not valid. - \nPlease change the entry to a number. - \nExiting...""".format( - **i - ) - ) + message = """Warning! + + \nThe value {value} for {name} in config.yml is not + valid. \nPlease change the entry to a number. + \nExiting... + """ + sys.exit(message.format(**i)) for i in [ rest_time, @@ -223,26 +220,23 @@ def param_checker(config_data): ]: val = i["value"] if val <= 0: - sys.exit( - """Warning! \nThe value {value} for - {name} in config.yml is not valid. - \nPlease change the entry to a value greater than - or equal to zero. - \nExiting...""".format( - **i - ) - ) + message = """Warning! + + \nThe value {value} for {name} in config.yml is not + valid. \nPlease change the entry to a value greater + than or equal to zero. \nExiting... + """ + sys.exit(message.format(**i)) exp_types = ["LSV", "CV", "CA"] val = exp_type["value"] if val not in exp_types: - sys.exit( - """Warning! \nThe entry {value} for {name} is not valid. - \nPlease change the entry to CA, CV, or LSV. - \nExiting...""".format( - **exp_type - ) - ) + message = """Warning! + + \nThe entry {value} for {name} is not valid. \nPlease + change the entry to CA, CV, or LSV. \nExiting... + """ + sys.exit(message.format(**exp_type)) voltage_ub = 2.2 voltage_lb = -2.2 @@ -270,11 +264,13 @@ def param_checker(config_data): time_for_range = exp_time["value"] if time_for_range == 0: - sys.exit( - """Warning! \nThe total time for range in config.yml in not valid. - \nPlease enter a new value greater than 0 and try again. - \nExiting...""" - ) + message = """Warning! + + \nThe total time for range in config.yml in not valid. + \nPlease enter a new value greater than 0 and try again. + \nExiting... + """ + sys.exit(message) lag_tolerance = 2 step_number_ub = int(1 / (lag_tolerance * time_per_step / time_for_range)) @@ -289,32 +285,29 @@ def param_checker(config_data): ]: val = i["value"] if val < voltage_lb or val > voltage_ub: - sys.exit( - """Warning! \nVoltages in config.yml should be < {} and > {}. - \n Please change value for {name} to value - between the bounds. - \nExiting...""".format( - str(voltage_ub), str(voltage_lb), **i - ) - ) + message = """Warning! + + \nVoltages in config.yml should be < {} and > {}. \n + Please change value for {name} to value between the + bounds. \nExiting... + """ + sys.exit(message.format(str(voltage_ub), str(voltage_lb), **i)) if time_step["value"] < time_step_lb: - sys.exit( - """Warning! \nTime step value in config.yml must be >= {}. - \nPlease change the time step value to be greater than this. - \nExiting...""".format( - str(time_step_lb) - ) - ) + message = """Warning! + + \nTime step value in config.yml must be >= {}. \nPlease + change the time step value to be greater than this. + \nExiting... + """ + sys.exit(message.format(str(time_step_lb))) if step_number["value"] > step_number_ub: - sys.exit( - """Warning! \nStep number must be <= {} given the other - input parameters. - \nPlease change the step number in config.yml to be - less than this. - \nExiting...""".format( - str(step_number_ub) - ) - ) + message = """Warning! + + \nStep number must be <= {} given the other input + parameters. \nPlease change the step number in + config.yml to be less than this. \nExiting... + """ + sys.exit(message.format(str(step_number_ub))) def get_output_params(config_data, override_ts=None): @@ -398,7 +391,6 @@ def get_adv_params(adv_config_data): shunt_resistor = adv_config_data["advanced_parameters"]["shunt_resistor"] time_step = adv_config_data["advanced_parameters"]["time_step"] average_number = adv_config_data["advanced_parameters"]["average_number"] - return ( conversion_factor, set_gain, diff --git a/src/pytentiostat/operator.py b/src/pytentiostat/operator.py index 44d6bcfd..35fff811 100644 --- a/src/pytentiostat/operator.py +++ b/src/pytentiostat/operator.py @@ -120,7 +120,7 @@ def read_write( ------- None """ - global Interrupt, Exp_running + global Exp_running voltage_offset = 0.5 Exp_running = True start_time = time.time() diff --git a/src/pytentiostat/reporter.py b/src/pytentiostat/reporter.py index cb9a62b7..62627a0d 100644 --- a/src/pytentiostat/reporter.py +++ b/src/pytentiostat/reporter.py @@ -27,7 +27,6 @@ def save_data_to_file( ------- nothing """ - filename, export_path = get_output_params( config_data, override_ts=override_ts ) diff --git a/src/pytentiostat/routines.py b/src/pytentiostat/routines.py index a493b500..b076be4f 100644 --- a/src/pytentiostat/routines.py +++ b/src/pytentiostat/routines.py @@ -39,8 +39,9 @@ def _load_arduino(): def _initialize_arduino(com): - """Creates board object with Arduino(). If the connection fails it - prints an error message and exits. + """Creates board object with Arduino(). + + If the connection fails it prints an error message and exits. Parameters ---------- @@ -70,7 +71,6 @@ def startup_routine(*, simulate: bool = False): a2 : location of analog read pin 2 d9 : location of digital pwm pin 9 """ - print("Welcome to the JUAMI pytentiostat interface!") if simulate: print("Simulation mode enabled. No hardware will be used.\n") @@ -92,8 +92,9 @@ def startup_routine(*, simulate: bool = False): def closing_routine(board, d9): - """Called after experiment is finished. Function brings the - potential back to 0 V and closes the board object. + """Called after experiment is finished. + + Function brings the potential back to 0 V and closes the board object. Parameters ---------- diff --git a/src/pytentiostat/simulator.py b/src/pytentiostat/simulator.py index 404deb08..81fcdb48 100644 --- a/src/pytentiostat/simulator.py +++ b/src/pytentiostat/simulator.py @@ -171,7 +171,6 @@ def simulated_startup( ) -> tuple[str, SimulatedBoard, object, object, object]: """Drop-in replacement for routines.startup_routine() for simulation mode.""" - sim = SimulatedPotentiostat( conversion_factor=conversion_factor, shunt_resistor=shunt_resistor,