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
11 changes: 10 additions & 1 deletion .github/workflows/realapps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,17 @@ jobs:
done

- name: Validate the generated crate
# rocrate-validator needs rich<14, which current Flower cannot share, so it
# gets its own virtual environment. The federation has finished by now.
# flwrcrate goes in without its dependencies (as in tests.yml), plus
# ro-crate-py, so the script can import flwrcrate.validation.
# offline=True only reads the validator's cache, so fill it first.
run: |
python tests/e2e/validate_crate.py \
python -m venv /tmp/validator
/tmp/validator/bin/pip install "roc-validator>=0.13" "rocrate>=0.15"
/tmp/validator/bin/pip install -e . --no-deps
/tmp/validator/bin/rocrate-validator cache warm -p ro-crate-1.2 -p process-run-crate-0.5
/tmp/validator/bin/python tests/e2e/validate_crate.py \
/tmp/flcrate_out/ro-crate/ro-crate-metadata.json \
"${{ matrix.strategy }}" "${{ matrix.framework }}"

Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ jobs:
# works without a Flower install.
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov "rocrate>=0.13" "tomli>=2.0; python_version < '3.11'"
pip install pytest pytest-cov "rocrate>=0.15" "roc-validator>=0.13" "tomli>=2.0; python_version < '3.11'"
pip install -e . --no-deps

- name: Cache the validator's profile resources
# The tests validate with offline=True, which only reads rocrate-validator's
# cache. A fresh runner's cache is empty, so fill it first.
run: rocrate-validator cache warm -p ro-crate-1.2 -p process-run-crate-0.5

- name: Run tests with coverage
run: pytest -q --cov=flwrcrate --cov-report=term-missing --cov-fail-under=85
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ grep -rn "flwr.serverapp\|strategy.start\|server_fn\|ServerAppComponents" <app>/
- `flwr.serverapp` **and** `strategy.start` → ✅ compatible
- `server_fn` or `ServerAppComponents` → ❌ classic API, not supported (yet)

Other requirements: Python ≥ 3.11, `rocrate ≥ 0.13`.
Other requirements: Python ≥ 3.11, `rocrate ≥ 0.15`.

## Install

Expand Down Expand Up @@ -243,11 +243,15 @@ series.

### Validation

Crates validate against the official
[`rocrate-validator`](https://github.com/crs4/rocrate-validator). Note:
`ro-crate-py` ≥ 0.15 writes RO-Crate **1.2**, while `rocrate-validator` 0.10
ships only a **1.1** profile, producing a single false-positive `MUST 5.3`
(`conformsTo` version). All other REQUIRED checks pass.
Crates declare and validate against **RO-Crate 1.2**: flwrCrate sets the
version explicitly, so the output does not depend on which `ro-crate-py` is
installed. The test suite and the end-to-end workflow check generated crates
with [`rocrate-validator`](https://github.com/crs4/rocrate-validator) at the
REQUIRED level, against the `ro-crate-1.2` and `process-run-crate-0.5`
profiles. Process Run Crate 0.5 is based on RO-Crate 1.1, so its inherited
checks are not reported; the RO-Crate 1.2 check covers the base rules.
The validator's opt-in extra checks are enabled too; they flag likely mistakes,
such as a plain string where a link to another entity was meant.

## Tested with

Expand Down Expand Up @@ -294,7 +298,8 @@ it produced.
## Development

```bash
pip install -e ".[dev]" # installs pytest + pytest-cov
pip install -e ".[dev]" # installs pytest + pytest-cov and roc-validator
rocrate-validator cache warm -p ro-crate-1.2 -p process-run-crate-0.5 # once, for the validation tests
pytest # run the full unit + integration suite
pytest --cov=flwrcrate --cov-report=term-missing # with a coverage report
```
Expand Down
2 changes: 1 addition & 1 deletion flwrcrate/crate_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def build_crate(captured: dict, crate_dir, metrics_log_path=None, model_path=Non
"""Assemble and write an RO-Crate. Returns the crate directory path."""
uri_map = uri_map or {}
crate_dir = Path(crate_dir)
crate = ROCrate()
crate = ROCrate(version="1.2")

crate.name = captured.get("app_name") or "Federated learning run"
crate.description = (
Expand Down
31 changes: 31 additions & 0 deletions flwrcrate/validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Validate RO-Crates with rocrate-validator.
Shared by the test suite and the end-to-end check, and meant for validating
crates after a run.
"""

from io import StringIO

from rocrate_validator import models, services
from rocrate_validator.utils.io_helpers.output.console import Console
from rocrate_validator.utils.io_helpers.output.text import TextOutputFormatter


def validate(crate_dir, profile, **settings):
"""Validate a crate folder against one profile: REQUIRED level, offline, extra checks on."""
return services.validate(services.ValidationSettings(
rocrate_uri=str(crate_dir),
profile_identifier=profile,
requirement_severity=models.Severity.REQUIRED,
offline=True,
extra_checks=True,
**settings,
))


def report(result):
"""The validator's full text report, which names the entity behind each issue."""
out = StringIO()
console = Console(file=out, width=120)
console.register_formatter(TextOutputFormatter())
console.print(result)
return out.getvalue()
Loading
Loading