Use lru_cache

This commit is contained in:
Daniel Nephin 2015-03-04 15:57:31 -05:00
parent ca59f3fa87
commit ee1a256fc8
7 changed files with 45 additions and 33 deletions

View File

@ -19,8 +19,6 @@ from jsonschema.validators import (
Draft3Validator, Draft4Validator, RefResolver, validate
)
__version__ = "2.5.0-dev"
from jsonschema.version import __version__
# flake8: noqa

View File

@ -38,22 +38,6 @@ class URIDict(MutableMapping):
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):
"""
An as-of-yet unset attribute or unprovided default parameter.

View File

@ -13,6 +13,7 @@ PY3 = sys.version_info[0] >= 3
if PY3:
zip = zip
from functools import lru_cache
from io import StringIO
from urllib.parse import (
unquote, urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit
@ -23,6 +24,7 @@ if PY3:
iteritems = operator.methodcaller("items")
else:
from itertools import izip as zip # noqa
from repoze.lru import lru_cache
from StringIO import StringIO
from urlparse import (
urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit # noqa

View File

@ -1,5 +1,4 @@
from collections import deque
from contextlib import contextmanager
import json
from jsonschema import FormatChecker, ValidationError
@ -870,6 +869,13 @@ class TestRefResolver(unittest.TestCase):
pass
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 key(error):

View File

@ -12,7 +12,7 @@ except ImportError:
from jsonschema import _utils, _validators
from jsonschema.compat import (
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 RefResolutionError, SchemaError, UnknownType
@ -233,18 +233,22 @@ class RefResolver(object):
first resolution
:argument dict handlers: a mapping from URI schemes to functions that
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__(
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
self.referrer = referrer
self.cache_remote = cache_remote
self.handlers = dict(handlers)
self.scopes_stack = [base_uri]
self._scopes_stack = [base_uri]
self.store = _utils.URIDict(
(id, validator.META_SCHEMA)
for id, validator in iteritems(meta_schemas)
@ -252,8 +256,8 @@ class RefResolver(object):
self.store.update(store)
self.store[base_uri] = referrer
self.urljoin_cache = _utils.Cache(urljoin)
self.resolve_cache = _utils.Cache(self.resolve_from_url)
self._urljoin_cache = cache_func(cache_maxsize)(urljoin)
self._resolve_cache = cache_func(cache_maxsize)(self.resolve_from_url)
@classmethod
def from_schema(cls, schema, *args, **kwargs):
@ -268,15 +272,21 @@ class RefResolver(object):
return cls(schema.get(u"id", u""), schema, *args, **kwargs)
def push_scope(self, scope):
self.scopes_stack.append(
self.urljoin_cache(self.resolution_scope, scope))
self._scopes_stack.append(
self._urljoin_cache(self.resolution_scope, scope))
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
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
@ -308,8 +318,8 @@ class RefResolver(object):
:argument str ref: reference to resolve
"""
url = self.urljoin_cache(self.resolution_scope, ref)
return url, self.resolve_cache(url)
url = self._urljoin_cache(self.resolution_scope, ref)
return url, self._resolve_cache(url)
def resolve_from_url(self, url):
url, fragment = urldefrag(url)

1
jsonschema/version.py Normal file
View File

@ -0,0 +1 @@
__version__ = "2.5.0-dev"

View File

@ -1,7 +1,12 @@
import os.path
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:
long_description = readme.read()
@ -21,6 +26,11 @@ classifiers = [
"Programming Language :: Python :: Implementation :: PyPy",
]
install_requires = []
if sys.version_info < (3, 2):
install_requires.append('repoze.lru >= 0.6')
setup(
name="jsonschema",
version=__version__,
@ -34,4 +44,5 @@ setup(
long_description=long_description,
url="http://github.com/Julian/jsonschema",
entry_points={"console_scripts": ["jsonschema = jsonschema.cli:main"]},
install_requires=install_requires,
)