Skip to content

Commit

Permalink
Merge pull request #14 from lorenzocerrone/update_to_fractal_task_cor…
Browse files Browse the repository at this point in the history
…e_1_2

Update to fractal task core 1.3
  • Loading branch information
lorenzocerrone authored Aug 16, 2024
2 parents 63a7824 + 23146ef commit e46c9b3
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/copy-install-test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
branches: ["main", "update_to_fractal_task_core_1_2"]

jobs:
copy_install_test_build:
Expand Down
52 changes: 52 additions & 0 deletions project/.github/workflows/build_and_test.yml.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: CI (build and test)

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]


jobs:
tests:
strategy:
matrix:
os: [ubuntu-22.04, macos-latest]
python-version: ["3.9", "3.10", "3.11"]
exclude:
- os: macos-latest
python-version: '3.9'
- os: macos-latest
python-version: '3.10'
name: "Core, Python ${{ '{{' }} matrix.python-version {{ '}}' }}, ${{ '{{' }} matrix.os {{ '}}' }}"
runs-on: ${{ '{{' }} matrix.os {{ '}}' }}
timeout-minutes: 10

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Set up Python ${{ '{{' }} matrix.python-version {{ '}}' }}
uses: actions/setup-python@v5
with:
python-version: ${{ '{{' }} matrix.python-version {{ '}}' }}
cache: "pip"

- name: Install package
run: python -m pip install -e .[dev]

- name: Regenerate the manifest
run: python src/{{package_name}}/dev/create_manifest.py

- name: Check if manifest has changed
run: |
if [ -n "$(git diff --exit-code ./src/{{package_name}}/__FRACTAL_MANIFEST__.json)" ]; then
echo "__FRACTAL_MANIFEST__.json has changed. Please run 'python src/{{package_name}}/dev/create_manifest.py' and commit the changes."
exit 1
else
echo "__FRACTAL_MANIFEST__.json has not changed."
fi

- name: Test core library with pytest
run: python -m pytest tests
112 changes: 112 additions & 0 deletions project/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Git ignored is sourced from https://github.com/pydev-guide/pyrepo-copier/blob/main/LICENSE
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

.DS_Store

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# ruff
.ruff_cache/

# IDE settings
.vscode/
.idea/
2 changes: 1 addition & 1 deletion project/pyproject.toml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ authors = [

# Required Python version and dependencies
requires-python = ">=3.9"
dependencies = ["fractal-tasks-core == 1.1.0", "scikit-image"]
dependencies = ["fractal-tasks-core == 1.3.0", "scikit-image"]

# Optional dependencies (e.g. for `pip install -e ".[dev]"`, see
# https://peps.python.org/pep-0621/#dependencies-optional-dependencies)
Expand Down
79 changes: 79 additions & 0 deletions project/src/{{package_name}}/__FRACTAL_MANIFEST__.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"manifest_version": "2",
"task_list": [
{
"name": "Thresholding Label Task",
"executable_parallel": "thresholding_label_task.py",
"meta_parallel": {
"cpus_per_task": 1,
"mem": 4000
},
"args_schema_parallel": {
"$defs": {
"ChannelInputModel": {
"description": "A channel which is specified by either `wavelength_id` or `label`.",
"properties": {
"wavelength_id": {
"title": "Wavelength Id",
"type": "string",
"description": "Unique ID for the channel wavelength, e.g. `A01_C01`. Can only be specified if label is not set."
},
"label": {
"title": "Label",
"type": "string",
"description": "Name of the channel. Can only be specified if wavelength_id is not set."
}
},
"title": "ChannelInputModel",
"type": "object"
}
},
"additionalProperties": false,
"properties": {
"zarr_url": {
"title": "Zarr Url",
"type": "string",
"description": "Absolute path to the OME-Zarr image."
},
"threshold": {
"title": "Threshold",
"type": "integer",
"description": "Threshold value to be applied."
},
"channel": {
"$ref": "#/$defs/ChannelInputModel",
"title": "Channel",
"description": "Channel to be thresholded."
},
"label_name": {
"title": "Label Name",
"type": "string",
"description": "Name of the resulting label image"
},
"min_size": {
"default": 50,
"title": "Min Size",
"type": "integer",
"description": "Minimum size of objects. Smaller objects are filtered out."
},
"overwrite": {
"default": true,
"title": "Overwrite",
"type": "boolean",
"description": "Whether to overwrite an existing label image"
}
},
"required": [
"zarr_url",
"threshold",
"channel"
],
"type": "object",
"title": "ThresholdingLabelTask"
},
"docs_info": "## thresholding_label_task\nThreshold an image and find connected components.\n"
}
],
"has_args_schemas": true,
"args_schema_version": "pydantic_v2"
}
4 changes: 2 additions & 2 deletions project/src/{{package_name}}/thresholding_label_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
from fractal_tasks_core.ngff.specs import NgffImageMeta
from fractal_tasks_core.pyramids import build_pyramid
from fractal_tasks_core.utils import rescale_datasets
from pydantic.v1.decorator import validate_arguments
from pydantic import validate_call
from skimage.measure import label
from skimage.morphology import ball, dilation, opening, remove_small_objects

