Vocabularies#

What a property’s value may be, what it means, and what a class descends from. rs-metadata answers none of these itself; they are fetched from upstream and read here.

What each property’s value may be.

Merged from every declared vocabulary by scripts/vendor_reference_data.py. CodeMeta is authoritative where both it and schema.org define a property, because it narrows schema.org for its own profile; schema.org supplies the properties CodeMeta leaves out of its context.

rs_metadata.vocabulary.properties.codemeta_terms()[source]#

The CodeMeta 3.1 term index derived from its JSON-LD context.

Return type:

dict[str, Any]

rs_metadata.vocabulary.properties.codemeta_types()[source]#

What each CodeMeta property’s value may be, derived from upstream.

Returns:

dict – Property name mapped to {"range": [...], "parent": str}, plus byParent for the one property CodeMeta types differently depending on where it appears.

Return type:

dict[str, dict[str, Any]]

Notes

Derived by scripts/vendor_reference_data.py from CodeMeta’s own property table, never transcribed by hand. CodeMeta narrows schema.org for its own profile — maintainer is Person here but Person-or-Organization upstream — so its table is the authority.

Examples

A range containing Text means a bare string is conformant:

>>> codemeta_types()["developmentStatus"]["range"]
['Text']

A range without it means the value must be a node or an absolute URL, because a bare string would expand to an unresolvable relative IRI:

>>> codemeta_types()["license"]["range"]
['CreativeWork', 'URL']
>>> "Text" in codemeta_types()["author"]["range"]
False

identifier is typed differently inside a Person:

>>> codemeta_types()["identifier"]["byParent"]
{'Person': ['URL']}
rs_metadata.vocabulary.properties.type_entry(name)[source]#

The type table entry for a property, however it is spelled.

Parameters:

name (str) – Property name as written in the document, with or without a prefix.

Returns:

dict or None – The entry, or None when neither vocabulary defines the property.

Return type:

dict[str, Any] | None

Notes

schema:featureList and featureList are the same property. CodeMeta leaves featureList out of its context, which is exactly why this profile writes it prefixed, and why looking it up by the prefixed spelling has to work.

Examples

>>> type_entry("license")["range"]
['CreativeWork', 'URL']

A prefixed spelling resolves to the same entry as the bare one:

>>> type_entry("schema:featureList") == type_entry("featureList")
True
>>> type_entry("schema:featureList")["range"]
['Text', 'URL']

A property neither vocabulary defines has no entry:

>>> type_entry("somethingInvented") is None
True
rs_metadata.vocabulary.properties.agent_properties()[source]#

Every CodeMeta property whose values are people or organizations.

Returns:

tuple of str – Property names, in CodeMeta’s own order.

Return type:

tuple[str, …]

Notes

Derived from the type table rather than listed by hand. An earlier hand-written list covered four properties and silently missed six that CodeMeta types identically, so the list is now a consequence of the data.

Only properties CodeMeta declares on the record itself are returned. affiliation is Organization-valued too, but it exists on a Person rather than on the document, so it is reached by walking into an agent rather than by looking for it at the top level.

Examples

>>> properties = agent_properties()
>>> len(properties)
10
>>> "author" in properties and "funder" in properties
True

The six a hand-written list had missed:

>>> sorted(set(properties) - {"author", "maintainer", "contributor",
...                           "copyrightHolder"})
['editor', 'funder', 'producer', 'provider', 'publisher', 'sponsor']

A nested-only property is not one of them:

>>> "affiliation" in properties
False
rs_metadata.vocabulary.properties.allows_text(name)[source]#

Whether a bare scalar is a conformant value for a property.

Parameters:

name (str) – CodeMeta property name.

Returns:

bool – True when the property’s range includes a literal type, and for properties CodeMeta does not define at all — an unknown property gets no type complaint on top of the unknown-property one.

Return type:

bool

Examples

>>> allows_text("developmentStatus"), allows_text("applicationCategory")
(True, True)
>>> allows_text("license"), allows_text("author")
(False, False)

