server/image-hash: improve exception handling

This commit is contained in:
rr- 2017-02-02 18:21:21 +01:00
parent f42fbbdc56
commit aa1faa3ccb
5 changed files with 74 additions and 30 deletions

View file

@ -1,5 +1,5 @@
import datetime
from szurubooru import search, db
from szurubooru import search, db, errors
from szurubooru.rest import routes
from szurubooru.func import (
auth, tags, posts, snapshots, favorites, scores, util, versions)
@ -211,6 +211,12 @@ def get_posts_around(ctx, params):
def get_posts_by_image(ctx, _params=None):
auth.verify_privilege(ctx.user, 'posts:reverse_search')
content = ctx.get_file('content', required=True)
try:
lookalikes = posts.search_by_image(content)
except (errors.ThirdPartyError, errors.ProcessingError):
lookalikes = []
return {
'exactPost':
_serialize_post(ctx, posts.search_by_image_exact(content)),
@ -220,6 +226,6 @@ def get_posts_by_image(ctx, _params=None):
'distance': lookalike.distance,
'post': _serialize_post(ctx, lookalike.post),
}
for lookalike in posts.search_by_image(content)
for lookalike in lookalikes
],
}

View file

@ -46,3 +46,7 @@ class MissingRequiredParameterError(ValidationError):
class InvalidParameterError(ValidationError):
pass
class ThirdPartyError(BaseError):
pass

View file

@ -44,6 +44,13 @@ def _on_processing_error(ex):
raise _map_error(ex, rest.errors.HttpBadRequest, 'Processing error')
def _on_third_party_error(ex):
raise _map_error(
ex,
rest.errors.HttpInternalServerError,
'Server configuration error')
def _on_stale_data_error(_ex):
raise rest.errors.HttpConflict(
name='IntegrityError',
@ -110,6 +117,7 @@ def create_app():
rest.errors.handle(errors.IntegrityError, _on_integrity_error)
rest.errors.handle(errors.NotFoundError, _on_not_found_error)
rest.errors.handle(errors.ProcessingError, _on_processing_error)
rest.errors.handle(errors.ThirdPartyError, _on_third_party_error)
rest.errors.handle(sqlalchemy.orm.exc.StaleDataError, _on_stale_data_error)
return rest.application

View file

@ -1,10 +1,13 @@
import logging
import elasticsearch
import elasticsearch_dsl
import xml.etree
from image_match.elasticsearch_driver import SignatureES
from szurubooru import config
from szurubooru import config, errors
# pylint: disable=invalid-name
logger = logging.getLogger(__name__)
es = elasticsearch.Elasticsearch([{
'host': config.config['elasticsearch']['host'],
'port': config.config['elasticsearch']['port'],
@ -12,6 +15,28 @@ es = elasticsearch.Elasticsearch([{
session = SignatureES(es, index='szurubooru')
def _safe_blanket(default_param_factory):
def wrapper_outer(target_function):
def wrapper_inner(*args, **kwargs):
try:
return target_function(*args, **kwargs)
except elasticsearch.exceptions.NotFoundError:
# index not yet created, will be created dynamically by
# add_image()
return default_param_factory()
except elasticsearch.exceptions.ElasticsearchException as ex:
logger.warning('Problem with elastic search: %s' % ex)
raise errors.ThirdPartyError(
'Error connecting to elastic search.')
except xml.etree.ElementTree.ParseError as ex:
# image-match issue #60
raise errors.ProcessingError('Not an image.')
except Exception as ex:
raise errors.ThirdPartyError('Unknown error (%s).' % ex)
return wrapper_inner
return wrapper_outer
class Lookalike:
def __init__(self, score, distance, path):
self.score = score
@ -19,39 +44,37 @@ class Lookalike:
self.path = path
@_safe_blanket(lambda: None)
def add_image(path, image_content):
if not path or not image_content:
return
session.add_image(path=path, img=image_content, bytestream=True)
@_safe_blanket(lambda: None)
def delete_image(path):
if not path:
return
try:
es.delete_by_query(
index=session.index,
doc_type=session.doc_type,
body={'query': {'term': {'path': path}}})
except elasticsearch.exceptions.NotFoundError:
pass
es.delete_by_query(
index=session.index,
doc_type=session.doc_type,
body={'query': {'term': {'path': path}}})
@_safe_blanket(lambda: [])
def search_by_image(image_content):
try:
for result in session.search_image(
path=image_content, # sic
bytestream=True):
yield Lookalike(
score=result['score'],
distance=result['dist'],
path=result['path'])
except elasticsearch.exceptions.ElasticsearchException:
raise
except Exception:
yield from []
ret = []
for result in session.search_image(
path=image_content, # sic
bytestream=True):
ret.append(Lookalike(
score=result['score'],
distance=result['dist'],
path=result['path']))
return ret
@_safe_blanket(lambda: None)
def purge():
es.delete_by_query(
index=session.index,
@ -59,12 +82,10 @@ def purge():
body={'query': {'match_all': {}}})
@_safe_blanket(lambda: set())
def get_all_paths():
try:
search = (
elasticsearch_dsl.Search(
using=es, index=session.index, doc_type=session.doc_type)
.source(['path']))
return set(h.path for h in search.scan())
except elasticsearch.exceptions.NotFoundError:
return set()
search = (
elasticsearch_dsl.Search(
using=es, index=session.index, doc_type=session.doc_type)
.source(['path']))
return set(h.path for h in search.scan())

View file

@ -47,5 +47,10 @@ class HttpMethodNotAllowed(BaseHttpError):
reason = 'Method Not Allowed'
class HttpInternalServerError(BaseHttpError):
code = 500
reason = 'Internal Server Error'
def handle(exception_type, handler):
error_handlers[exception_type] = handler