1

I am using Cerberus to validate some YAML files that might look like this:

fleet.yml

fleet_city: "New York"
vehicles:
  vehicle_1:
    car:
      # License plate required for car
      license_plate: "ABC123"
      cargo: 10

  vehicle_2:
    # All vehicle types can have optional remarks block
    remarks:
      active: true

    car:
      license_plate: "XYZ789"
      # Cargo is optional

  # Vehicle names (dict keys) are arbitrary
  cool_ride:
    remarks:
      active: false

    boat:
      # Required
      seaworthy: true

      # not required
      sails: 3

The general structure is that there are some file-wide values like fleet_city, and then a block called vehicles. The latter is a dictionary with vehicle names as keys. Every vehicle type has its own schema, although they're all dictionaries. I came up with this schema to formalize this:

schema.yml

fleet_city: 
  type: string

vehicles:
  type: dict

  keysrules:
    # Vehicle name constraints
    type: string
    regex: '[A-Za-z\d_]+'

  valuesrules:
    type: dict

    schema:
      # Optional remarks block
      remarks:
        type: dict

      # Car schema
      car:
        schema:
          license_plate:
            type: string
            required: true

          cargo:
            type: integer
            required: false

      # Boat schema
      boat:
        schema:
          seaworthy:
            type: boolean
            required: true

          propulsion:
            type: dict

            schema:
              sails:
                type: integer
                required: false

Which I can test with:

import yaml
from cerberus import Validator

with open('schema.yml') as f:
    schema = yaml.safe_load(f)
v = Validator(schema)

with open('fleet.yml') as f:
    fleet = yaml.safe_load(f)

print(v.validate(fleet))
print(v.errors)

Which correctly returns True. However, in the input every vehicle is supposed to have one type (either one car block or one boat block, not both). This rule is not reflected in the schema above. I tried to add it like so:

schema2.yml

fleet_city: 
  type: string

vehicles:
  type: dict

  keysrules:
    # Vehicle name constraints
    type: string
    regex: '[A-Za-z\d_]+'

  valuesrules:
    type: dict

    schema:
      # Optional remarks block
      remarks:
        type: dict

      oneof_schema:
        # Car schema
      - car:
        schema:
          license_plate:
            type: string
            required: true

          cargo:
            type: integer
            required: false

        # Boat schema
      - boat:
        schema:
          seaworthy:
            type: boolean
            required: true

          propulsion:
            type: dict

            schema:
              sails:
                type: integer
                required: false

From what I can tell, I have followed the syntax given in the docs but schema2.yml causes:

cerberus.schema.SchemaError: {'vehicles': [{'valuesrules': [{'schema': ['no definitions validate', {'anyof definition 0': [{'oneof': ['must be of dict type']}], 'anyof definition 1': [{'oneof': [{'schema': ['no definitions validate', 'no definitions validate', {'anyof definition 0': [{'car': ['null value not allowed'], 'schema': [{'cargo': ['unknown rule'], 'license_plate': ['unknown rule'], 'propulsion': ['unknown rule'], 'seaworthy': ['unknown rule']}], 'boat': ['null value not allowed']}], 'anyof definition 1': [{'car': ['unknown rule'], 'boat': ['unknown rule']}]}]}], 'remarks': ['unknown rule']}]}]}]}]}

Which doesn't really help.

One workaround is to use excludes for car in boat and vice versa, but that approach will fall apart if we have not just 2 vehicle types but many.

What is the issue in my schema2.yml and how can I fix it?

Donentolon
  • 1,363
  • 1
  • 10
  • 17

0 Answers0