__OME_NGFF_VERSION__ = fractal_tasks_core.__OME_NGFF_VERSION__


@validate_arguments
@validate_call
def thresholding_label_task(
*,
zarr_url: str,
Expand Down
2 changes: 1 addition & 1 deletion project/tests/__init__.py.jinja
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {{package_name}}
import json
from pathlib import Path

import {{package_name}}

PACKAGE = "{{package_name}}"
PACKAGE_DIR = Path({{package_name}}.__file__).parent
Expand Down
2 changes: 1 addition & 1 deletion project/tests/test_thresholding_label_task.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ from pathlib import Path

import pytest
from devtools import debug

from fractal_tasks_core.channels import ChannelInputModel

from {{package_name}}.thresholding_label_task import thresholding_label_task


Expand Down
12 changes: 6 additions & 6 deletions project/tests/test_valid_args_schemas.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ import json
from pathlib import Path

import pytest
from jsonschema.validators import Draft201909Validator
from jsonschema.validators import Draft202012Validator
from jsonschema.validators import Draft7Validator

from fractal_tasks_core.dev.lib_args_schemas import (
create_schema_for_single_task,
)
from fractal_tasks_core.dev.lib_signature_constraints import _extract_function
from fractal_tasks_core.dev.lib_signature_constraints import (
_extract_function,
_validate_function_signature,
)
from jsonschema.validators import (
Draft7Validator,
Draft201909Validator,
Draft202012Validator,
)

from . import TASK_LIST



def test_task_functions_have_valid_signatures():
"""
Test that task functions have valid signatures.
Expand Down
1 change: 1 addition & 0 deletions project/tests/test_valid_manifest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json

import requests
from jsonschema import validate

Expand Down
17 changes: 7 additions & 10 deletions project/tests/test_valid_task_interface.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import json
import subprocess
from shlex import split as shlex_split
from subprocess import PIPE

import pytest
from devtools import debug

from . import MANIFEST
from . import PACKAGE_DIR
from . import MANIFEST, PACKAGE_DIR


def validate_command(cmd: str):
Expand All @@ -17,22 +15,21 @@ def validate_command(cmd: str):
debug(cmd)
result = subprocess.run( # nosec
shlex_split(cmd),
stdout=PIPE,
stderr=PIPE,
capture_output=True,
)
# The command must always fail, since tmp_file_args includes invalid
# arguments
assert result.returncode == 1
stderr = result.stderr.decode()
debug(stderr)

# Valid stderr includes pydantic.error_wrappers.ValidationError (type
# Valid stderr includes pydantic.v1.error_wrappers.ValidationError (type
# match between model and function, but tmp_file_args has wrong arguments)
assert "pydantic.v1.error_wrappers.ValidationError" in stderr
assert "ValidationError" in stderr

# Valid stderr must include a mention of "unexpected keyword arguments",
# Valid stderr must include a mention of "Unexpected keyword argument",
# because we are including some invalid arguments
assert "unexpected keyword arguments" in stderr
assert "Unexpected keyword argument" in stderr

# Invalid stderr includes ValueError
assert "ValueError" not in stderr
Expand All @@ -47,7 +44,7 @@ def test_task_interface(task, tmp_path):
tmp_file_args = str(tmp_path / "args.json")
tmp_file_metadiff = str(tmp_path / "metadiff.json")
with open(tmp_file_args, "w") as fout:
args = dict(wrong_arg_1=123, wrong_arg_2=[1, 2, 3])
args = {"wrong_arg_1": 123, "wrong_arg_2": [1, 2, 3]}
json.dump(args, fout, indent=4)

for key in ["executable_non_parallel", "executable_parallel"]:
Expand Down

0 comments on commit e46c9b3

Please sign in to comment.