Repository navigation
Offer the derived properties with a derived property element - #125
Merged
Merged
Conversation
…rty element The tests run every case file and every rejection case in the submodule's tests folder through a pipeline, and test the element's settings, logging, metadata and the checks it makes when a pipeline is built. The package is listed in ci/packages.json as not published, and the pipeline build and unit test steps include it. The element itself follows in the next commit.
fiftyone_pipeline_derivedproperty runs the scripts of derived-properties, built into the package from the submodule's scripts folder or given as files or text, and writes one property for each under derived. parse, validate, evaluate and canonical follow the reference tools of derived-properties function for function, reading YAML with the YAML core schema. The package is tested but not yet published.
This was referenced Nov 14, 2026
jwrosewell
marked this pull request as ready for review
November 15, 2026 04:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Python had no element for the derived properties that derived-properties defines, so a Python pipeline could not offer HumanConfidence or IsGdpr, whilst .NET, Rust and PHP can. This adds a
pipeline/fiftyone_pipeline_derivedpropertypackage whoseDerivedPropertyElementruns every script in thescriptsfolder of derived-properties, and tests that run every case file in itstestsfolder, the scripts undertests/invalidincluded.Closes #105.
What the specification says
All quotes are from derived-properties at cc306386, the head of its
mainand the commit the new submodule pins.docs/format-1.md, lines 4 to 6:
The one rule, lines 35 to 39:
When a property is absent, lines 451 to 455:
Lines 491 to 497, on an element added after a source it reads:
Writing YAML, lines 562 to 570:
Two places where the languages are allowed to differ, lines 590 to 605, being the line a fault names, and how strictly a JSON script is read:
docs/testing.md, lines 3 to 6:
Lines 177 and 196 to 197:
docs/authoring.md, lines 365 to 384 asks for one information line per script at build naming "the script, its version, the format number, where the script came from (a built in name, a file path or code) and the output property", and at debug level "the compiled model as canonical JSON". Lines 441 to 442 add a warning for a deprecated script. Lines 452 to 464 describe the check when the element is added, where "a source property that no earlier element supplies" and "two elements in one pipeline producing the same derived property name" fail the pipeline build. The word "earlier" there is at odds with format-1.md lines 491 to 494 above, and .NET, Rust and PHP accept an element that supplies the property wherever it sits, so Python follows the format reference, which calls itself the contract. derived-properties#19 asks for authoring.md to say the same.
README.md, lines 80 to 109 names the build parameters
ScriptsandScriptFiles, each "one string with commas between the names", where "a name that holds a comma is wrapped in double quotes".What changed
The reference implementations read were .NET,
pipeline/DerivedPropertyof 51degrees-dotnet, Rust, thederived-propertiescrate of 51degrees-rust, and PHP,pipeline/derivedpropertyof 51degrees-php. The Python code ports the reference tools the specification follows,tools/parse.mjs,validate.mjs,evaluate.mjsandcanonical.mjs, function for function, with the same fault paths and messages. Files are linked at the head, 9a8bd25d..gitmodulesadds derived-properties aspipeline/fiftyone_pipeline_derivedproperty/src/fiftyone_pipeline_derivedproperty/derived-propertieswith the relative URL../derived-properties, pinned at cc306386. It sits inside the package's source folder so that the package reads the built in scripts from it directly, with no copy to keep in step.MANIFEST.inandinclude_package_datainsetup.pyput the submodule'sscripts/*.yamlin the source distribution and the wheel, and nothing else from the submodule.yes,no,onandoffare booleans and a date is a date.parse.pyuses a loader that keeps only the core schema's null, boolean, integer and float forms, soyesstays the string"yes"as the reference tools read it, and it records the line of each mapping so a fault names its line. JSON scripts are read with thejsonmodule, the strict reading the specification allows.jsvalues.pyholds the JavaScript rules for numbers, equality and text that the reference tools rely on, so the evaluator gives the same answers.DerivedPropertyElementwrites oneAspectPropertyValueper script underderived, throughDerivedPropertyData, where names are matched without regard to case. A script with a source property missing has no value, and readingvalue()raises the message naming every missing property, which is the Python pipeline's no value error. It reads a source as Python elements hold it, carrying the source's own no value message, or the message of the exception an element raises for a property it does not have, and turning a weighted list into theValueandWeightshape the evaluator takes. A script reads what an earlier script of the element wrote. A request with no element data at all under a key a script reads is logged once for each script, as format-1.md lines 491 to 497 ask.scriptsandscript_filessettings, which a pipeline configuration file gives, matched without regard to case or underscores, soScriptsandScriptFileswork as the README names them.DerivedPropertyElementBuilderoffersadd_built_in_script,add_script_fileandadd_scriptin code, as .NET's builder does. Every fault in every script is reported in oneScriptValidationError. A setting the element does not have is refused, so a setting written wrongly cannot leave its scripts out without a word, and an element with no script is refused with a message saying how to give one.on_registrationrefuses the pipeline with the same messages as .NET, Rust and PHP where nothing supplies a source property or another element writes the same derived property, then logs the line and the canonical JSON authoring.md asks for, and a warning for a deprecated script. Where an element sits is not judged, and an element that lists no properties may supply any, because a cloud engine learns its properties from its first response.name,type,categoryanddescription, followed by the rest of the script'sOutputblock.ci/packages.jsonlists the package with"Publish": false, depending onfiftyone_pipeline_core, andpipeline/ci/build-project.ps1andpipeline/ci/run-unit-tests.ps1add it to the pipeline packages, so every build lints it and runs its tests, and Publish leaves it out. AGENTS.md, the README andpipeline/readme.mddescribe the package and the submodule.The tests, in
pipeline/fiftyone_pipeline_derivedproperty/tests, 224 in all:test_shared_cases.pyruns the 69 value cases (57 for HumanConfidence and 12 for IsGdpr) twice, once with the source values asAspectPropertyValueand once as plain values, each through a real pipeline with a source element giving the case's properties and the script the package carries, and the 29 rejection cases, each refused with a fault at every expected path and mentioning every expected fragment. A missing value must name exactly the properties the case lists.test_built_in_scripts.pychecks that the built in scripts are the submodule'sscriptsfolder and builds one element with every one of them.test_pipeline_check.pyfollows the Rust crate'stests/pipeline_check.rs.test_element.pyandtest_scripts.pycover reading the values Python elements give, logging, settings, metadata, the JSON bundler, YAML and JSON, type inference and conversion.Runs
Each throwaway run uses a workflow that runs the package's tox environments, as every build does and as Publish does, and pylint with the package's
.pylintrc, on Ubuntu with Python 3.10 and 3.14 and on Windows with Python 3.14. A fourth job builds the wheel and the source distribution, lists both, installs the wheel into an empty virtual environment withfiftyone_pipeline_corefrom PyPI, and runs the built in scripts.setup.py,tox.iniandMANIFEST.in, and the tests, and none of the package's code. Every test job stopped with five errors while collecting its five test files, four of themImportError: cannot import name 'DerivedPropertyElement'(or'DerivedPropertyElementBuilder')from 'fiftyone_pipeline_derivedproperty'and oneModuleNotFoundError: No module named 'fiftyone_pipeline_derivedproperty.canonical', and the build job failed with the sameImportErrorforDerivedPropertyElementBuilder.fiftyone_pipeline_derivedproperty/derived-properties/scripts/HumanConfidence.yamlandIsGdpr.yamlin the wheel, installedfiftyone_pipeline_core4.5.43 from PyPI beside it, built both scripts asHumanConfidence 0.8.0 from built in HumanConfidenceandIsGdpr 0.1.0 from built in IsGdpr, and answered IsGdprTrueforFR.rcwas merged in twice, at 853446b and at the head 9a8bd25d, each with no conflict and no change to the package.tmp/pr-matrix-125b, success. Configure, all ten Build and Test jobs (Ubuntu and Windows, Python 3.10 to 3.14) and Complete are green. All 11Setting 'PullRequestSha'lines name the head 9a8bd25d, and Configure printed "Left out 5 macOS entries".tmp/pr-matrix-125c, on the same head againstrcat cf520fe, which holds this week's other changes, success. Configure, all ten Build and Test jobs (Ubuntu and Windows, Python 3.10 to 3.14) and Complete are green,fiftyone_pipeline_derivedpropertyis built and its tests run in each job, all 11Setting 'PullRequestSha'lines name 9a8bd25, and Configure printed "Left out 5 macOS entries".macOS was left out of this draft's full build under the 9 November decision. The rc head is built with macOS before it merges into main.
What remains
FirstVersionand is"Publish": false. A new project on PyPI is hidden from search engines for its first 30 days, so when to publish it is a separate choice, tracked in #154.getValueAsBool, which answersvalue == "True"(ResultsBase.cpp lines 118 to 140), so a Boolean property stored asUnknownreaches Python asFalse. So a derived property script that compares a Boolean property with the text"Unknown"never finds it through that engine. PHP found the same and raised it as 51degrees-php#95. It needs a data file to check, so it was not checked here, and #153 tracks it for Python.Output.Namenaming that element. Format 1 as published at cc306386 asksOutput.Nameto match^[A-Za-z][A-Za-z0-9]*$, so Python follows the format and has no such scripts.