Dates and numbers are literals, so a bare value is right for them — this is not only about Text:

>>> allows_text("datePublished"), allows_text("copyrightYear")
(True, True)

URL alone does not qualify, because an arbitrary string is not a URL:

>>> allows_text("codeRepository")
False

Unknown properties are not second-guessed:

>>> allows_text("somethingInvented")
True

schema.org’s class hierarchy.

The hierarchy is what makes subtyping possible. CodeMeta types citation as CreativeWork; schema.org says a ScholarlyArticle is an Article is a CreativeWork, so one belongs there. Without this, accepting only the exact class name is a false alarm about correct metadata.

rs_metadata.vocabulary.classes.schema_org_classes()[source]#

schema.org’s class hierarchy, and the properties of the classes in use.

Returns:

dict – Class name mapped to ancestors, and for classes any range names, a properties map from property name to its range.

Return type:

dict[str, Any]

Examples

>>> schema_org_classes()["ScholarlyArticle"]["ancestors"]
['Article', 'CreativeWork', 'Thing']
rs_metadata.vocabulary.classes.subclasses(name)[source]#

Every class schema.org derives from this one, directly or not.

Parameters:

name (str) – A class name, for example CreativeWork.

Returns:

tuple of str – Descendant class names, sorted.

Return type:

tuple[str, …]

Notes

Used to express subtyping in the generated schema: a property typed CreativeWork accepts an @type from this list, so one jsonschema pass enforces what would otherwise need the validator.

Examples

>>> "ScholarlyArticle" in subclasses("CreativeWork")
True
>>> "Person" in subclasses("CreativeWork")
False
>>> subclasses("ComputerLanguage")
()
rs_metadata.vocabulary.classes.satisfies(declared, expected)[source]#

Whether a node of class declared can stand where expected is asked for.

Parameters:
  • declared (str) – The node’s own @type.

  • expected (str) – A class named in the property’s range.

Returns:

bool – True when the classes are the same, or when declared is a subclass of expected.

Return type:

bool

Notes

CodeMeta types citation as CreativeWork. schema.org says a ScholarlyArticle is an Article is a CreativeWork, so one belongs there. Comparing the names alone would reject it, which is a false alarm about correct metadata.

Examples

>>> satisfies("CreativeWork", "CreativeWork")
True
>>> satisfies("ScholarlyArticle", "CreativeWork")
True
>>> satisfies("SoftwareSourceCode", "CreativeWork")
True

Subtyping runs one way only, and unrelated classes stay unrelated:

>>> satisfies("CreativeWork", "ScholarlyArticle")
False
>>> satisfies("Person", "Organization")
False

Schemas, vocabularies and rule data read at runtime.

Two kinds of file live in data/. Vendored ones have an upstream, a checksum and a refresh script. Project-owned ones are maintained by hand and the refresh script never touches them; a contract test keeps the two sets disjoint.

rs_metadata.vocabulary.documents.codemeta_versions()[source]#

Which @context values are recognized, and what 2.0 terms became.

Returns:

dict – current, currentMajor, and a contexts map from context URL to version.

Return type:

dict[str, Any]

Examples

Every 3.x context denotes the current vocabulary — 3.0 and 3.1 resolve to byte-identical documents, so a 3.0 record is not out of date:

>>> versions = codemeta_versions()
>>> versions["contexts"]["https://w3id.org/codemeta/3.0"]
'3.0'
>>> versions["currentMajor"]
'3'

Earlier CodeMeta is not listed, and is reported as an unrecognized context:

>>> any(v.startswith("2") for v in versions["contexts"].values())
False
rs_metadata.vocabulary.documents.cff_schema()[source]#

The official Citation File Format 1.2.0 JSON Schema.

Return type:

dict[str, Any]

rs_metadata.vocabulary.documents.biotools_schema()[source]#

The official bio.tools JSON Schema.

Return type:

