From cfe4eb26c1f5fa0b8dd0b992e01b63441fcac2a5 Mon Sep 17 00:00:00 2001 From: Joseph McKinsey <31461013+josephmckinsey@users.noreply.github.com> Date: Fri, 25 Oct 2024 08:52:03 -0600 Subject: [PATCH] Split web server into separate package (#98) * Split web server into separate package - Eliminated npm part in setup.py commands - Created separate package TODO - rename to helics_cli_extras - move observer stuff to helics_cli_extras - move testing if necessary - setup ci/cd and PyPI package * Run formatter * Add CI for web * Move folder * Rename in setup.py * Rename in cli.py and improve test * Stop duplication of push and pull request * Move observer and database into cli_extras * Fix imports on observer and change ci-web to ci-cli.yml * .github/workflows/ci-cli.yml * Rename and create cd * Add NPM and environment name * Fix cd-cli-extras.yml * Run on workflow dispatch * Fix typo in helics_cli_extras/pyproject.toml Co-authored-by: Ryan Mast <3969255+nightlark@users.noreply.github.com> * Remove pypi package 'install' * Add import check for helics_cli_extras. Direct to helics[cli] * Remove sqlalchemy, pandas, and requests * Make cd-cli-extras happen only on workflow dispatch (will require manual version updates) --------- Co-authored-by: Joseph McKinsey Co-authored-by: Ryan Mast <3969255+nightlark@users.noreply.github.com> --- .github/workflows/cd-cli-extras.yml | 74 +++++++ .github/workflows/cd.yml | 5 +- .github/workflows/ci-cli-extras.yml | 55 +++++ .github/workflows/ci.yml | 10 +- .github/workflows/docs.yml | 2 +- helics/cli.py | 122 ++++++++--- helics_cli_extras/.gitignore | 1 + .../client}/.eslintrc.cjs | 0 .../client}/.gitignore | 0 {client => helics_cli_extras/client}/.npmrc | 0 .../client}/.prettierrc | 0 .../client}/README.md | 0 .../client}/package-lock.json | 0 .../client}/package.json | 0 .../client}/playwright.config.js | 0 .../client}/postcss.config.cjs | 0 .../client}/src/app.css | 0 .../client}/src/app.d.ts | 0 .../client}/src/app.html | 0 .../client}/src/lib/BrokerLayout.svelte | 0 .../client}/src/lib/DataFlowGraph.svelte | 0 .../client}/src/lib/Navbar.svelte | 0 .../client}/src/lib/ProfilePlot.svelte | 0 .../client}/src/lib/Switch.svelte | 0 .../client}/src/lib/Table.svelte | 0 .../client}/src/lib/Topology.svelte | 0 .../client}/src/lib/stores.ts | 0 .../client}/src/routes/__layout.svelte | 0 .../client}/src/routes/broker.svelte | 0 .../client}/src/routes/index.svelte | 0 .../client}/src/routes/observe.svelte | 0 .../client}/src/routes/profile.svelte | 0 .../client}/src/routes/run.svelte | 0 .../client}/static/favicon.png | Bin .../client}/svelte.config.js | 0 .../client}/tailwind.config.cjs | 0 .../client}/tests/test.js | 0 .../client}/tsconfig.json | 0 .../helics_cli_extras/__init__.py | 2 + .../helics_cli_extras}/database.py | 11 +- .../helics_cli_extras/flask_app.py | 94 +++++--- .../helics_cli_extras}/observer.py | 56 ++++- helics_cli_extras/pyproject.toml | 66 ++++++ setup.py | 200 ++++++------------ 44 files changed, 486 insertions(+), 212 deletions(-) create mode 100644 .github/workflows/cd-cli-extras.yml create mode 100644 .github/workflows/ci-cli-extras.yml create mode 100644 helics_cli_extras/.gitignore rename {client => helics_cli_extras/client}/.eslintrc.cjs (100%) rename {client => helics_cli_extras/client}/.gitignore (100%) rename {client => helics_cli_extras/client}/.npmrc (100%) rename {client => helics_cli_extras/client}/.prettierrc (100%) rename {client => helics_cli_extras/client}/README.md (100%) rename {client => helics_cli_extras/client}/package-lock.json (100%) rename {client => helics_cli_extras/client}/package.json (100%) rename {client => helics_cli_extras/client}/playwright.config.js (100%) rename {client => helics_cli_extras/client}/postcss.config.cjs (100%) rename {client => helics_cli_extras/client}/src/app.css (100%) rename {client => helics_cli_extras/client}/src/app.d.ts (100%) rename {client => helics_cli_extras/client}/src/app.html (100%) rename {client => helics_cli_extras/client}/src/lib/BrokerLayout.svelte (100%) rename {client => helics_cli_extras/client}/src/lib/DataFlowGraph.svelte (100%) rename {client => helics_cli_extras/client}/src/lib/Navbar.svelte (100%) rename {client => helics_cli_extras/client}/src/lib/ProfilePlot.svelte (100%) rename {client => helics_cli_extras/client}/src/lib/Switch.svelte (100%) rename {client => helics_cli_extras/client}/src/lib/Table.svelte (100%) rename {client => helics_cli_extras/client}/src/lib/Topology.svelte (100%) rename {client => helics_cli_extras/client}/src/lib/stores.ts (100%) rename {client => helics_cli_extras/client}/src/routes/__layout.svelte (100%) rename {client => helics_cli_extras/client}/src/routes/broker.svelte (100%) rename {client => helics_cli_extras/client}/src/routes/index.svelte (100%) rename {client => helics_cli_extras/client}/src/routes/observe.svelte (100%) rename {client => helics_cli_extras/client}/src/routes/profile.svelte (100%) rename {client => helics_cli_extras/client}/src/routes/run.svelte (100%) rename {client => helics_cli_extras/client}/static/favicon.png (100%) rename {client => helics_cli_extras/client}/svelte.config.js (100%) rename {client => helics_cli_extras/client}/tailwind.config.cjs (100%) rename {client => helics_cli_extras/client}/tests/test.js (100%) rename {client => helics_cli_extras/client}/tsconfig.json (100%) create mode 100644 helics_cli_extras/helics_cli_extras/__init__.py rename {helics => helics_cli_extras/helics_cli_extras}/database.py (95%) rename helics/flaskr/__init__.py => helics_cli_extras/helics_cli_extras/flask_app.py (87%) rename {helics => helics_cli_extras/helics_cli_extras}/observer.py (78%) create mode 100644 helics_cli_extras/pyproject.toml diff --git a/.github/workflows/cd-cli-extras.yml b/.github/workflows/cd-cli-extras.yml new file mode 100644 index 00000000..f7070dd4 --- /dev/null +++ b/.github/workflows/cd-cli-extras.yml @@ -0,0 +1,74 @@ +name: CD - CLI Extras + +on: + push: + branches: + - main + workflow_dispatch: + inputs: + publish_to_pypi: + description: 'Publish to TestPyPI and PyPI' + required: true + type: boolean + default: false + +jobs: + build-wheels: + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + + - name: Set up Python3 + uses: actions/setup-python@v5 + with: + python-version: 3.8 + + - name: Install python3 dependencies + run: | + python -m pip install -U pip wheel setuptools cffi build + - name: Build cli_extras wheel + run: | + cd helics_cli_extras/client + npm install + npm run build + cp -r build ../helics_cli_extras/static + cd .. + python -m build + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: python-cli-extras-dist + path: helics_cli_extras/dist/* + + publish-helics: + needs: [build-wheels] + runs-on: ubuntu-latest + environment: + name: pypi-cli-extras + url: https://pypi.org/p/helics-cli-extras + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: + - name: Get the built packages + uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: helics_cli_extras/dist + + - name: Publish package to TestPyPI + if: ${{ github.event.inputs.publish_to_pypi == true }} + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.TEST_PYPI_PASSWORD }} + repository-url: https://test.pypi.org/legacy/ + packages-dir: helics_cli_extras/dist + + - name: Publish package to PyPI + if: ${{ github.event.inputs.publish_to_pypi == true }} + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: helics_cli_extras/dist \ No newline at end of file diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index e998fa51..673fd783 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -6,6 +6,7 @@ on: - main tags: - v* + workflow_dispatch: jobs: build-wheels: @@ -37,7 +38,7 @@ jobs: - name: Install python3 dependencies run: | - python -m pip install -U pip install wheel setuptools cffi + python -m pip install -U pip wheel setuptools cffi - name: Download helics library run: python setup.py download --plat-name=${{ matrix.target }} @@ -97,7 +98,7 @@ jobs: with: user: __token__ password: ${{ secrets.TEST_PYPI_PASSWORD }} - repository_url: https://test.pypi.org/legacy/ + repository-url: https://test.pypi.org/legacy/ - name: Publish package to PyPI if: startsWith(github.ref, 'refs/tags/') diff --git a/.github/workflows/ci-cli-extras.yml b/.github/workflows/ci-cli-extras.yml new file mode 100644 index 00000000..562f829f --- /dev/null +++ b/.github/workflows/ci-cli-extras.yml @@ -0,0 +1,55 @@ +name: CI - CLI + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + python-version: ["3.9"] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install -U pip wheel setuptools cffi + - name: Build NPM + run: | + cd helics_cli_extras/client + npm install + npm run build + cp -r build ../helics_cli_extras/static + - name: Build the web interface first + run: | + cd helics_cli_extras + pip install . + - name: Download helics library and run pip install + run: | + python setup.py download + python setup.py build_ext + pip install -e ".[cli]" + - name: Run Server + run: | + helics server & + sleep 5 + curl http://localhost:5000 + kill %+ + - name: Run Observer + run: | + mkdir db + helics observer & + sleep 5 + kill %+ \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35d56545..c88eb0b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,12 @@ name: CI -on: [push, pull_request] +on: + push: + branches: + - main + pull_request: + branches: + - main jobs: test: @@ -19,7 +25,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - python -m pip install -U pip install wheel setuptools cffi + python -m pip install -U pip wheel setuptools cffi - name: Download helics library and run pip install run: | python setup.py download diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ef426d2c..6769293f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,7 +16,7 @@ jobs: python-version: "3.12" - name: Install dependencies run: | - python -m pip install -U pip install wheel setuptools + python -m pip install -U pip wheel setuptools python setup.py download pip install -e ".[cli,docs]" - name: Copy README.md diff --git a/helics/cli.py b/helics/cli.py index ab6bf127..9ef72f84 100644 --- a/helics/cli.py +++ b/helics/cli.py @@ -3,6 +3,7 @@ HELICS command line interface """ +from dataclasses import dataclass import json import os import io @@ -13,7 +14,7 @@ import urllib.request import logging from ._version import __version__ -from .status_checker import CheckStatusThread, HELICSRuntimeError +from .status_checker import CheckStatusThread import click import pathlib @@ -31,7 +32,9 @@ def _get_version(): try: import helics as h - helics_version = "Python HELICS version {}\n\nHELICS Library version {}".format(h.__version__, h.helicsGetVersion()) + helics_version = "Python HELICS version {}\n\nHELICS Library version {}".format( + h.__version__, h.helicsGetVersion() + ) except ImportError: helics_version = "Python `helics` package not installed. Install using `pip install helics --upgrade`." try: @@ -47,28 +50,25 @@ def _get_version(): import helics_apps as ha if not h.helicsGetVersion().startswith(ha.__version__.strip("v")): - echo("`helics` and `helics-apps` versions don't match. You may want to run `pip install helics helics-apps --upgrade`.") + echo( + "`helics` and `helics-apps` versions don't match. You may want to run `pip install helics helics-apps --upgrade`." + ) except ImportError: pass try: - import flask + import helics_cli_extras except ImportError: - echo('helics-cli\'s web interface is not installed. You may want to run `pip install "helics[cli]"`.') - - try: - import sqlalchemy - except ImportError: - echo('helics-cli\'s observer functionality is not installed. You may want to run `pip install "helics[cli]"`.') + echo( + 'helics_cli_extras is not installed. You may want to run `pip install "helics[cli]"`.' + ) return """{} {} {} -""".format( - __version__, helics_version, helics_apps_version - ).strip() +""".format(__version__, helics_version, helics_apps_version).strip() VERSION = _get_version() @@ -99,20 +99,37 @@ def server(open: bool): Run helics web server to access web interface """ import webbrowser - from . import flaskr + + try: + import helics_cli_extras + except ImportError: + error( + 'helics_cli_extras is not installed. You may want to run `pip install "helics[cli]"`.' + ) if open: webbrowser.open("http://127.0.0.1:5000", 1) - flaskr.run() + helics_cli_extras.run() @cli.command() -@click.option("--db-folder", prompt="path to database folder", type=click.Path(exists=True, file_okay=False, writable=True, path_type=pathlib.Path)) +@click.option( + "--db-folder", + prompt="path to database folder", + type=click.Path( + exists=True, file_okay=False, writable=True, path_type=pathlib.Path + ), +) def observer(db_folder: pathlib.Path): """ Run helics observer and write data to sqlite file """ - from .observer import HelicsObserverFederate + try: + from helics_cli_extras import HelicsObserverFederate + except ImportError: + error( + 'helics-cli\'s observer functionality is not installed. You may want to run `pip install "helics[cli]"`.' + ) o = HelicsObserverFederate(folder=db_folder) o.run() @@ -131,7 +148,14 @@ def observer(db_folder: pathlib.Path): default=True, help="Invert plot", ) -@click.option("--save", prompt=True, prompt_required=False, type=click.Path(), default=None, help="Path to save the plot") +@click.option( + "--save", + prompt=True, + prompt_required=False, + type=click.Path(), + default=None, + help="Path to save the plot", +) def profile_plot(path, save, invert): """ Plot profiler output using matplotlib @@ -141,9 +165,6 @@ def profile_plot(path, save, invert): p.plot(p.profile(path, invert), save=save, kind="realtime") -from dataclasses import dataclass - - @dataclass class Job: name: str @@ -164,7 +185,9 @@ def fetch(url, data={}, method="POST"): r.add_header("Content-Length", str(len(bytes))) try: with urllib.request.urlopen(r, bytes) as response: - return json.loads(response.read().decode(response.info().get_param("charset") or "utf-8")) + return json.loads( + response.read().decode(response.info().get_param("charset") or "utf-8") + ) except Exception as e: logger.exception("Unable to post to helics-cli server: {}".format(e)) @@ -179,7 +202,12 @@ def fetch(url, data={}, method="POST"): @click.option("--silent", is_flag=True) @click.option("--connect-server", is_flag=True) @click.option("--no-log-files", is_flag=True, default=False) -@click.option("--no-kill-on-error", is_flag=True, default=False, help="Do not kill all federates on error") +@click.option( + "--no-kill-on-error", + is_flag=True, + default=False, + help="Do not kill all federates on error", +) def run(path, silent, connect_server, no_log_files, no_kill_on_error): """ Run HELICS federation @@ -192,7 +220,12 @@ def run(path, silent, connect_server, no_log_files, no_kill_on_error): if connect_server: with urllib.request.urlopen(r) as response: helics_server_available = ( - json.loads(response.read().decode(response.info().get_param("charset") or "utf-8")).get("status", None) == 200 + json.loads( + response.read().decode( + response.info().get_param("charset") or "utf-8" + ) + ).get("status", None) + == 200 ) except Exception: warn("Unable to connect to helics-cli web server") @@ -205,7 +238,9 @@ def run(path, silent, connect_server, no_log_files, no_kill_on_error): if not os.path.exists(path_to_config): info( - "Unable to find file `config.json` in path: {path_to_config}".format(path_to_config=path_to_config), + "Unable to find file `config.json` in path: {path_to_config}".format( + path_to_config=path_to_config + ), ) return None @@ -218,17 +253,28 @@ def run(path, silent, connect_server, no_log_files, no_kill_on_error): if "broker" in config.keys() and config["broker"] is not False: if not silent: info( - "Adding auto broker (i.e. `helics_broker -f{f}`) to helics-cli subprocesses.".format(f=len(config["federates"])), + "Adding auto broker (i.e. `helics_broker -f{f}`) to helics-cli subprocesses.".format( + f=len(config["federates"]) + ), blink=True, ) config["federates"].append( - {"directory": ".", "exec": "helics_broker -f{}".format(len(config["federates"])), "host": "localhost", "name": "broker"} + { + "directory": ".", + "exec": "helics_broker -f{}".format(len(config["federates"])), + "host": "localhost", + "name": "broker", + } ) names = [c["name"] for c in config["federates"]] if len(set(n for n in names)) != len(config["federates"]): error("Repeated names found in runner.json federates.", blink=True) - for n, c in [(item, count) for item, count in collections.Counter(names).items() if count > 1]: + for n, c in [ + (item, count) + for item, count in collections.Counter(names).items() + if count > 1 + ]: info('Found name "{}" {} times'.format(n, c)) return -1 @@ -241,7 +287,9 @@ def run(path, silent, connect_server, no_log_files, no_kill_on_error): for f in config["federates"]: if not silent: info( - "Running federate {name} as a background process".format(name=f["name"]), + "Running federate {name} as a background process".format( + name=f["name"] + ), ) fname = os.path.abspath(os.path.join(path, "{}.log".format(f["name"]))) @@ -300,8 +348,16 @@ def run(path, silent, connect_server, no_log_files, no_kill_on_error): finally: for p in process_list: t.status(p) - if p.process.returncode != 0 and p.process.returncode != -9 and p.process.returncode is not None: - error("Process {} exited with return code {}".format(p.name, p.process.returncode)) + if ( + p.process.returncode != 0 + and p.process.returncode != -9 + and p.process.returncode is not None + ): + error( + "Process {} exited with return code {}".format( + p.name, p.process.returncode + ) + ) if os.path.exists(p.file): with open(p.file) as f: warn("Last 10 lines of {}.log:".format(p.name), blink=False) @@ -351,7 +407,9 @@ def list_brokers(): else: cmd = "ps aux" - p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + p = subprocess.Popen( + shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True + ) p.wait() out, _ = p.communicate() for line in out.decode("utf-8").splitlines(): diff --git a/helics_cli_extras/.gitignore b/helics_cli_extras/.gitignore new file mode 100644 index 00000000..5781fe7b --- /dev/null +++ b/helics_cli_extras/.gitignore @@ -0,0 +1 @@ +helics_cli_extras/static diff --git a/client/.eslintrc.cjs b/helics_cli_extras/client/.eslintrc.cjs similarity index 100% rename from client/.eslintrc.cjs rename to helics_cli_extras/client/.eslintrc.cjs diff --git a/client/.gitignore b/helics_cli_extras/client/.gitignore similarity index 100% rename from client/.gitignore rename to helics_cli_extras/client/.gitignore diff --git a/client/.npmrc b/helics_cli_extras/client/.npmrc similarity index 100% rename from client/.npmrc rename to helics_cli_extras/client/.npmrc diff --git a/client/.prettierrc b/helics_cli_extras/client/.prettierrc similarity index 100% rename from client/.prettierrc rename to helics_cli_extras/client/.prettierrc diff --git a/client/README.md b/helics_cli_extras/client/README.md similarity index 100% rename from client/README.md rename to helics_cli_extras/client/README.md diff --git a/client/package-lock.json b/helics_cli_extras/client/package-lock.json similarity index 100% rename from client/package-lock.json rename to helics_cli_extras/client/package-lock.json diff --git a/client/package.json b/helics_cli_extras/client/package.json similarity index 100% rename from client/package.json rename to helics_cli_extras/client/package.json diff --git a/client/playwright.config.js b/helics_cli_extras/client/playwright.config.js similarity index 100% rename from client/playwright.config.js rename to helics_cli_extras/client/playwright.config.js diff --git a/client/postcss.config.cjs b/helics_cli_extras/client/postcss.config.cjs similarity index 100% rename from client/postcss.config.cjs rename to helics_cli_extras/client/postcss.config.cjs diff --git a/client/src/app.css b/helics_cli_extras/client/src/app.css similarity index 100% rename from client/src/app.css rename to helics_cli_extras/client/src/app.css diff --git a/client/src/app.d.ts b/helics_cli_extras/client/src/app.d.ts similarity index 100% rename from client/src/app.d.ts rename to helics_cli_extras/client/src/app.d.ts diff --git a/client/src/app.html b/helics_cli_extras/client/src/app.html similarity index 100% rename from client/src/app.html rename to helics_cli_extras/client/src/app.html diff --git a/client/src/lib/BrokerLayout.svelte b/helics_cli_extras/client/src/lib/BrokerLayout.svelte similarity index 100% rename from client/src/lib/BrokerLayout.svelte rename to helics_cli_extras/client/src/lib/BrokerLayout.svelte diff --git a/client/src/lib/DataFlowGraph.svelte b/helics_cli_extras/client/src/lib/DataFlowGraph.svelte similarity index 100% rename from client/src/lib/DataFlowGraph.svelte rename to helics_cli_extras/client/src/lib/DataFlowGraph.svelte diff --git a/client/src/lib/Navbar.svelte b/helics_cli_extras/client/src/lib/Navbar.svelte similarity index 100% rename from client/src/lib/Navbar.svelte rename to helics_cli_extras/client/src/lib/Navbar.svelte diff --git a/client/src/lib/ProfilePlot.svelte b/helics_cli_extras/client/src/lib/ProfilePlot.svelte similarity index 100% rename from client/src/lib/ProfilePlot.svelte rename to helics_cli_extras/client/src/lib/ProfilePlot.svelte diff --git a/client/src/lib/Switch.svelte b/helics_cli_extras/client/src/lib/Switch.svelte similarity index 100% rename from client/src/lib/Switch.svelte rename to helics_cli_extras/client/src/lib/Switch.svelte diff --git a/client/src/lib/Table.svelte b/helics_cli_extras/client/src/lib/Table.svelte similarity index 100% rename from client/src/lib/Table.svelte rename to helics_cli_extras/client/src/lib/Table.svelte diff --git a/client/src/lib/Topology.svelte b/helics_cli_extras/client/src/lib/Topology.svelte similarity index 100% rename from client/src/lib/Topology.svelte rename to helics_cli_extras/client/src/lib/Topology.svelte diff --git a/client/src/lib/stores.ts b/helics_cli_extras/client/src/lib/stores.ts similarity index 100% rename from client/src/lib/stores.ts rename to helics_cli_extras/client/src/lib/stores.ts diff --git a/client/src/routes/__layout.svelte b/helics_cli_extras/client/src/routes/__layout.svelte similarity index 100% rename from client/src/routes/__layout.svelte rename to helics_cli_extras/client/src/routes/__layout.svelte diff --git a/client/src/routes/broker.svelte b/helics_cli_extras/client/src/routes/broker.svelte similarity index 100% rename from client/src/routes/broker.svelte rename to helics_cli_extras/client/src/routes/broker.svelte diff --git a/client/src/routes/index.svelte b/helics_cli_extras/client/src/routes/index.svelte similarity index 100% rename from client/src/routes/index.svelte rename to helics_cli_extras/client/src/routes/index.svelte diff --git a/client/src/routes/observe.svelte b/helics_cli_extras/client/src/routes/observe.svelte similarity index 100% rename from client/src/routes/observe.svelte rename to helics_cli_extras/client/src/routes/observe.svelte diff --git a/client/src/routes/profile.svelte b/helics_cli_extras/client/src/routes/profile.svelte similarity index 100% rename from client/src/routes/profile.svelte rename to helics_cli_extras/client/src/routes/profile.svelte diff --git a/client/src/routes/run.svelte b/helics_cli_extras/client/src/routes/run.svelte similarity index 100% rename from client/src/routes/run.svelte rename to helics_cli_extras/client/src/routes/run.svelte diff --git a/client/static/favicon.png b/helics_cli_extras/client/static/favicon.png similarity index 100% rename from client/static/favicon.png rename to helics_cli_extras/client/static/favicon.png diff --git a/client/svelte.config.js b/helics_cli_extras/client/svelte.config.js similarity index 100% rename from client/svelte.config.js rename to helics_cli_extras/client/svelte.config.js diff --git a/client/tailwind.config.cjs b/helics_cli_extras/client/tailwind.config.cjs similarity index 100% rename from client/tailwind.config.cjs rename to helics_cli_extras/client/tailwind.config.cjs diff --git a/client/tests/test.js b/helics_cli_extras/client/tests/test.js similarity index 100% rename from client/tests/test.js rename to helics_cli_extras/client/tests/test.js diff --git a/client/tsconfig.json b/helics_cli_extras/client/tsconfig.json similarity index 100% rename from client/tsconfig.json rename to helics_cli_extras/client/tsconfig.json diff --git a/helics_cli_extras/helics_cli_extras/__init__.py b/helics_cli_extras/helics_cli_extras/__init__.py new file mode 100644 index 00000000..62e038e0 --- /dev/null +++ b/helics_cli_extras/helics_cli_extras/__init__.py @@ -0,0 +1,2 @@ +from .flask_app import run +from .observer import HelicsObserverFederate diff --git a/helics/database.py b/helics_cli_extras/helics_cli_extras/database.py similarity index 95% rename from helics/database.py rename to helics_cli_extras/helics_cli_extras/database.py index 1e701784..3915c211 100644 --- a/helics/database.py +++ b/helics_cli_extras/helics_cli_extras/database.py @@ -1,7 +1,14 @@ # -*- coding: utf-8 -*- -from sqlalchemy import Column, Integer, Float, String, JSON, Sequence, Table, Boolean, create_engine +from sqlalchemy import ( + Column, + Integer, + Float, + String, + JSON, + Sequence, + Boolean, +) from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import Session Base = declarative_base() diff --git a/helics/flaskr/__init__.py b/helics_cli_extras/helics_cli_extras/flask_app.py similarity index 87% rename from helics/flaskr/__init__.py rename to helics_cli_extras/helics_cli_extras/flask_app.py index 2a066f07..1800b684 100644 --- a/helics/flaskr/__init__.py +++ b/helics_cli_extras/helics_cli_extras/flask_app.py @@ -1,15 +1,13 @@ # -*- coding: utf-8 -*- -import logging import json import time import os import shlex import subprocess import sys -from dataclasses import dataclass from typing import cast -from flask import Flask, render_template, send_from_directory, request, jsonify +from flask import Flask, send_from_directory from flask_restful import Resource, Api, reqparse, abort from flask_cors import CORS import sqlalchemy as sa @@ -17,16 +15,21 @@ import werkzeug import re - -from .. import database as db +from . import database as db current_directory = os.path.realpath(os.path.dirname(__file__)) -app = Flask(__name__.split(".")[0], static_url_path="", static_folder=os.path.join(current_directory, "../static")) +app = Flask( + __name__.split(".")[0], + static_url_path="", + static_folder=os.path.join(current_directory, "static"), +) api = Api(app) CORS(app, resources={r"/api/*": {"origins": "*"}}) -app.config["UPLOAD_FOLDER"] = os.path.abspath(os.path.join(os.getcwd(), "__helics-server")) +app.config["UPLOAD_FOLDER"] = os.path.abspath( + os.path.join(os.getcwd(), "__helics-server") +) cache = { "path": os.path.join(app.config["UPLOAD_FOLDER"], "helics-cli.sqlite.db"), @@ -54,12 +57,16 @@ def get(self): def post(self): parser = reqparse.RequestParser() - parser.add_argument("file", type=werkzeug.datastructures.FileStorage, location="files") + parser.add_argument( + "file", type=werkzeug.datastructures.FileStorage, location="files" + ) args = parser.parse_args() file = args["file"] os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) file.save(os.path.join(app.config["UPLOAD_FOLDER"], "helics-cli.sqlite.db")) - cache["path"] = os.path.join(app.config["UPLOAD_FOLDER"], "helics-cli.sqlite.db") + cache["path"] = os.path.join( + app.config["UPLOAD_FOLDER"], "helics-cli.sqlite.db" + ) dm = DatabaseManager() return {"filename": dm.path_to_helics_db} @@ -80,7 +87,11 @@ class Cores(Resource): def get(self): dm = DatabaseManager() cores = dm.session.query(db.Cores).all() - return [{"id": e.id, "name": e.name, "address": e.address} for e in cores if not e.name.startswith("__observer__")] + return [ + {"id": e.id, "name": e.name, "address": e.address} + for e in cores + if not e.name.startswith("__observer__") + ] api.add_resource(Cores, "/api/observer/cores") @@ -90,7 +101,11 @@ class Federates(Resource): def get(self): dm = DatabaseManager() federates = dm.session.query(db.Federates).all() - return [{"id": f.id, "name": f.name, "parent": f.parent} for f in federates if f.name != "__observer__"] + return [ + {"id": f.id, "name": f.name, "parent": f.parent} + for f in federates + if f.name != "__observer__" + ] api.add_resource(Federates, "/api/observer/federates") @@ -161,13 +176,22 @@ def get(self): data["filename"] = cache["runner-file-name"] if data.get("broker", False) is True: data["federates"].append( - {"directory": ".", "exec": "helics_broker -f{}".format(len(data["federates"])), "host": "localhost", "name": "broker"} + { + "directory": ".", + "exec": "helics_broker -f{}".format(len(data["federates"])), + "host": "localhost", + "name": "broker", + } ) for federate in data["federates"]: if federate["directory"].startswith("."): - federate["directory"] = os.path.abspath(os.path.join(data["folder"], federate["directory"])) + federate["directory"] = os.path.abspath( + os.path.join(data["folder"], federate["directory"]) + ) federate["old_name"] = federate["name"] - if os.path.exists(os.path.join(data["folder"], "{}.log".format(federate["name"]))): + if os.path.exists( + os.path.join(data["folder"], "{}.log".format(federate["name"])) + ): federate["log_available"] = True else: federate["log_available"] = False @@ -180,7 +204,9 @@ def get(self): def post(self): parser = reqparse.RequestParser() - parser.add_argument("file", type=werkzeug.datastructures.FileStorage, location="files") + parser.add_argument( + "file", type=werkzeug.datastructures.FileStorage, location="files" + ) args = parser.parse_args() file = args["file"] path = cache["runner-folder"] @@ -200,7 +226,9 @@ def post(self): args = parser.parse_args() name = args["name"] cache["runner-file-name"] = name - cache["runner-path"] = os.path.join(cache["runner-folder"], cache["runner-file-name"]) + cache["runner-path"] = os.path.join( + cache["runner-folder"], cache["runner-file-name"] + ) api.add_resource(RunnerFileName, "/api/runner/file/name") @@ -213,7 +241,9 @@ def post(self): args = parser.parse_args() folder = args["folder"] cache["runner-folder"] = os.path.abspath(os.path.expanduser(folder)) - cache["runner-path"] = os.path.join(cache["runner-folder"], cache["runner-file-name"]) + cache["runner-path"] = os.path.join( + cache["runner-folder"], cache["runner-file-name"] + ) api.add_resource(RunnerFileFolder, "/api/runner/file/folder") @@ -320,7 +350,9 @@ class RunnerLog(Resource): def get(self, name): with open(cache["runner-path"]) as f: data = json.loads(f.read()) - with open(os.path.join(os.path.dirname(cache["runner-path"]), "{}.log".format(name))) as f: + with open( + os.path.join(os.path.dirname(cache["runner-path"]), "{}.log".format(name)) + ) as f: data = f.read() return {"log": data} @@ -340,7 +372,11 @@ def get(self): def post(self): if self.get()["status"]: self.delete() - p = subprocess.Popen(shlex.split("helics run --path {} --connect-server".format(cache["runner-path"]))) + p = subprocess.Popen( + shlex.split( + "helics run --path {} --connect-server".format(cache["runner-path"]) + ) + ) self.runner_server["process"] = p return {"status": True} @@ -396,7 +432,9 @@ def get(self): def post(self): parser = reqparse.RequestParser() parser.add_argument("name", type=str, required=True, help="Name of federate") - parser.add_argument("status", type=str, required=True, help="Status of federate") + parser.add_argument( + "status", type=str, required=True, help="Status of federate" + ) args = parser.parse_args() status_tracker[args["name"]] = args["status"] return {"status": status_tracker[args["name"]]} @@ -414,7 +452,6 @@ def get(self): class Profile(Resource): - # SenderFederate2[131074](initializing)HELICS CODE ENTRY<4570827706580384>[t=-1000000] PATTERN = re.compile( r""" @@ -466,7 +503,9 @@ def get(self): for name in set(names): profile[name].append({}) - for (name, state, message, simtime, realtime) in zip(names, states, messages, simtimes, realtimes): + for name, state, message, simtime, realtime in zip( + names, states, messages, simtimes, realtimes + ): if state == "created": continue if "ENTRY" in message and not invert: @@ -499,7 +538,9 @@ def get(self): def post(self): parser = reqparse.RequestParser() - parser.add_argument("file", type=werkzeug.datastructures.FileStorage, location="files") + parser.add_argument( + "file", type=werkzeug.datastructures.FileStorage, location="files" + ) args = parser.parse_args() file = args["file"] os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) @@ -539,7 +580,9 @@ def get(self): def post(self): parser = reqparse.RequestParser() - parser.add_argument("status", type=bool, required=True, help="requested status of broker server") + parser.add_argument( + "status", type=bool, required=True, help="requested status of broker server" + ) args = parser.parse_args() status = args["status"] if status is True and self.broker_server.get("process", None) is not None: @@ -570,7 +613,7 @@ def post(self): @app.route("/", defaults={"path": "index.html"}) @app.route("/") def index(path): - return send_from_directory(os.path.join(current_directory, "..", "static"), path) + return send_from_directory(os.path.join(current_directory, "static"), path) def run(): @@ -581,5 +624,4 @@ def run(): host = "0.0.0.0" cli = sys.modules["flask.cli"] cli.show_server_banner = lambda *x: None - # os.environ["WERKZEUG_RUN_MAIN"] = "true" app.run(host=host, debug=debug) diff --git a/helics/observer.py b/helics_cli_extras/helics_cli_extras/observer.py similarity index 78% rename from helics/observer.py rename to helics_cli_extras/helics_cli_extras/observer.py index 6635f46a..1eb5e30c 100644 --- a/helics/observer.py +++ b/helics_cli_extras/helics_cli_extras/observer.py @@ -7,9 +7,12 @@ from typing import Dict, List, cast from datetime import datetime +# HELICS is a runtime dependency here. import helics as h from . import database as db +from sqlalchemy import create_engine +from sqlalchemy.orm import Session logger = logging.getLogger(__name__) hdlr = logging.StreamHandler() @@ -37,9 +40,11 @@ def _setup_engine(self): db_file = os.path.abspath(os.path.join(self._folder, "helics-cli.sqlite.db")) if os.path.exists(db_file): os.remove(db_file) - self.engine = db.create_engine("sqlite+pysqlite:///{}".format(db_file), echo=False, future=True) + self.engine = create_engine( + "sqlite+pysqlite:///{}".format(db_file), echo=False, future=True + ) db.Base.metadata.create_all(self.engine) - self.session = db.Session(bind=self.engine) + self.session = Session(bind=self.engine) self.session.add(db.MetaData(name="helics_version", value=h.helicsGetVersion())) self.session.add(db.MetaData(name="created", value=datetime.now().isoformat())) self.session.add(db.SystemInfo(data=h.helicsGetSystemInfo())) @@ -76,7 +81,9 @@ def publications(self) -> List[str]: @property def subscriptions(self) -> List[str]: - return [cast(str, name) for name in self.federate.query("root", "subscriptions")] + return [ + cast(str, name) for name in self.federate.query("root", "subscriptions") + ] @property def inputs(self) -> List[str]: @@ -106,7 +113,13 @@ def get_data(self): for federate in core["federates"]: assert federate["attributes"]["parent"] == core["attributes"]["id"] logger.info(f"Adding federate {federate}") - self.session.add(db.Federates(id=federate["attributes"]["id"], name=federate["attributes"]["name"], parent=core["attributes"]["id"])) + self.session.add( + db.Federates( + id=federate["attributes"]["id"], + name=federate["attributes"]["name"], + parent=core["attributes"]["id"], + ) + ) if "inputs" in federate.keys(): for input in federate["inputs"]: @@ -115,16 +128,28 @@ def get_data(self): self.session.add(db.Inputs(source=input["federate"])) else: for s in input["sources"]: - self.session.add(db.Inputs(source=input["federate"], target=s["federate"])) + self.session.add( + db.Inputs( + source=input["federate"], target=s["federate"] + ) + ) if "publications" in federate.keys(): for publication in federate["publications"]: logger.info(f"Adding publication {publication}") if "targets" not in publication.keys(): - self.session.add(db.Publications(source=publication["federate"])) + self.session.add( + db.Publications(source=publication["federate"]) + ) else: for t in publication["targets"]: - self.session.add(db.Publications(name=publication["key"], target=publication["federate"], source=t["federate"])) + self.session.add( + db.Publications( + name=publication["key"], + target=publication["federate"], + source=t["federate"], + ) + ) self.session.commit() @@ -138,7 +163,12 @@ def get_data(self): ] columns.insert(0, db.Column("simulation_time", db.Float)) columns.insert(0, db.Column("updated_at", db.Float)) - columns.insert(0, db.Column("id", db.Integer, db.Sequence("id"), primary_key=True, nullable=False)) + columns.insert( + 0, + db.Column( + "id", db.Integer, db.Sequence("id"), primary_key=True, nullable=False + ), + ) datatable = db.Table("datatable", db.Base.metadata, *columns) class DataTable(db.Base): @@ -154,7 +184,9 @@ def hook(self): def wait(self): federates = self.federates - while not all(self.federate.query(name, "isinit") is True for name in federates): + while not all( + self.federate.query(name, "isinit") is True for name in federates + ): for name in federates: logger.debug(f"{name} isinit = {self.federate.query(name, 'isinit')}") time.sleep(1) @@ -191,7 +223,11 @@ def run(self): self.session.commit() if ( - all(self.federate.query(name, "state") == "disconnected" for name in self.federates if name != "__observer__") + all( + self.federate.query(name, "state") == "disconnected" + for name in self.federates + if name != "__observer__" + ) or simulation_time >= 9223372036.3 ): break diff --git a/helics_cli_extras/pyproject.toml b/helics_cli_extras/pyproject.toml new file mode 100644 index 00000000..283689c3 --- /dev/null +++ b/helics_cli_extras/pyproject.toml @@ -0,0 +1,66 @@ +[build-system] +requires = ["setuptools>=61.0.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "helics-cli-extras" +version = "0.0.1" +authors = [{ name = "Dheepak Krishnamurthy", email = "me@kdheepak.com" }] +maintainers = [ + { name = "Dheepak Krishnamurthy", email = "me@kdheepak.com" }, + { name = "Ryan Mast", email = "mast9@llnl.gov" }, +] +description = "Python HELICS bindings" +readme = "README.md" +requires-python = ">=3.6" +keywords = ["helics", "co-simulation", "webserver"] +license = { text = "MIT License" } +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: Unix", + "Operating System :: POSIX", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "Environment :: Console", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Utilities", + "Topic :: Software Development", +] +dependencies = [ + "flask>=2", + "requests", + "flask-restful", + "flask-cors", + "pandas", + "SQLAlchemy", +] + +[project.urls] +Homepage = "https://github.com/GMLC-TDC/pyhelics" +Discussions = "https://github.com/GMLC-TDC/HELICS/discussions" +Documentation = "https://python.helics.org/" +"Issue Tracker" = "https://github.com/GMLC-TDC/pyhelics/issues" +"Source Code" = "https://github.com/GMLC-TDC/pyhelics" + +[tool.setuptools.packages.find] +include = ["helics_cli_extras", "helics_cli_extras.*"] + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.package-data] +"*" = ["static/**"] diff --git a/setup.py b/setup.py index 3d2e4e74..1b8b9725 100755 --- a/setup.py +++ b/setup.py @@ -64,117 +64,19 @@ def update_package_data(distribution): build_py.finalize_options() -def js_prerelease(command, strict=False): - """decorator for building minified js/css prior to another command""" - - class DecoratedCommand(command): - def run(self): - jsdeps = self.distribution.get_command_obj("jsdeps") - if not IS_REPO and all(os.path.exists(t) for t in jsdeps.targets): - # sdist, nothing to do - command.run(self) - return - - try: - self.distribution.run_command("jsdeps") - except Exception as e: - missing = [t for t in jsdeps.targets if not os.path.exists(t)] - if strict or missing: - log.warn("rebuilding js and css failed") - if missing: - log.error("missing files: %s" % missing) - raise e - else: - log.warn("rebuilding js and css failed (not a problem)") - log.warn(str(e)) - command.run(self) - update_package_data(self.distribution) - - return DecoratedCommand - - -class NPM(Command): - description = "install package.json dependencies using npm" - - user_options = [] - - node_modules = os.path.join(NODE_ROOT, "node_modules") - - targets = ["index.html"] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def get_npm_name(self): - npm_name = "npm" - if platform.system() == "Windows": - npm_name = "npm.cmd" - return npm_name - - def has_npm(self): - npm_name = self.get_npm_name() - try: - subprocess.check_call([npm_name, "--version"]) - return True - except: - return False - - def should_run_npm_install(self): - node_modules_exists = os.path.exists(self.node_modules) - return self.has_npm() and not node_modules_exists - - def run(self): - has_npm = self.has_npm() - if not has_npm: - log.error("`npm` unavailable, skipping npm build. If you're running this command using " "sudo, make sure `npm` is available to sudo") - return - - env = os.environ.copy() - env["PATH"] = NPM_PATH - - npm_name = self.get_npm_name() - - if self.should_run_npm_install(): - log.info("Installing build dependencies with npm. " "This may take a while...") - subprocess.check_call( - [npm_name, "install"], - cwd=NODE_ROOT, - stdout=sys.stdout, - stderr=sys.stderr, - ) - os.utime(self.node_modules, None) - - subprocess.check_call( - [npm_name, "run", "build"], - cwd=NODE_ROOT, - stdout=sys.stdout, - stderr=sys.stderr, - ) - - copy_tree(os.path.join(NODE_ROOT, "build"), os.path.join(STATIC_DIR)) - - for t in self.targets: - if not os.path.exists(os.path.join(STATIC_DIR, t)): - msg = "Missing file: %s" % t - if not has_npm: - msg += "\nnpm is required to build a development version " - "of a widget extension" - raise ValueError(msg) - - # update package data in case this created new files - update_package_data(self.distribution) - - def read(*names, **kwargs): - with io.open(join(dirname(__file__), *names), encoding=kwargs.get("encoding", "utf8")) as fh: + with io.open( + join(dirname(__file__), *names), encoding=kwargs.get("encoding", "utf8") + ) as fh: return fh.read() -PYHELICS_VERSION = read(os.path.join(os.path.dirname(__file__), "helics", "_version.py"), encoding="utf-8") -PYHELICS_VERSION = PYHELICS_VERSION.splitlines()[1].split()[2].strip('"').strip("'").lstrip("v") +PYHELICS_VERSION = read( + os.path.join(os.path.dirname(__file__), "helics", "_version.py"), encoding="utf-8" +) +PYHELICS_VERSION = ( + PYHELICS_VERSION.splitlines()[1].split()[2].strip('"').strip("'").lstrip("v") +) HELICS_VERSION = re.findall(r"(?:(\d+\.(?:\d+\.)*\d+))", PYHELICS_VERSION)[0] # HELICS_VERSION = "{}-beta".format(HELICS_VERSION) @@ -182,18 +84,22 @@ def read(*names, **kwargs): CURRENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) HELICS_SOURCE = os.path.join(CURRENT_DIRECTORY, "./_source") -PYHELICS_INSTALL = os.environ.get("PYHELICS_INSTALL", os.path.join(CURRENT_DIRECTORY, "./helics/install")) +PYHELICS_INSTALL = os.environ.get( + "PYHELICS_INSTALL", os.path.join(CURRENT_DIRECTORY, "./helics/install") +) -DOWNLOAD_URL = "https://github.com/GMLC-TDC/HELICS/releases/download/v{version}/Helics-v{version}-source.tar.gz".format(version=HELICS_VERSION) +DOWNLOAD_URL = "https://github.com/GMLC-TDC/HELICS/releases/download/v{version}/Helics-v{version}-source.tar.gz".format( + version=HELICS_VERSION +) def create_default_url(helics_version, plat_name=""): if "macos" in plat_name.lower(): - if helics_version.startswith("3") and int(helics_version.split(".")[1]) >= 1: # >= 3.1.x - default_url = ( - "https://github.com/GMLC-TDC/HELICS/releases/download/v{helics_version}/Helics-{helics_version}-macOS-universal2.zip".format( - helics_version=helics_version - ) + if ( + helics_version.startswith("3") and int(helics_version.split(".")[1]) >= 1 + ): # >= 3.1.x + default_url = "https://github.com/GMLC-TDC/HELICS/releases/download/v{helics_version}/Helics-{helics_version}-macOS-universal2.zip".format( + helics_version=helics_version ) else: default_url = "https://github.com/GMLC-TDC/HELICS/releases/download/v{helics_version}/Helics-{helics_version}-macOS-x86_64.zip".format( @@ -214,11 +120,11 @@ def create_default_url(helics_version, plat_name=""): helics_version=helics_version ) elif platform.system() == "Darwin": - if helics_version.startswith("3") and int(helics_version.split(".")[1]) >= 1: # >= 3.1.x - default_url = ( - "https://github.com/GMLC-TDC/HELICS/releases/download/v{helics_version}/Helics-{helics_version}-macOS-universal2.zip".format( - helics_version=helics_version - ) + if ( + helics_version.startswith("3") and int(helics_version.split(".")[1]) >= 1 + ): # >= 3.1.x + default_url = "https://github.com/GMLC-TDC/HELICS/releases/download/v{helics_version}/Helics-{helics_version}-macOS-universal2.zip".format( + helics_version=helics_version ) else: default_url = "https://github.com/GMLC-TDC/HELICS/releases/download/v{helics_version}/Helics-{helics_version}-macOS-x86_64.zip".format( @@ -347,7 +253,6 @@ def unzip(zip_file_path, output_dir, permission=None): """ extracted_path = None with zipfile.ZipFile(zip_file_path, "r") as zip_ref: - # For each item in the zip file, extract the file and set permissions if available for file_info in zip_ref.infolist(): extracted_path = _extract(file_info, output_dir, zip_ref) @@ -389,10 +294,14 @@ def run(self): unzip("./tmp.zip", self.pyhelics_install) os.remove("./tmp.zip") - if len(os.listdir(self.pyhelics_install)) == 1 and os.listdir(self.pyhelics_install)[0].startswith("Helics-"): + if len(os.listdir(self.pyhelics_install)) == 1 and os.listdir( + self.pyhelics_install + )[0].startswith("Helics-"): tmp = os.listdir(self.pyhelics_install)[0] for folder in os.listdir(os.path.join(self.pyhelics_install, tmp)): - p = Path(os.path.join(self.pyhelics_install, tmp, folder)).absolute() + p = Path( + os.path.join(self.pyhelics_install, tmp, folder) + ).absolute() parent_dir = p.parents[1] p.rename(parent_dir / p.name) else: @@ -424,9 +333,13 @@ def run(self): IGNOREBLOCK = False print("Writing to {}".format(os.path.abspath(self.pyhelics_install))) for file in files: - if not os.path.isfile(os.path.join(self.pyhelics_install, "include", "helics", file)): + if not os.path.isfile( + os.path.join(self.pyhelics_install, "include", "helics", file) + ): continue - with open(os.path.join(self.pyhelics_install, "include", "helics", file)) as f: + with open( + os.path.join(self.pyhelics_install, "include", "helics", file) + ) as f: lines = [] for line in f: if line.startswith("#ifdef __cplusplus"): @@ -443,7 +356,9 @@ def run(self): data = "\n".join(lines) data = data.replace("HELICS_EXPORT", "") data = data.replace("HELICS_DEPRECATED_EXPORT", "") - with open(os.path.join(self.pyhelics_install, "include", "helics", file), "w") as f: + with open( + os.path.join(self.pyhelics_install, "include", "helics", file), "w" + ) as f: f.write(data) @@ -457,14 +372,19 @@ class HELICSCMakeBuild(build_ext): def run(self): try: out = subprocess.check_output(["cmake", "--version"]) - cmake_version = re.search(r"version\s*([\d.]+)", out.decode().lower()).group(1) + cmake_version = re.search( + r"version\s*([\d.]+)", out.decode().lower() + ).group(1) cmake_version = [int(i) for i in cmake_version.split(".")] if cmake_version < [3, 5, 1]: raise RuntimeError("CMake >= 3.5.1 is required to build helics") except OSError: if not os.path.exists(PYHELICS_INSTALL): - raise RuntimeError("CMake must be installed to build the following extensions: " + ", ".join(e.name for e in self.extensions)) + raise RuntimeError( + "CMake must be installed to build the following extensions: " + + ", ".join(e.name for e in self.extensions) + ) for ext in self.extensions: self.build_extension(ext) @@ -501,7 +421,9 @@ def build_extension(self, ext): build_args = ["--config", cfg] if platform.system() == "Windows": - cmake_args += ["-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir)] + cmake_args += [ + "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir) + ] if sys.maxsize > 2**32: cmake_args += ["-A", "x64"] build_args += ["--", "/m"] @@ -512,7 +434,9 @@ def build_extension(self, ext): env = os.environ.copy() if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) - subprocess.check_call(["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) + subprocess.check_call( + ["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env + ) cmd = " ".join(["cmake", "--build", ".", "--target", "install"] + build_args) print(cmd) subprocess.check_call(shlex.split(cmd), cwd=self.build_temp) @@ -567,13 +491,12 @@ def get_tag(self): cmdclass = { "download": HELICSDownloadCommand, - "build_ext": js_prerelease(HELICSCMakeBuild), - "bdist_wheel": js_prerelease(HelicsBdistWheel), - "develop": js_prerelease(develop), - "build_py": js_prerelease(build_py), - "egg_info": js_prerelease(egg_info), - "sdist": js_prerelease(sdist, strict=True), - "jsdeps": NPM, + "build_ext": HELICSCMakeBuild, + "bdist_wheel": HelicsBdistWheel, + "develop": develop, + "build_py": build_py, + "egg_info": egg_info, + "sdist": sdist, } @@ -582,7 +505,10 @@ def is_pure(self): return False -helics_cli_install_requires = ["flask>=2", "requests", "flask-restful", "flask-cors", "pandas", "SQLAlchemy", "matplotlib"] +helics_cli_install_requires = [ + "helics_cli_extras==0.0.1", + "matplotlib", +] setup( name="helics",