Skip to content

Add Pydantic v2 schema.org models for CodeMeta JSON-LD - #1

Merged
alee merged 20 commits into
mainfrom
copilot/code-meta-schema-implementation
Aug 19, 2026
Merged

alee merged 20 commits into
mainfrom
copilot/code-meta-schema-implementation

Conversation

Copilot AI commented Jun 4, 2026 •

Copy link
Copy Markdown
Contributor

This introduces a clean-room, Pydantic v2-only schema.org model layer for the CodeMeta subset used by the package. It removes any need for legacy pydantic.v1 schema models while preserving JSON-LD behavior and forward-compatible round-tripping of unknown properties.

  • Schema.org model layer

    • adds a shared SchemaOrgBase with Pydantic v2 config, JSON-LD aliases, and to_jsonld() / from_jsonld() helpers
    • implements the required schema.org subset only: Thing, Person, Organization, ContactPoint, PropertyValue, SoftwareSourceCode, and SoftwareApplication
    • keeps field typing explicit for relation fields such as author, publisher, identifier, and contactPoint
  • CodeMeta wrapper semantics

    • adds CodeMeta as a SoftwareSourceCode convenience wrapper rather than a distinct schema type
    • fixes wrapper defaults to always serialize as CodeMeta JSON-LD:
      • @context: https://w3id.org/codemeta/3.0
      • @type: SoftwareSourceCode
  • JSON-LD compatibility

    • supports @context, @type, and @id via aliases
    • preserves unknown schema.org properties with extra="allow" so non-modeled fields survive full round-trip serialization
    • accepts ISO date strings for CodeMeta date fields while allowing datetime.date internally
  • Fixtures and focused coverage

    • adds minimal and full CodeMeta fixtures covering multiple authors, structured DOI identifiers, dates, and unknown-field preservation
    • adds focused tests for alias handling, nested model parsing, validation errors, JSON-LD round-trips, and CodeMeta-specific serialization behavior
from codemeticulous.schemaorg import CodeMeta, Person

metadata = CodeMeta(
    name="My Tool",
    author=Person(name="Jane Smith", email="jane@example.com"),
    version="1.0.0",
)

payload = metadata.to_jsonld()
# {
#   "@context": "https://w3id.org/codemeta/3.0",
#   "@type": "SoftwareSourceCode",
#   "name": "My Tool",
#   "author": {"@type": "Person", "name": "Jane Smith", "email": "jane@example.com"},
#   "version": "1.0.0"
# }
Original prompt
# Copilot Prompt: CodeMeta-Focused Schema.org Models (Pydantic v2 Clean-Room Implementation)

## Context

You are working in the Python package:

```text
src/codemeticulous/

The project is migrating entirely to Pydantic v2.

The current CodeMeta implementation relies on a legacy schema.org model layer that uses
pydantic.v1.BaseModel. This dependency must be removed.

Your task is to create a clean-room implementation of a small subset of schema.org models
needed for CodeMeta support. Do not copy code from pydantic_schemaorg or any other project.


Goal

Create a minimal, production-quality, extensible schema.org model layer using Pydantic v2 only.

The implementation should:

  1. Eliminate all dependence on pydantic.v1.
  2. Support parsing and serialization of JSON-LD metadata.
  3. Support the subset of schema.org required for CodeMeta.
  4. Be designed so additional schema.org types can be added later without major refactoring.
  5. Preserve unknown schema.org properties for forward compatibility.

This is not intended to be a complete schema.org implementation.


Non-goals

Do NOT:

  • Implement the entire schema.org vocabulary.
  • Generate code automatically.
  • Add speculative schema.org types beyond those explicitly listed below.
  • Recreate the architecture of existing schema.org libraries.
  • Introduce any dependency besides Pydantic v2 and the Python standard library.

Pydantic Requirements

Use only Pydantic v2 APIs.

Allowed:

from pydantic import BaseModel, ConfigDict, Field

Not allowed:

from pydantic.v1 import ...

No deprecated v1 APIs.

All models should inherit a shared base class and use:

model_config = ConfigDict(
    populate_by_name=True,
    extra="allow",
    use_enum_values=True,
)

Unknown properties must round-trip successfully.


JSON-LD Requirements

Support the JSON-LD keywords:

@context
@type
@id

Use aliases:

context: str | None = Field(None, alias="@context")
type: Literal["Thing"] = Field("Thing", alias="@type")
id: str | None = Field(None, alias="@id")

The canonical serialization contract is:

model.model_dump(
    by_alias=True,
    exclude_none=True,
    mode="json",
)

Provide convenience helpers:

def to_jsonld(self) -> dict:
    ...

@classmethod
def from_jsonld(cls, data: dict):
    ...

from_jsonld() should simply delegate to model_validate().


Design Principles

Explicit typing

Use explicit unions rather than Any.

Preferred:

AuthorType = str | Person | Organization
AuthorField = AuthorType | list[AuthorType]

Avoid:

author: Any

Forward references

Use:

from __future__ import annotations

or equivalent typing techniques to avoid circular import issues.

Extensibility

The implementation should be organized around reusable base classes.

Future schema.org types should be easy to add by subclassing existing models.


Schema.org Types to Implement

Implement exactly these classes and no others.

Python class schema.org @type
SchemaOrgBase abstract
Thing Thing
Person Person
Organization Organization
ContactPoint ContactPoint
PropertyValue PropertyValue
SoftwareSourceCode SoftwareSourceCode
SoftwareApplication SoftwareApplication
CodeMeta SoftwareSourceCode

No additional schema.org classes should be introduced.


CodeMeta Wrapper Semantics

CodeMeta is NOT a distinct schema.org type.

It is a convenience wrapper around SoftwareSourceCode.

It should always serialize as:

{
  "@context": "https://w3id.org/codemeta/3.0",
  "@type": "SoftwareSourceCode"
}

Implement:

class CodeMeta(SoftwareSourceCode):
    context: str = Field(
        "https://w3id.org/codemeta/3.0",
        alias="@context",
    )

    type: Literal["SoftwareSourceCode"] = Field(
        "SoftwareSourceCode",
        alias="@type",
    )

The defaults should be treated as fixed conventions of the wrapper class.
Changing them should require subclassing rather than ordinary usage.


Fields

SchemaOrgBase

@id
@type

Thing

name
description
url
identifier

Type:

identifier:
    str
    | PropertyValue
    | list[str | PropertyValue]
    | None

Do not simplify identifier to string-only.

CodeMeta frequently uses structured identifiers such as DOI metadata.


Person

email
affiliation
contactPoint

Types:

affiliation:
    Organization | str | None

contactPoint:
    ContactPoint
    | list[ContactPoint]
    | None

Organization

email
contactPoint

ContactPoint

contactType
email
url

--...

Copilot AI changed the title [WIP] Implement clean-room schema.org models with Pydantic v2 Add Pydantic v2 schema.org models for CodeMeta JSON-LD Jun 4, 2026
Copilot finished work on behalf of alee June 4, 2026 03:36
Copilot AI requested a review from alee June 4, 2026 03:36
@alee
alee requested a review from sgfost June 4, 2026 03:43
@sgfost

sgfost commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

👍 its definitely easiest to just skip figuring out how to migrate the codegen stuff in pydantic_schemaorg and keep going generating it all like this. The only real issue I had while back was migrating whatever deferred importing magic was happening (if you have all of the possible schema.org type models that can constitute SoftwareSourceCode imported at runtime its unusably slow), but that will probably also be much easier just generating everything from scratch

alee added 8 commits June 4, 2026 12:22
- rename package to codemeta_datamodel
- add containerization and support for package publishing and uv
- add initial AGENTS.md scaffolding
address permissions errors with generated files on the volume mounts
being owned by root instead
Add CreativeWork, PostalAddress, Role, ComputerLanguage, MediaObject,
DataFeed, Review, and ScholarlyArticle types. Fix SoftwareApplication
to extend CreativeWork instead of SoftwareSourceCode (schema.org treats
them as siblings). Update field types per spec: programmingLanguage
accepts ComputerLanguage, license/citation accept CreativeWork, and
author/contributor/maintainer now include Role.

Also fix from_jsonld return type to use Self, add sameAs to Thing,
document CodeMeta version-lock constraint in its docstring, fix spec
path reference (codemeta_pydantic → codemeta_datamodel), and add
Conventional Commits directive to AGENTS.md.
Build and push the Docker image to GitHub Container Registry on
release publication. Tags follow semver (major.minor.patch, major.minor,
major) with 'latest' applied to stable releases only. Uses GHA cache
for layer reuse across builds.
@alee
alee force-pushed the copilot/code-meta-schema-implementation branch from bd2c70d to ec4400b Compare August 17, 2026 03:24
alee and others added 4 commits August 17, 2026 02:17
…odels

- Rename package codemeta_datamodel -> pydantic_codemeta
- Rename pyproject distribution codemeticulous -> pydantic-codemeta
- Add VersionedLanguage type and broaden programmingLanguage to accept it
- Broaden many CreativeWork/SoftwareSourceCode fields to accept lists
- Add SoftwareSourceCode fields: applicationCategory, applicationSubCategory,
  fileSize, permissions, releaseNotes, softwareVersion, fileFormat,
  isAccessibleForFree, position, isPartOf, hasPart, encoding, creator,
  editor, copyrightHolder, copyrightYear
- Add CodeMeta before-validators: map bare type/id keys, collapse JSON-LD
  @context dicts, coalesce embargoDate/contIntegration/creator into v3 terms
- Add SchemaOrgBase.dict/json/yaml serialization helpers and pyyaml dep
- Allow SoftwareApplication as CodeMeta @type
- Update AGENTS.md and docs to reflect new package name

TODO: full alignment with codemeticulous client usage
- add explicit CodeMetaV3 and v3/canonical adapters
- move JSON-LD normalization and legacy migrations into pure helpers
- preserve open extensions while enforcing the v3 context and source-code root
- broaden CodeMeta relation ranges and support unknown CreativeWork subtypes
- reject known non-CreativeWork nodes in CreativeWork relationship fields
- document model boundaries and add focused round-trip, migration, and validation coverage
- separate the open CodeMeta model from its v3-specific binding
- make ordinary JSON-LD parsing pure and legacy migration explicit
- adopt snake_case Python fields with schema.org and CodeMeta aliases
- preserve structured contexts, unknown JSON values, and explicit nulls
- enforce alias collisions, relationship cardinalities, and type boundaries
- remove legacy serialization helpers and the PyYAML dependency
- expand the living specification, README, and regression coverage
- reject coercion of Python-only values in raw JSON fields
- enforce CreativeWork type invariants and strict scalar ranges
- preserve structured PropertyValue and DataFeed values
- expand cardinality, validation, and round-trip coverage
- test supported Python versions and validate built distributions
- include JSON test fixtures in source distributions
- declare typing-extensions for every supported Python version
- align contributor guidance, commands, and documented requirements
- reduce GitHub Actions permissions

Co-authored-by: Codex <codex@openai.com>
@alee
alee marked this pull request as ready for review August 18, 2026 22:45
alee added 5 commits August 18, 2026 16:37
- pin gh actions to SHAs
- minor readme refinement, prepwork for release
- bump python min to 3.11 since 3.10 is EOL this october
- add codemeta and citation.cff dogfood
- add format, lint, validation checks
@alee
alee merged commit b4c7226 into main Aug 19, 2026
5 checks passed
@alee
alee deleted the copilot/code-meta-schema-implementation branch August 19, 2026 03:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants