def __init__(self, directive, arguments, options, content, lineno, content_offset, block_text, state, state_machine): assert directive == 'jsonschema' self.options = options self.state = state self.lineno = lineno self.statemachine = state_machine if len(arguments) == 1: filename, pointer = self._splitpointer(arguments[0]) if filename != '': self._load_external(filename) else: self._load_internal(content) if pointer: self.schema = resolve_pointer(self.schema, pointer) else: self._load_internal(content) hidden_paths = self.options.get('hide') if hidden_paths is not None: orig_schema = json.loads(json.dumps(self.schema)) for hidden_path in hidden_paths.split(' '): ptr = JsonPointer(hidden_path) parent, name = ptr.to_last(self.schema) del parent[name] shown_paths = self.options.get('show') for shown_path in shown_paths.split(' '): ptr = JsonPointer(shown_path) orig_parent = orig_schema current_parent = self.schema for part in ptr.parts[:-1]: orig_parent = ptr.walk(orig_parent, part) try: current_parent = ptr.walk(current_parent, part) except JsonPointerException: if isinstance(orig_parent, Sequence): new_entry = [] elif isinstance(orig_parent, Mapping): new_entry = OrderedDict() else: raise Exception('Unsupported type parent') if isinstance(current_parent, MutableSequence): current_parent.append(new_entry) elif isinstance(current_parent, MutableMapping): current_parent[part] = new_entry current_parent = new_entry if isinstance(current_parent, MutableSequence): current_parent.append(ptr.resolve(orig_schema)) elif isinstance(current_parent, MutableMapping): current_parent[ptr.parts[-1]] = ptr.resolve(orig_schema) else: raise Exception('Unsupported type parent')
def _set_val( pointer: str, val: object, context: dict, root: Union[dict, None] = None, context_pointer: Union[str, None] = None, ): """ This function given a *pointer* (jsonpointer RFC 6901 or relative json pointer) to a property in a python object, sets the supplied value in-place in the *context* object within *root* object. The object we are adding data to is *root*. The object may or may not have any of the intermediate structure to fully insert the desired property. For example: consider pointer = "0/prop1/prop2" val = {"more": "props"} context = {"Pid": 1} root = {"participants": [context]} context_pointer = "/participants/0" First we see an `0` in pointer which denotes update should be within context. So no need to jump higher than context. So we truncate path to = "prop1/prop2" We see it's a string, so we know we are entering object's *prop1* as a property: { "participants": [{ "prop1": ... }] } It's our whole Trial object, and ... here denotes our current descend. Now we truncate path one step further to = "prop2" Go down there and set `val={"more": "props"}` : { "participants": [{ "prop1": { "prop2": {"more": "props"} } }] } While context is a sub-part of that: { "prop1": { "prop2": {"more": "props"} } } Args: pointer: relative jsonpointer to the property being set within current `context` val: the value being set context: the python object relatively to which val is being set root: the whole python object being constructed, contains `context` context_pointer: jsonpointer of `context` within `root`. Needed to jump up. Returns: Nothing """ # don't do anything for None value, asserting None is permissable # is handled by the "allow_empty" functionality. if val is None: return # special case to set context doc itself if pointer.rstrip("#") == "": context.update(val) return # fill defaults if root is None: root = context if context_pointer is None: context_pointer = "/" # first we need to convert pointer to an absolute one # if it was a relative one (https://tools.ietf.org/id/draft-handrews-relative-json-pointer-00.html) if pointer.startswith("/"): jpoint = JsonPointer(pointer) doc = context else: # parse "relative" jumps up jumpups, slash, rem_pointer = pointer.partition("/") try: jumpups = int(jumpups.rstrip("#")) except ValueError: jumpups = 0 # check that we don't have to jump up more than we dived in already assert jumpups <= context_pointer.rstrip("/").count( "/" ), f"Can't set value for pointer {pointer} too many jumps up from current context: {context_pointer}" # and we'll go down remaining part of `pointer` from there jpoint = JsonPointer(slash + rem_pointer) if jumpups > 0: # new context pointer higher_context_pointer = "/".join( context_pointer.strip("/").split("/")[: -1 * jumpups] ) # making jumps up, by going down context_pointer but no all the way down if higher_context_pointer == "": doc = root assert ( len(jpoint.parts) > 0 ), f"Can't update root object (pointer {pointer})" else: try: doc = resolve_pointer(root, "/" + higher_context_pointer) except Exception as e: raise Exception(e) else: doc = context jp_parts = jpoint.parts # then we update it for i, part in enumerate(jp_parts[:-1]): try: doc = jpoint.walk(doc, part) except (JsonPointerException, IndexError) as e: # means that there isn't needed sub-object in place, so create one # look ahead to figure out a proper type that needs to be created next_thing = __jpointer_get_next_thing(jp_parts[i + 1]) # insert it __jpointer_insert_next_thing(doc, jpoint, part, next_thing) # and `walk` it again - this time should be OK doc = jpoint.walk(doc, part) if isinstance(doc, EndOfList): actual_doc = __jpointer_get_next_thing(jp_parts[i + 1]) __jpointer_insert_next_thing(doc.list_, jpoint, "-", actual_doc) doc = actual_doc __jpointer_insert_next_thing(doc, jpoint, jp_parts[-1], val)