Repository: fractal-analytics-platform/fractal-client (language: python)

Issue:
 Update ongoing-mainteinance CHANGELOG



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 385842e76..a3fa3d022 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,16 @@
 **Note**: Numbers like (\#123) point to closed Pull Requests on the fractal repository.
 
+# 1.3.0
+
+* Align with [fractal-server 1.2.0](https://fractal-analytics-platform.github.io/fractal-server/changelog/#130), by updating all relevant endpoint path/query/body parameters (\#479).
+* Make `project_id` a required positional argument of `fractal {workflow,dataset,job}` commands (\#479).
+* Add `fractal job stop` command (\#485).
+* Add `--type` optional argument to `fractal dataset new` command (\#479).
+* Make `output_dataset_id` a required positional argument of `fractal workflow apply` (\#483).
+* Fix a bug in tests, by starting the fractal-server FastAPI app in a more standard way (\#481).
+
 # 1.2.0
+
 * Align with [fractal-server 1.2.0](https://fractal-analytics-platform.github.io/fractal-server/changelog/#120) (\#472).
 
 # 1.1.0
diff --git a/fractal/cmd/__init__.py b/fractal/cmd/__init__.py
index 7526d332a..b5c55ada2 100644
--- a/fractal/cmd/__init__.py
+++ b/fractal/cmd/__init__.py
@@ -14,6 +14,7 @@
 from ._job import get_job
 from ._job import get_job_list
 from ._job import get_job_logs
+from ._job import stop_job
 from ._project import delete_project
 from ._project import get_project
 from ._project import get_project_list
@@ -175,6 +176,8 @@ async def job(
         iface = await get_job(client, batch=batch, **kwargs)
     elif subcmd == "download-logs":
         iface = await get_job_logs(client, **kwargs)
+    elif subcmd == "stop":
+        iface = await stop_job(client, batch=batch, **kwargs)
     else:
         raise NoCommandError(f"Command job {subcmd} not found")
     return iface
diff --git a/fractal/cmd/_job.py b/fractal/cmd/_job.py
index ec0791c4c..102abf653 100644
--- a/fractal/cmd/_job.py
+++ b/fractal/cmd/_job.py
@@ -150,3 +150,22 @@ async def get_job_logs(
     return PrintInterface(
         retcode=0, data=f"Logs downloaded to {output_folder=}"
     )
+
+
+async def stop_job(
+    client: AuthClient,
+    project_id: int,
+    job_id: int,
+    **kwargs,
+) -> BaseInterface:
+    """
+    Stop a workflow-execution job
+    """
+
+    res = await client.get(
+        f"{settings.BASE_URL}/project/{project_id}/job/{job_id}/stop/"
+    )
+    check_response(res, expected_status_code=200)
+    return PrintInterface(
+        retcode=0, data="Correctly called the job-stopping endpoint"
+    )
diff --git a/fractal/parser.py b/fractal/parser.py
index 436900f83..8ce9cefb8 100644
--- a/fractal/parser.py
+++ b/fractal/parser.py
@@ -321,7 +321,10 @@
 task_check_collection_parser.add_argument(
     "--do-not-separate-logs",
     dest="do_not_separate_logs",
-    help="Show the job logs in the main output, instead of a separate field",
+    help=(
+        "Show the task-collection logs in the main output, "
+        "instead of a separate field"
+    ),
     action="store_true",
     required=False,
 )
@@ -672,6 +675,15 @@
     required=True,
 )
 
+# job stop
+job_stop_parser = job_subparsers.add_parser(
+    "stop",
+    description="Stop workflow-execution job",
+    allow_abbrev=False,
+)
+job_stop_parser.add_argument("project_id", type=int, help="Project ID")
+job_stop_parser.add_argument("job_id", type=int, help="Job ID")
+
 
 # VERSION GROUP
 version_parser = subparsers_main.add_parser(
diff --git a/poetry.lock b/poetry.lock
index e292e9ef1..0bd105b4b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -623,7 +623,7 @@ sqlalchemy = ["fastapi-users-db-sqlalchemy (>=4.0.0)"]
 
 [[package]]
 name = "fractal-server"
-version = "1.2.5"
+version = "1.3.0a1"
 description = "Server component of the Fractal analytics platform"
 category = "dev"
 optional = false
@@ -645,13 +645,13 @@ uvicorn = "^0.20.0"
 [package.extras]
 gunicorn = ["gunicorn (>=20.1.0,<21.0.0)"]
 postgres = ["asyncpg (>=0.27.0,<0.28.0)", "psycopg2 (>=2.9.5,<3.0.0)"]
-slurm = ["clusterfutures (>=0.5,<0.6)"]
+slurm = ["cloudpickle (>=2.2.1,<2.3.0)", "clusterfutures (>=0.5,<0.6)"]
 
 [package.source]
 type = "git"
 url = "https://github.com/fractal-analytics-platform/fractal-server.git"
 reference = "main"
-resolved_reference = "447c21bdc6d07a3851f4da22bee4479b082f8a31"
+resolved_reference = "038d66488e9b65bbdfc81a2cc42fb5c1b94b70f8"
 
 [[package]]
 name = "greenlet"


Test patch:
diff --git a/tests/fixtures_testserver.py b/tests/fixtures_testserver.py
index 37aa377d0..9d53786b5 100644
--- a/tests/fixtures_testserver.py
+++ b/tests/fixtures_testserver.py
@@ -21,6 +21,7 @@ def override_server_settings(tmp_path):
     tmp_db_path.parent.mkdir()
     settings.DB_ENGINE = "sqlite"
     settings.SQLITE_PATH = tmp_db_path
+    settings.FRACTAL_RUNNER_BACKEND = "local"
 
     settings.JWT_SECRET_KEY = "secret_key"
     settings.DEPLOYMENT_TYPE = "development"
diff --git a/tests/test_job.py b/tests/test_job.py
index b0115003c..626f7988c 100644
--- a/tests/test_job.py
+++ b/tests/test_job.py
@@ -177,3 +177,47 @@ async def test_job_download_logs(
     with logfile.open("r") as f:
         contents = f.read()
     assert contents == LOG
+
+
+async def test_job_stop(
+    register_user,
+    invoke,
+    tmp_path: Path,
+    workflow_factory,
+    project_factory,
+    job_factory,
+    caplog,
+):
+    """
+    GIVEN a job entry in the database
+    WHEN calling the `job stop` command
+    THEN the client response has the expected status
+
+    NOTE 1: This is a test of the client command, not a test of the
+    corresponding fractal-server feature
+
+    NOTE 2: We don't have a fractal-server instance with SLURM backend in the
+    fractal client CI, so this command can be tested for consistency but not
+    for functionality.
+    """
+
+    # Create mock Workflow/ApplyWorkflow objects
+    res = await invoke("project new prj0")
+    project_id = res.data["id"]
+    wf = await workflow_factory(project_id=project_id)
+    workflow_path = tmp_path / f"workflow_{wf.id}"
+    workflow_path.mkdir()
+    job = await job_factory(
+        working_dir=workflow_path.as_posix(),
+        worfklow_id=wf.id,
+    )
+    debug(job)
+
+    # Call `job stop` (this will fail because FRACTAL_RUNNER_BACKEND="local"
+    cmd = f"job stop {project_id} {job.id}"
+    debug(cmd)
+    with pytest.raises(SystemExit):
+        res = await invoke(cmd)
+    debug(caplog.text)
+    assert "Stopping a job execution is not implemented" in caplog.text
+    caplog.clear()