Skip to content

Instantly share code, notes, and snippets.

@kratsg
Created April 2, 2020 18:52
Show Gist options
  • Save kratsg/96cec81df8c0d78ebdf14bf7b800e938 to your computer and use it in GitHub Desktop.
Save kratsg/96cec81df8c0d78ebdf14bf7b800e938 to your computer and use it in GitHub Desktop.
python-jsonschema recursive RefResolver class
>>> import jsonschema
>>> resolver = jsonschema.RefResolver(base_uri='https://scikit-hep.org/pyhf/schemas/1.0.0/', referrer='workspace.json')
>>> rec_resolver = RecursiveRefResolver(base_uri='https://scikit-hep.org/pyhf/schemas/1.0.0/', referrer='workspace.json')
>>> resolver.resolve('https://scikit-hep.org/pyhf/schemas/1.0.0/workspace.json')
('https://scikit-hep.org/pyhf/schemas/1.0.0/workspace.json',
{'$schema': 'http://json-schema.org/draft-06/schema#',
'$id': 'https://scikit-hep.org/pyhf/schemas/1.0.0/workspace.json',
'$ref': 'defs.json#/definitions/workspace'})
>>> rec_resolver.resolve('https://scikit-hep.org/pyhf/schemas/1.0.0/workspace.json')
{'$schema': 'http://json-schema.org/draft-06/schema#',
'$id': 'https://scikit-hep.org/pyhf/schemas/1.0.0/workspace.json',
'type': 'object',
'properties': {'channels': {'type': 'array',
'items': {'type': 'object',
'properties': {'name': {'type': 'string'},
'samples': {'type': 'array',
'items': {'type': 'object',
'properties': {'name': {'type': 'string'},
'data': {'type': 'array', 'items': {'type': 'number'}, 'minItems': 1},
'modifiers': {'type': 'array',
'items': {'anyOf': [{'type': 'object',
'properties': {'name': {'type': 'string'},
'type': {'const': 'histosys'},
'data': {'type': 'object',
'properties': {'lo_data': {'type': 'array',
'items': {'type': 'number'},
'minItems': 1},
'hi_data': {'type': 'array',
'items': {'type': 'number'},
'minItems': 1}},
'required': ['lo_data', 'hi_data']}},
'required': ['name', 'type', 'data'],
'additionalProperties': False},
{'type': 'object',
'properties': {'name': {'const': 'lumi'},
'type': {'const': 'lumi'},
'data': {'type': 'null'}},
'required': ['name', 'type', 'data'],
'additionalProperties': False},
{'type': 'object',
'properties': {'name': {'type': 'string'},
'type': {'const': 'normfactor'},
'data': {'type': 'null'}},
'required': ['name', 'type', 'data'],
'additionalProperties': False},
{'type': 'object',
'properties': {'name': {'type': 'string'},
'type': {'const': 'normsys'},
'data': {'type': 'object',
'properties': {'lo': {'type': 'number'},
'hi': {'type': 'number'}},
'required': ['lo', 'hi']}},
'required': ['name', 'type', 'data'],
'additionalProperties': False},
{'type': 'object',
'properties': {'name': {'type': 'string'},
'type': {'const': 'shapefactor'},
'data': {'type': 'null'}},
'required': ['name', 'type', 'data'],
'additionalProperties': False},
{'type': 'object',
'properties': {'name': {'type': 'string'},
'type': {'const': 'shapesys'},
'data': {'type': 'array',
'items': {'type': 'number'},
'minItems': 1}},
'required': ['name', 'type', 'data'],
'additionalProperties': False},
{'type': 'object',
'properties': {'name': {'type': 'string'},
'type': {'const': 'staterror'},
'data': {'type': 'array',
'items': {'type': 'number'},
'minItems': 1}},
'required': ['name', 'type', 'data'],
'additionalProperties': False}]}}},
'required': ['name', 'data', 'modifiers'],
'additionalProperties': False},
'minItems': 1}},
'required': ['name', 'samples'],
'additionalProperties': False},
'minItems': 1},
'measurements': {'type': 'array',
'items': {'type': 'object',
'properties': {'name': {'type': 'string'},
'config': {'type': 'object',
'properties': {'poi': {'type': 'string'},
'parameters': {'type': 'array',
'items': {'type': 'object',
'properties': {'name': {'type': 'string'},
'inits': {'type': 'array',
'items': {'type': 'number'},
'minItems': 1},
'bounds': {'type': 'array',
'items': {'type': 'array',
'items': {'type': 'number', 'minItems': 2, 'maxItems': 2}},
'minItems': 1},
'auxdata': {'type': 'array',
'items': {'type': 'number'},
'minItems': 1},
'factors': {'type': 'array',
'items': {'type': 'number'},
'minItems': 1},
'sigmas': {'type': 'array',
'items': {'type': 'number'},
'minItems': 1},
'fixed': {'type': 'boolean'}},
'required': ['name'],
'additionalProperties': False}}},
'required': ['poi', 'parameters'],
'additionalProperties': False}},
'required': ['name', 'config'],
'additionalProperties': False},
'minItems': 1},
'observations': {'type': 'array',
'items': {'type': 'object',
'properties': {'name': {'type': 'string'},
'data': {'type': 'array', 'items': {'type': 'number'}, 'minItems': 1}},
'required': ['name', 'data'],
'additionalProperties': False},
'minItems': 1},
'version': {'const': '1.0.0'}},
'additionalProperties': False,
'required': ['channels', 'measurements', 'observations', 'version']}
import jsonschema
from urllib.parse import urljoin
import copy.deepcopy
class RecursiveRefResolver(jsonschema.RefResolver):
def resolve(self, ref):
top_ref, top_obj = super(RecursiveRefResolver, self).resolve(ref)
def _walk_dict(obj, ref):
out_obj = copy.deepcopy(obj)
for key in obj:
if key == '$ref':
path = urljoin(ref, out_obj.pop(key))
new_ref, new_obj = super(RecursiveRefResolver, self).resolve(path)
resolved_obj = _walk_dict(new_obj, new_ref)
out_obj.update(resolved_obj)
elif isinstance(obj[key], dict):
out_obj[key] = _walk_dict(obj[key], ref)
elif isinstance(obj[key], list):
out_obj[key] = [_walk_dict(item, ref) if isinstance(item, dict) else item for item in obj[key]]
else:
pass
return out_obj
return _walk_dict(top_obj, top_ref)
@cwells
Copy link

cwells commented Jul 30, 2022

Hell yes. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment