Use lru_cache
This commit is contained in:
parent
ca59f3fa87
commit
ee1a256fc8
|
@ -19,8 +19,6 @@ from jsonschema.validators import (
|
||||||
Draft3Validator, Draft4Validator, RefResolver, validate
|
Draft3Validator, Draft4Validator, RefResolver, validate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from jsonschema.version import __version__
|
||||||
__version__ = "2.5.0-dev"
|
|
||||||
|
|
||||||
|
|
||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
|
|
|
@ -38,22 +38,6 @@ class URIDict(MutableMapping):
|
||||||
return repr(self.store)
|
return repr(self.store)
|
||||||
|
|
||||||
|
|
||||||
class Cache(object):
|
|
||||||
"""Cache the result of a function, using the arguments to the function as
|
|
||||||
the key.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, func):
|
|
||||||
self.func = func
|
|
||||||
self._cache = {}
|
|
||||||
|
|
||||||
def __call__(self, *args):
|
|
||||||
if args in self._cache:
|
|
||||||
return self._cache[args]
|
|
||||||
self._cache[args] = value = self.func(*args)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class Unset(object):
|
class Unset(object):
|
||||||
"""
|
"""
|
||||||
An as-of-yet unset attribute or unprovided default parameter.
|
An as-of-yet unset attribute or unprovided default parameter.
|
||||||
|
|
|
@ -13,6 +13,7 @@ PY3 = sys.version_info[0] >= 3
|
||||||
|
|
||||||
if PY3:
|
if PY3:
|
||||||
zip = zip
|
zip = zip
|
||||||
|
from functools import lru_cache
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from urllib.parse import (
|
from urllib.parse import (
|
||||||
unquote, urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit
|
unquote, urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit
|
||||||
|
@ -23,6 +24,7 @@ if PY3:
|
||||||
iteritems = operator.methodcaller("items")
|
iteritems = operator.methodcaller("items")
|
||||||
else:
|
else:
|
||||||
from itertools import izip as zip # noqa
|
from itertools import izip as zip # noqa
|
||||||
|
from repoze.lru import lru_cache
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from urlparse import (
|
from urlparse import (
|
||||||
urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit # noqa
|
urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit # noqa
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from contextlib import contextmanager
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from jsonschema import FormatChecker, ValidationError
|
from jsonschema import FormatChecker, ValidationError
|
||||||
|
@ -870,6 +869,13 @@ class TestRefResolver(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
self.assertEqual(str(err.exception), "Oh no! What's this?")
|
self.assertEqual(str(err.exception), "Oh no! What's this?")
|
||||||
|
|
||||||
|
def test_helpful_error_message_on_failed_pop_scope(self):
|
||||||
|
resolver = RefResolver("", {})
|
||||||
|
resolver.pop_scope()
|
||||||
|
with self.assertRaises(RefResolutionError) as exc:
|
||||||
|
resolver.pop_scope()
|
||||||
|
self.assertIn("Failed to pop the scope", str(exc.exception))
|
||||||
|
|
||||||
|
|
||||||
def sorted_errors(errors):
|
def sorted_errors(errors):
|
||||||
def key(error):
|
def key(error):
|
||||||
|
|
|
@ -12,7 +12,7 @@ except ImportError:
|
||||||
from jsonschema import _utils, _validators
|
from jsonschema import _utils, _validators
|
||||||
from jsonschema.compat import (
|
from jsonschema.compat import (
|
||||||
Sequence, urljoin, urlsplit, urldefrag, unquote, urlopen,
|
Sequence, urljoin, urlsplit, urldefrag, unquote, urlopen,
|
||||||
str_types, int_types, iteritems,
|
str_types, int_types, iteritems, lru_cache,
|
||||||
)
|
)
|
||||||
from jsonschema.exceptions import ErrorTree # Backwards compatibility # noqa
|
from jsonschema.exceptions import ErrorTree # Backwards compatibility # noqa
|
||||||
from jsonschema.exceptions import RefResolutionError, SchemaError, UnknownType
|
from jsonschema.exceptions import RefResolutionError, SchemaError, UnknownType
|
||||||
|
@ -233,18 +233,22 @@ class RefResolver(object):
|
||||||
first resolution
|
first resolution
|
||||||
:argument dict handlers: a mapping from URI schemes to functions that
|
:argument dict handlers: a mapping from URI schemes to functions that
|
||||||
should be used to retrieve them
|
should be used to retrieve them
|
||||||
|
:arguments callable cache_func: a function decorator used to cache
|
||||||
|
expensive calls. Should support the `functools.lru_cache` interface.
|
||||||
|
:argument int cache_maxsize: number of items to store in the cache. Set
|
||||||
|
this to 0 to disable caching. Defaults to 1000.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, base_uri, referrer, store=(), cache_remote=True, handlers=(),
|
self, base_uri, referrer, store=(), cache_remote=True, handlers=(),
|
||||||
|
cache_func=lru_cache, cache_maxsize=1000,
|
||||||
):
|
):
|
||||||
# This attribute is not used, it is for backwards compatibility
|
# This attribute is not used, it is for backwards compatibility
|
||||||
self.referrer = referrer
|
self.referrer = referrer
|
||||||
self.cache_remote = cache_remote
|
self.cache_remote = cache_remote
|
||||||
self.handlers = dict(handlers)
|
self.handlers = dict(handlers)
|
||||||
|
|
||||||
self.scopes_stack = [base_uri]
|
self._scopes_stack = [base_uri]
|
||||||
self.store = _utils.URIDict(
|
self.store = _utils.URIDict(
|
||||||
(id, validator.META_SCHEMA)
|
(id, validator.META_SCHEMA)
|
||||||
for id, validator in iteritems(meta_schemas)
|
for id, validator in iteritems(meta_schemas)
|
||||||
|
@ -252,8 +256,8 @@ class RefResolver(object):
|
||||||
self.store.update(store)
|
self.store.update(store)
|
||||||
self.store[base_uri] = referrer
|
self.store[base_uri] = referrer
|
||||||
|
|
||||||
self.urljoin_cache = _utils.Cache(urljoin)
|
self._urljoin_cache = cache_func(cache_maxsize)(urljoin)
|
||||||
self.resolve_cache = _utils.Cache(self.resolve_from_url)
|
self._resolve_cache = cache_func(cache_maxsize)(self.resolve_from_url)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_schema(cls, schema, *args, **kwargs):
|
def from_schema(cls, schema, *args, **kwargs):
|
||||||
|
@ -268,15 +272,21 @@ class RefResolver(object):
|
||||||
return cls(schema.get(u"id", u""), schema, *args, **kwargs)
|
return cls(schema.get(u"id", u""), schema, *args, **kwargs)
|
||||||
|
|
||||||
def push_scope(self, scope):
|
def push_scope(self, scope):
|
||||||
self.scopes_stack.append(
|
self._scopes_stack.append(
|
||||||
self.urljoin_cache(self.resolution_scope, scope))
|
self._urljoin_cache(self.resolution_scope, scope))
|
||||||
|
|
||||||
def pop_scope(self):
|
def pop_scope(self):
|
||||||
self.scopes_stack.pop()
|
try:
|
||||||
|
self._scopes_stack.pop()
|
||||||
|
except IndexError:
|
||||||
|
raise RefResolutionError(
|
||||||
|
"Failed to pop the scope from an empty stack. "
|
||||||
|
"`pop_scope()` should only be called once for every "
|
||||||
|
"`push_scope()`")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def resolution_scope(self):
|
def resolution_scope(self):
|
||||||
return self.scopes_stack[-1]
|
return self._scopes_stack[-1]
|
||||||
|
|
||||||
|
|
||||||
# Deprecated, this function is no longer used, but is preserved for
|
# Deprecated, this function is no longer used, but is preserved for
|
||||||
|
@ -308,8 +318,8 @@ class RefResolver(object):
|
||||||
:argument str ref: reference to resolve
|
:argument str ref: reference to resolve
|
||||||
|
|
||||||
"""
|
"""
|
||||||
url = self.urljoin_cache(self.resolution_scope, ref)
|
url = self._urljoin_cache(self.resolution_scope, ref)
|
||||||
return url, self.resolve_cache(url)
|
return url, self._resolve_cache(url)
|
||||||
|
|
||||||
def resolve_from_url(self, url):
|
def resolve_from_url(self, url):
|
||||||
url, fragment = urldefrag(url)
|
url, fragment = urldefrag(url)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
__version__ = "2.5.0-dev"
|
15
setup.py
15
setup.py
|
@ -1,7 +1,12 @@
|
||||||
|
import os.path
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
import sys
|
||||||
|
|
||||||
from jsonschema import __version__
|
# Load __version__ info globals without importing anything
|
||||||
|
with open(
|
||||||
|
os.path.join(os.path.dirname(__file__), 'jsonschema', 'version.py')
|
||||||
|
) as fh:
|
||||||
|
exec(fh.read())
|
||||||
|
|
||||||
with open("README.rst") as readme:
|
with open("README.rst") as readme:
|
||||||
long_description = readme.read()
|
long_description = readme.read()
|
||||||
|
@ -21,6 +26,11 @@ classifiers = [
|
||||||
"Programming Language :: Python :: Implementation :: PyPy",
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
install_requires = []
|
||||||
|
|
||||||
|
if sys.version_info < (3, 2):
|
||||||
|
install_requires.append('repoze.lru >= 0.6')
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="jsonschema",
|
name="jsonschema",
|
||||||
version=__version__,
|
version=__version__,
|
||||||
|
@ -34,4 +44,5 @@ setup(
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
url="http://github.com/Julian/jsonschema",
|
url="http://github.com/Julian/jsonschema",
|
||||||
entry_points={"console_scripts": ["jsonschema = jsonschema.cli:main"]},
|
entry_points={"console_scripts": ["jsonschema = jsonschema.cli:main"]},
|
||||||
|
install_requires=install_requires,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue