def __init__(self, specs, url=None): """ Construct a JSON reference resolver. The resolved specs are in the `specs` member after a call to `resolve_references` has been made. If a URL is given, it is used as a base for calculating the absolute URL of relative file references. :param dict specs: The parsed specs in which to resolve any references. :param str url: [optional] The URL to base relative references on. """ import copy self.specs = copy.deepcopy(specs) self.url = url if self.url: self.parsed_url = _url.absurl(self.url) self._url_key = _url.urlresource(self.parsed_url) else: self.parsed_url = self._url_key = None self.__resolution_status = self.__RS_UNRESOLVED self.__recursion_protection = set()
def _fetch_url(self, url): """ Fetch the parsed contents of the given URL. Uses a caching mechanism so that each URL is only fetched once. """ url_key = _url.urlresource(url) # Same URL key means it's the current file if url_key == self._url_key: return url, self.specs # For all other URLs, we might have a cached parser around already. resolver = self.__reference_cache.get(url_key, None) # If we don't have a parser for the url yet, create and cache one. if not resolver: resolver = RefResolver(_url.fetch_url(url), url) self.__reference_cache[url_key] = resolver # Resolve references *after* (potentially) adding the resolver to the # cache. This together with the __recursion_protection allows us to detect # and report recursions and bad references. resolver.resolve_references() # That's it! return url, resolver.specs
def __init__(self, specs, url): """ Construct a JSON reference translator. The translated specs are in the `specs` member after a call to `translate_references` has been made. If a URL is given, it is used as a base for calculating the absolute URL of relative file references. :param dict specs: The parsed specs in which to translate any references. :param str url: [optional] The URL to base relative references on. """ import copy self.specs = copy.deepcopy(specs) self.__strict = True self.__reference_cache = {} self.__collected_references = {} if url: self.url = _url.absurl(url) url_key = (_url.urlresource(self.url), self.__strict) # If we have a url, we want to add ourselves to the reference cache # - that creates a reference loop, but prevents child resolvers from # creating a new resolver for this url. self.__reference_cache[url_key] = self.specs else: self.url = None
def __init__(self, specs, url=None, **options): """ Construct a JSON reference resolver. The resolved specs are in the `specs` member after a call to `resolve_references` has been made. If a URL is given, it is used as a base for calculating the absolute URL of relative file references. :param dict specs: The parsed specs in which to resolve any references. :param str url: [optional] The URL to base relative references on. :param dict reference_cache: [optional] Reference cache to use. When encountering references, nested RefResolvers are created, and this parameter is used by the RefResolver hierarchy to create only one resolver per unique URL. If you wish to use this optimization across distinct RefResolver instances, pass a dict here for the RefResolvers you create yourself. It's safe to ignore this parameter in other cases. :param int recursion_limit: [optional] set the limit on recursive references. The default is 1, indicating that an element may be refered to exactly once when resolving references. When the limit is reached, the recursion_limit_handler is invoked. :param callable recursion_limit_handler: [optional] A callable that gets invoked when the recursion_limit is reached. Defaults to raising ResolutionError. Receives the recursion_limit as the first parameter, and the parsed reference URL as the second. As the last parameter, it receives a tuple of references that have been detected as recursions. :param str encoding: [optional] The encoding to use. If not given, detect_encoding is used to determine the encoding. :param int resolve_types: [optional] Specify which types of references to resolve. Defaults to RESOLVE_ALL. """ import copy self.specs = copy.deepcopy(specs) self.url = url self.__reclimit = options.get('recursion_limit', 1) self.__reclimit_handler = options.get('recursion_limit_handler', default_reclimit_handler) self.__reference_cache = options.get('reference_cache', {}) if self.url: self.parsed_url = _url.absurl(self.url) self._url_key = _url.urlresource(self.parsed_url) # If we have a url, we want to add ourselves to the reference cache # - that creates a reference loop, but prevents child resolvers from # creating a new resolver for this url. if self.specs: self.__reference_cache[self._url_key] = self.specs else: self.parsed_url = self._url_key = None self.__resolve_types = options.get('resolve_types', RESOLVE_ALL) self.__encoding = options.get('encoding', None)
def _dereferencing_iterator(self, base_url, partial, path, recursions): """ Iterate over a partial spec, dereferencing all references within. Yields the resolved path and value of all items that need substituting. :param mixed base_url: URL that the partial specs is located at. :param dict partial: The partial specs to work on. :param tuple path: The parent path of the partial specs. :param tuple recursions: A recursion stack for resolving references. """ from .iterators import reference_iterator for _, refstring, item_path in reference_iterator(partial): # Split the reference string into parsed URL and object path ref_url, obj_path = _url.split_url_reference(base_url, refstring) translate = (self.__resolve_method == TRANSLATE_EXTERNAL and self.parsed_url.path != ref_url.path) if self._skip_reference(base_url, ref_url): continue # The reference path is the url resource and object path ref_path = (_url.urlresource(ref_url), tuple(obj_path)) # Count how often the reference path has been recursed into. from collections import Counter rec_counter = Counter(recursions) next_recursions = recursions + (ref_path, ) if rec_counter[ref_path] >= self.__reclimit: # The referenced value may be produced by the handler, or the handler # may raise, etc. ref_value = self.__reclimit_handler(self.__reclimit, ref_url, next_recursions) else: # The referenced value is to be used, but let's copy it to avoid # building recursive structures. ref_value = self._dereference(ref_url, obj_path, next_recursions) # Full item path full_path = path + item_path # First yield parent if translate: url = self._collect_soft_refs(ref_url, obj_path, ref_value) yield full_path, {"$ref": "#/components/schemas/" + url} else: yield full_path, ref_value
def test_urlresource(): parsed = url.absurl('http://foo.bar/asdf?some=query#myfrag') res = url.urlresource(parsed) assert res == 'http://foo.bar/asdf'