dict[str, Any]

rs_metadata.vocabulary.documents.codemeta_crosswalks()[source]#

CodeMeta’s own crosswalk tables, keyed by the mapping id that uses them.

Each entry is {"source", "format", "properties"}, where properties maps a CodeMeta property to the source fields CodeMeta says correspond to it. Vendored so a mapping’s claim about its own provenance can be checked rather than believed.

Return type:

dict[str, Any]

rs_metadata.vocabulary.documents.profile_schema()[source]#

The LUMC CodeMeta profile schema.

Return type:

dict[str, Any]

rs_metadata.vocabulary.documents.report_schema()[source]#

The JSON schema describing rs-metadata’s own report format.

Return type:

dict[str, Any]

rs_metadata.vocabulary.documents.spdx_licenses()[source]#

SPDX identifiers mapped to name, deprecation status and OSI approval.

Return type:

dict[str, Any]

rs_metadata.vocabulary.documents.vocabularies()[source]#

Controlled vocabularies with no stable machine-readable upstream form.

Return type:

dict[str, Any]

rs_metadata.vocabulary.documents.provenance()[source]#

Where each vendored file came from, and when.

Return type:

dict[str, Any]

rs_metadata.vocabulary.documents.PROJECT_OWNED_DATA = ('placeholders.json',)#

Files in data/ that rs-metadata maintains itself.

rs_metadata.vocabulary.documents.format_hints()[source]#

How other packaging ecosystems spell things rs-metadata maps.

Returns:

dict – rLicenseAliases mapping CRAN license strings to SPDX identifiers, and pyprojectUrlRoles mapping CodeMeta concepts to the PEP 621 [project.urls] labels that denote them.

Return type:

dict[str, Any]

Examples

>>> format_hints()["rLicenseAliases"]["aliases"]["gpl-3"]
'GPL-3.0-only'
>>> "repository" in format_hints()["pyprojectUrlRoles"]["labels"]["source"]
True
rs_metadata.vocabulary.documents.concept_order()[source]#

The order concepts are reported in.

Returns:

tuple of str – Mandatory fields first, in the order the profile declares them, then recommended ones. Concepts absent from both sort after these, alphabetically, so adding a property never makes the report unstable.

Return type:

tuple[str, …]

Notes

Derived from the profile rather than kept in a file of its own: the profile already lists its fields in a deliberate order, identity before provenance before technical detail, and repeating that order elsewhere is one more thing to keep in step.

Examples

>>> concept_order()[:3]
('name', 'description', 'version')
rs_metadata.vocabulary.documents.placeholders()[source]#

Template values that get left behind, and how to recognize them.

Returns:

dict – orcid, doiPrefix, hosts, and patterns carrying regex sources compiled by rs_metadata.profile.placeholders.

Return type:

dict[str, Any]

Examples

>>> placeholders()["orcid"]
'0000-0000-0000-0000'
>>> "example.com" in placeholders()["hosts"]
True
rs_metadata.vocabulary.documents.spdx_index()[source]#

Case-insensitive lookup from an SPDX identifier to its canonical case.

SPDX identifiers are case-sensitive by specification, but people write apache-2.0 constantly. Recognising the value and reporting the canonical spelling is more useful than rejecting it outright.

Return type:

dict[str, str]

rs_metadata.vocabulary.documents.profile_field_policy()[source]#

The mandatory and recommended field lists, read from the profile schema.

The schema is the single source of truth. Duplicating these lists in Python would let documentation, validator and schema drift apart.

Returns:

dict – mandatory, recommended and featureListAliases lists.

Return type:

dict[str, list[str]]

Examples

>>> policy = profile_field_policy()
>>> len(policy["mandatory"])
10
>>> "license" in policy["mandatory"]
True
>>> "keywords" in policy["recommended"]
True

The mandatory list matches the schema’s own required keyword, which a contract test enforces:

>>> set(policy["mandatory"]) <= set(profile_schema()["required"])
True