Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 31 additions & 43 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,67 +7,58 @@ on:
branches: [main, dev]

jobs:
lint-and-test:
lint:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'

- name: Install ruff
run: pip install ruff

- name: Check formatting
run: ruff format --check .

- name: Lint
run: ruff check . --output-format=github

test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-ci.txt
# Uses minimal CI requirements (no tensorflow/heavy packages)

- name: Run linter (ruff)
- name: Run tests
run: |
ruff check . --select=E9,F63,F7,F82 --output-format=github \
--exclude="Dockerfile.*" \
--exclude="linktest/" \
--exclude="measurements/" \
--exclude="0mq/" \
--exclude="ratc/"
# E9: Runtime errors (syntax errors, etc.)
# F63: Invalid print syntax
# F7: Syntax errors in type comments
# F82: Undefined names in __all__
# Excludes: Dockerfiles (not Python), linktest (symlinks),
# measurements/0mq/ratc (config-dependent experimental scripts)

- name: Run tests (pytest)
run: |
set +e
pytest --tb=short -q \
--cov=concore_cli --cov=concore_base \
--cov-report=term-missing \
--ignore=measurements/ \
--ignore=0mq/ \
--ignore=ratc/ \
--ignore=linktest/
status=$?
set -e
# Allow success if no tests are collected (pytest exit code 5)
if [ "$status" -ne 0 ] && [ "$status" -ne 5 ]; then
exit "$status"
fi
# Fails on real test failures, passes on no tests collected

docker-build:
runs-on: ubuntu-latest
# Only run when Dockerfile.py or related files change
if: |
github.event_name == 'push' ||
(github.event_name == 'pull_request' &&
contains(github.event.pull_request.changed_files, 'Dockerfile'))

steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Check if Dockerfile.py changed
uses: dorny/paths-filter@v3
Expand All @@ -80,7 +71,4 @@ jobs:

- name: Validate Dockerfile build
if: steps.filter.outputs.dockerfile == 'true'
run: |
docker build -f Dockerfile.py -t concore-py-test .
# Validates that Dockerfile.py can be built successfully
# Does not push the image
run: docker build -f Dockerfile.py -t concore-py-test .
33 changes: 0 additions & 33 deletions .github/workflows/tests.yml

This file was deleted.

7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.12
hooks:
- id: ruff
args: [--output-format=full]
- id: ruff-format
2 changes: 1 addition & 1 deletion concore_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

from .cli import cli

__all__ = ['cli']
__all__ = ["cli"]
55 changes: 36 additions & 19 deletions concore_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@
from . import __version__

console = Console()
DEFAULT_EXEC_TYPE = 'windows' if os.name == 'nt' else 'posix'
DEFAULT_EXEC_TYPE = "windows" if os.name == "nt" else "posix"


@click.group()
@click.version_option(version=__version__, prog_name='concore')
@click.version_option(version=__version__, prog_name="concore")
def cli():
pass


@cli.command()
@click.argument('name', required=True)
@click.option('--template', default='basic', help='Template type to use')
@click.argument("name", required=True)
@click.option("--template", default="basic", help="Template type to use")
def init(name, template):
"""Create a new concore project"""
try:
Expand All @@ -31,12 +33,21 @@ def init(name, template):
console.print(f"[red]Error:[/red] {str(e)}")
sys.exit(1)


@cli.command()
@click.argument('workflow_file', type=click.Path(exists=True))
@click.option('--source', '-s', default='src', help='Source directory')
@click.option('--output', '-o', default='out', help='Output directory')
@click.option('--type', '-t', default=DEFAULT_EXEC_TYPE, type=click.Choice(['windows', 'posix', 'docker']), help='Execution type')
@click.option('--auto-build', is_flag=True, help='Automatically run build after generation')
@click.argument("workflow_file", type=click.Path(exists=True))
@click.option("--source", "-s", default="src", help="Source directory")
@click.option("--output", "-o", default="out", help="Output directory")
@click.option(
"--type",
"-t",
default=DEFAULT_EXEC_TYPE,
type=click.Choice(["windows", "posix", "docker"]),
help="Execution type",
)
@click.option(
"--auto-build", is_flag=True, help="Automatically run build after generation"
)
def run(workflow_file, source, output, type, auto_build):
"""Run a concore workflow"""
try:
Expand All @@ -45,9 +56,10 @@ def run(workflow_file, source, output, type, auto_build):
console.print(f"[red]Error:[/red] {str(e)}")
sys.exit(1)


@cli.command()
@click.argument('workflow_file', type=click.Path(exists=True))
@click.option('--source', '-s', default='src', help='Source directory')
@click.argument("workflow_file", type=click.Path(exists=True))
@click.option("--source", "-s", default="src", help="Source directory")
def validate(workflow_file, source):
"""Validate a workflow file"""
try:
Expand All @@ -58,10 +70,11 @@ def validate(workflow_file, source):
console.print(f"[red]Error:[/red] {str(e)}")
sys.exit(1)


@cli.command()
@click.argument('workflow_file', type=click.Path(exists=True))
@click.option('--source', '-s', default='src', help='Source directory')
@click.option('--json', 'output_json', is_flag=True, help='Output in JSON format')
@click.argument("workflow_file", type=click.Path(exists=True))
@click.option("--source", "-s", default="src", help="Source directory")
@click.option("--json", "output_json", is_flag=True, help="Output in JSON format")
def inspect(workflow_file, source, output_json):
"""Inspect a workflow file and show its structure"""
try:
Expand All @@ -70,6 +83,7 @@ def inspect(workflow_file, source, output_json):
console.print(f"[red]Error:[/red] {str(e)}")
sys.exit(1)


@cli.command()
def status():
"""Show running concore processes"""
Expand All @@ -79,8 +93,9 @@ def status():
console.print(f"[red]Error:[/red] {str(e)}")
sys.exit(1)


@cli.command()
@click.confirmation_option(prompt='Stop all running concore processes?')
@click.confirmation_option(prompt="Stop all running concore processes?")
def stop():
"""Stop all running concore processes"""
try:
Expand All @@ -89,10 +104,11 @@ def stop():
console.print(f"[red]Error:[/red] {str(e)}")
sys.exit(1)


@cli.command()
@click.argument('study_dir', type=click.Path(exists=True))
@click.option('--interval', '-n', default=2.0, help='Refresh interval in seconds')
@click.option('--once', is_flag=True, help='Print a single snapshot and exit')
@click.argument("study_dir", type=click.Path(exists=True))
@click.option("--interval", "-n", default=2.0, help="Refresh interval in seconds")
@click.option("--once", is_flag=True, help="Print a single snapshot and exit")
def watch(study_dir, interval, once):
"""Watch a running simulation study for live monitoring"""
try:
Expand All @@ -101,5 +117,6 @@ def watch(study_dir, interval, once):
console.print(f"[red]Error:[/red] {str(e)}")
sys.exit(1)

if __name__ == '__main__':

if __name__ == "__main__":
cli()
9 changes: 8 additions & 1 deletion concore_cli/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,11 @@
from .stop import stop_all
from .watch import watch_study

__all__ = ['init_project', 'run_workflow', 'validate_workflow', 'show_status', 'stop_all', 'watch_study']
__all__ = [
"init_project",
"run_workflow",
"validate_workflow",
"show_status",
"stop_all",
"watch_study",
]
63 changes: 32 additions & 31 deletions concore_cli/commands/init.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import os
import shutil
from pathlib import Path
from rich.panel import Panel

SAMPLE_GRAPHML = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
SAMPLE_GRAPHML = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd" xmlns:y="http://www.yworks.com/xml/graphml">
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
Expand All @@ -21,9 +19,9 @@
</node>
</graph>
</graphml>
'''
"""

SAMPLE_PYTHON = '''import concore
SAMPLE_PYTHON = """import concore

concore.default_maxtime(100)
concore.delay = 0.02
Expand All @@ -36,9 +34,9 @@
val = concore.read(1,"data",init_simtime_val)
result = [v * 2 for v in val]
concore.write(1,"result",result,delta=0)
'''
"""

README_TEMPLATE = '''# {project_name}
README_TEMPLATE = """# {project_name}

A concore workflow project.

Expand All @@ -63,38 +61,41 @@
- Add Python/C++/MATLAB scripts to `src/`
- Use `concore validate workflow.graphml` to check your workflow
- Use `concore status` to monitor running processes
'''
"""


def init_project(name, template, console):
project_path = Path(name)

if project_path.exists():
raise FileExistsError(f"Directory '{name}' already exists")

console.print(f"[cyan]Creating project:[/cyan] {name}")

project_path.mkdir()
(project_path / 'src').mkdir()
workflow_file = project_path / 'workflow.graphml'
with open(workflow_file, 'w') as f:
(project_path / "src").mkdir()

workflow_file = project_path / "workflow.graphml"
with open(workflow_file, "w") as f:
f.write(SAMPLE_GRAPHML)
sample_script = project_path / 'src' / 'script.py'
with open(sample_script, 'w') as f:

sample_script = project_path / "src" / "script.py"
with open(sample_script, "w") as f:
f.write(SAMPLE_PYTHON)
readme_file = project_path / 'README.md'
with open(readme_file, 'w') as f:

readme_file = project_path / "README.md"
with open(readme_file, "w") as f:
f.write(README_TEMPLATE.format(project_name=name))

console.print()
console.print(Panel.fit(
f"[green]✓[/green] Project created successfully!\n\n"
f"Next steps:\n"
f" cd {name}\n"
f" concore validate workflow.graphml\n"
f" concore run workflow.graphml",
title="Success",
border_style="green"
))
console.print(
Panel.fit(
f"[green]✓[/green] Project created successfully!\n\n"
f"Next steps:\n"
f" cd {name}\n"
f" concore validate workflow.graphml\n"
f" concore run workflow.graphml",
title="Success",
border_style="green",
)
)
Loading