Make backend argument type errors more specific
This commit is contained in:
parent
d6f558a92b
commit
7645c85c9d
|
@ -7,7 +7,7 @@ from typing import Optional
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
from amanuensis.db import *
|
from amanuensis.db import *
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
|
@ -21,11 +21,11 @@ def create(
|
||||||
"""
|
"""
|
||||||
# Verify argument types are correct
|
# Verify argument types are correct
|
||||||
if not isinstance(lexicon_id, int):
|
if not isinstance(lexicon_id, int):
|
||||||
raise ArgumentError("lexicon_id")
|
raise BackendArgumentTypeError(int, lexicon_id=lexicon_id)
|
||||||
if not isinstance(user_id, int):
|
if not isinstance(user_id, int):
|
||||||
raise ArgumentError("user_id")
|
raise BackendArgumentTypeError(int, user_id=user_id)
|
||||||
if character_id is not None and not isinstance(character_id, int):
|
if character_id is not None and not isinstance(character_id, int):
|
||||||
raise ArgumentError("character_id")
|
raise BackendArgumentTypeError(int, character_id=character_id)
|
||||||
|
|
||||||
# Check that the user is a member of this lexicon
|
# Check that the user is a member of this lexicon
|
||||||
mem: Membership = db(
|
mem: Membership = db(
|
||||||
|
|
|
@ -7,7 +7,7 @@ from typing import Optional
|
||||||
from sqlalchemy import select, func
|
from sqlalchemy import select, func
|
||||||
|
|
||||||
from amanuensis.db import *
|
from amanuensis.db import *
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
|
@ -22,13 +22,13 @@ def create(
|
||||||
"""
|
"""
|
||||||
# Verify argument types are correct
|
# Verify argument types are correct
|
||||||
if not isinstance(lexicon_id, int):
|
if not isinstance(lexicon_id, int):
|
||||||
raise ArgumentError("lexicon_id")
|
raise BackendArgumentTypeError(int, lexicon_id=lexicon_id)
|
||||||
if not isinstance(user_id, int):
|
if not isinstance(user_id, int):
|
||||||
raise ArgumentError("user_id")
|
raise BackendArgumentTypeError(int, user_id=user_id)
|
||||||
if not isinstance(name, str):
|
if not isinstance(name, str):
|
||||||
raise ArgumentError("name")
|
raise BackendArgumentTypeError(str, name=name)
|
||||||
if signature is not None and not isinstance(signature, str):
|
if signature is not None and not isinstance(signature, str):
|
||||||
raise ArgumentError("signature")
|
raise BackendArgumentTypeError(str, signature=signature)
|
||||||
|
|
||||||
# Verify character name is valid
|
# Verify character name is valid
|
||||||
if not name.strip():
|
if not name.strip():
|
||||||
|
|
|
@ -6,7 +6,7 @@ import re
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from amanuensis.db import DbContext, ArticleIndex, IndexType
|
from amanuensis.db import DbContext, ArticleIndex, IndexType
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
|
@ -23,17 +23,17 @@ def create(
|
||||||
"""
|
"""
|
||||||
# Verify argument types are correct
|
# Verify argument types are correct
|
||||||
if not isinstance(lexicon_id, int):
|
if not isinstance(lexicon_id, int):
|
||||||
raise ArgumentError("lexicon_id")
|
raise BackendArgumentTypeError(int, lexicon_id=lexicon_id)
|
||||||
if not isinstance(index_type, IndexType):
|
if not isinstance(index_type, IndexType):
|
||||||
raise ArgumentError("index_type")
|
raise BackendArgumentTypeError(IndexType, index_type=index_type)
|
||||||
if not isinstance(pattern, str):
|
if not isinstance(pattern, str):
|
||||||
raise ArgumentError("pattern")
|
raise BackendArgumentTypeError(str, pattern=pattern)
|
||||||
if not isinstance(logical_order, int):
|
if not isinstance(logical_order, int):
|
||||||
raise ArgumentError("logical_order")
|
raise BackendArgumentTypeError(int, logical_order=logical_order)
|
||||||
if not isinstance(display_order, int):
|
if not isinstance(display_order, int):
|
||||||
raise ArgumentError("display_order")
|
raise BackendArgumentTypeError(int, display_order=display_order)
|
||||||
if capacity is not None and not isinstance(capacity, int):
|
if capacity is not None and not isinstance(capacity, int):
|
||||||
raise ArgumentError("capacity")
|
raise BackendArgumentTypeError(int, capacity=capacity)
|
||||||
|
|
||||||
# Verify the pattern is valid for the index type:
|
# Verify the pattern is valid for the index type:
|
||||||
if index_type == IndexType.CHAR:
|
if index_type == IndexType.CHAR:
|
||||||
|
|
|
@ -8,7 +8,7 @@ from typing import Sequence, Optional
|
||||||
from sqlalchemy import select, func
|
from sqlalchemy import select, func
|
||||||
|
|
||||||
from amanuensis.db import DbContext, Lexicon, Membership
|
from amanuensis.db import DbContext, Lexicon, Membership
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
RE_ALPHANUM_DASH_UNDER = re.compile(r"^[A-Za-z0-9-_]*$")
|
RE_ALPHANUM_DASH_UNDER = re.compile(r"^[A-Za-z0-9-_]*$")
|
||||||
|
@ -25,7 +25,7 @@ def create(
|
||||||
"""
|
"""
|
||||||
# Verify name
|
# Verify name
|
||||||
if not isinstance(name, str):
|
if not isinstance(name, str):
|
||||||
raise ArgumentError("Lexicon name must be a string")
|
raise BackendArgumentTypeError(str, name=name)
|
||||||
if not name.strip():
|
if not name.strip():
|
||||||
raise ArgumentError("Lexicon name must not be blank")
|
raise ArgumentError("Lexicon name must not be blank")
|
||||||
if not RE_ALPHANUM_DASH_UNDER.match(name):
|
if not RE_ALPHANUM_DASH_UNDER.match(name):
|
||||||
|
@ -34,12 +34,12 @@ def create(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify title
|
# Verify title
|
||||||
if title is not None and not isinstance(name, str):
|
if title is not None and not isinstance(title, str):
|
||||||
raise ArgumentError("Lexicon name must be a string")
|
raise BackendArgumentTypeError(str, title=title)
|
||||||
|
|
||||||
# Verify prompt
|
# Verify prompt
|
||||||
if not isinstance(prompt, str):
|
if not isinstance(prompt, str):
|
||||||
raise ArgumentError("Lexicon prompt must be a string")
|
raise BackendArgumentTypeError(str, prompt=prompt)
|
||||||
|
|
||||||
# Query the db to make sure the lexicon name isn't taken
|
# Query the db to make sure the lexicon name isn't taken
|
||||||
if db(select(func.count(Lexicon.id)).where(Lexicon.name == name)).scalar() > 0:
|
if db(select(func.count(Lexicon.id)).where(Lexicon.name == name)).scalar() > 0:
|
||||||
|
|
|
@ -6,7 +6,7 @@ from sqlalchemy import select, func
|
||||||
|
|
||||||
from amanuensis.db import DbContext, Membership
|
from amanuensis.db import DbContext, Membership
|
||||||
from amanuensis.db.models import Lexicon
|
from amanuensis.db.models import Lexicon
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
|
@ -20,11 +20,11 @@ def create(
|
||||||
"""
|
"""
|
||||||
# Verify argument types are correct
|
# Verify argument types are correct
|
||||||
if not isinstance(user_id, int):
|
if not isinstance(user_id, int):
|
||||||
raise ArgumentError("user_id")
|
raise BackendArgumentTypeError(int, user_id=user_id)
|
||||||
if not isinstance(lexicon_id, int):
|
if not isinstance(lexicon_id, int):
|
||||||
raise ArgumentError("lexicon_id")
|
raise BackendArgumentTypeError(int, lexicon_id=lexicon_id)
|
||||||
if not isinstance(is_editor, bool):
|
if not isinstance(is_editor, bool):
|
||||||
raise ArgumentError("is_editor")
|
raise BackendArgumentTypeError(bool, is_editor=is_editor)
|
||||||
|
|
||||||
# Verify user has not already joined lexicon
|
# Verify user has not already joined lexicon
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -8,7 +8,7 @@ from sqlalchemy import select
|
||||||
|
|
||||||
from amanuensis.db import DbContext, Post
|
from amanuensis.db import DbContext, Post
|
||||||
from amanuensis.db.models import Lexicon
|
from amanuensis.db.models import Lexicon
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
|
@ -23,15 +23,15 @@ def create(
|
||||||
|
|
||||||
# Verify lexicon id
|
# Verify lexicon id
|
||||||
if not isinstance(lexicon_id, int):
|
if not isinstance(lexicon_id, int):
|
||||||
raise ArgumentError("Lexicon id must be an integer.")
|
raise BackendArgumentTypeError(int, lexicon_id=lexicon_id)
|
||||||
|
|
||||||
# Verify user_id
|
# Verify user_id
|
||||||
if not (isinstance(user_id, int) or user_id is None):
|
if user_id is not None and not isinstance(user_id, int):
|
||||||
raise ArgumentError("User id must be an integer.")
|
raise BackendArgumentTypeError(int, user_id=user_id)
|
||||||
|
|
||||||
# Verify body
|
# Verify body
|
||||||
if not isinstance(body, str):
|
if not isinstance(body, str):
|
||||||
raise ArgumentError("Post body must be a string.")
|
raise BackendArgumentTypeError(str, body=body)
|
||||||
if not body.strip():
|
if not body.strip():
|
||||||
raise ArgumentError("Post body cannot be empty.")
|
raise ArgumentError("Post body cannot be empty.")
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from sqlalchemy import select, func, update
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
|
||||||
from amanuensis.db import DbContext, User
|
from amanuensis.db import DbContext, User
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
RE_NO_LETTERS = re.compile(r"^[0-9-_]*$")
|
RE_NO_LETTERS = re.compile(r"^[0-9-_]*$")
|
||||||
|
@ -30,7 +30,7 @@ def create(
|
||||||
"""
|
"""
|
||||||
# Verify username
|
# Verify username
|
||||||
if not isinstance(username, str):
|
if not isinstance(username, str):
|
||||||
raise ArgumentError("Username must be a string")
|
raise BackendArgumentTypeError(str, username=username)
|
||||||
if len(username) < 3 or len(username) > 32:
|
if len(username) < 3 or len(username) > 32:
|
||||||
raise ArgumentError("Username must be between 3 and 32 characters")
|
raise ArgumentError("Username must be between 3 and 32 characters")
|
||||||
if RE_NO_LETTERS.match(username):
|
if RE_NO_LETTERS.match(username):
|
||||||
|
@ -42,18 +42,18 @@ def create(
|
||||||
|
|
||||||
# Verify password
|
# Verify password
|
||||||
if not isinstance(password, str):
|
if not isinstance(password, str):
|
||||||
raise ArgumentError("Password must be a string")
|
raise BackendArgumentTypeError(str, password=password)
|
||||||
|
|
||||||
# Verify display name
|
# Verify display name
|
||||||
if display_name is not None and not isinstance(display_name, str):
|
if display_name is not None and not isinstance(display_name, str):
|
||||||
raise ArgumentError("Display name must be a string")
|
raise BackendArgumentTypeError(str, display_name=display_name)
|
||||||
# If display name is not provided, use the username
|
# If display name is not provided, use the username
|
||||||
if not display_name or not display_name.strip():
|
if not display_name or not display_name.strip():
|
||||||
display_name = username
|
display_name = username
|
||||||
|
|
||||||
# Verify email
|
# Verify email
|
||||||
if not isinstance(email, str):
|
if not isinstance(email, str):
|
||||||
raise ArgumentError("Email must be a string")
|
raise BackendArgumentTypeError(str, email=email)
|
||||||
|
|
||||||
# Query the db to make sure the username isn't taken
|
# Query the db to make sure the username isn't taken
|
||||||
if db(select(func.count(User.id)).where(User.username == username)).scalar() > 0:
|
if db(select(func.count(User.id)).where(User.username == username)).scalar() > 0:
|
||||||
|
|
|
@ -4,8 +4,21 @@ Submodule of custom exception types
|
||||||
|
|
||||||
|
|
||||||
class AmanuensisError(Exception):
|
class AmanuensisError(Exception):
|
||||||
"""Base class for exceptions in amanuensis"""
|
"""Base class for exceptions in Amanuensis"""
|
||||||
|
|
||||||
|
|
||||||
class ArgumentError(AmanuensisError):
|
class ArgumentError(AmanuensisError):
|
||||||
"""An internal call was made with invalid arguments"""
|
"""An internal call was made with invalid arguments."""
|
||||||
|
|
||||||
|
|
||||||
|
class BackendArgumentTypeError(ArgumentError):
|
||||||
|
"""
|
||||||
|
A call to a backend function was made with a value of an invalid type for the parameter.
|
||||||
|
Specify the invalid parameter and value as a kwarg.
|
||||||
|
"""
|
||||||
|
def __init__(self, obj_type, **kwarg):
|
||||||
|
if not kwarg:
|
||||||
|
raise ValueError("Missing kwarg")
|
||||||
|
param, value = next(iter(kwarg.items()))
|
||||||
|
msg = f"Expected {param} of type {obj_type}, got {type(value)}"
|
||||||
|
super().__init__(msg)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import pytest
|
||||||
|
|
||||||
from amanuensis.backend import charq
|
from amanuensis.backend import charq
|
||||||
from amanuensis.db import *
|
from amanuensis.db import *
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
def test_create_character(db: DbContext, lexicon_with_editor, make):
|
def test_create_character(db: DbContext, lexicon_with_editor, make):
|
||||||
|
@ -20,13 +20,13 @@ def test_create_character(db: DbContext, lexicon_with_editor, make):
|
||||||
kwargs: dict
|
kwargs: dict
|
||||||
|
|
||||||
# Bad argument types
|
# Bad argument types
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(BackendArgumentTypeError):
|
||||||
kwargs = {**defaults, "name": b"bytestring"}
|
kwargs = {**defaults, "name": b"bytestring"}
|
||||||
charq.create(**kwargs)
|
charq.create(**kwargs)
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(BackendArgumentTypeError):
|
||||||
kwargs = {**defaults, "name": None}
|
kwargs = {**defaults, "name": None}
|
||||||
charq.create(**kwargs)
|
charq.create(**kwargs)
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(BackendArgumentTypeError):
|
||||||
kwargs = {**defaults, "signature": b"bytestring"}
|
kwargs = {**defaults, "signature": b"bytestring"}
|
||||||
charq.create(**kwargs)
|
charq.create(**kwargs)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import pytest
|
||||||
|
|
||||||
from amanuensis.backend import lexiq
|
from amanuensis.backend import lexiq
|
||||||
from amanuensis.db import DbContext, Lexicon, User
|
from amanuensis.db import DbContext, Lexicon, User
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
from tests.conftest import ObjectFactory
|
from tests.conftest import ObjectFactory
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ def test_create_lexicon(db: DbContext):
|
||||||
kwargs: dict
|
kwargs: dict
|
||||||
|
|
||||||
# Test name constraints
|
# Test name constraints
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(BackendArgumentTypeError):
|
||||||
kwargs = {**defaults, "name": None}
|
kwargs = {**defaults, "name": None}
|
||||||
lexiq.create(**kwargs)
|
lexiq.create(**kwargs)
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(ArgumentError):
|
||||||
|
|
|
@ -3,7 +3,7 @@ import pytest
|
||||||
from amanuensis.backend import postq
|
from amanuensis.backend import postq
|
||||||
from amanuensis.db import DbContext
|
from amanuensis.db import DbContext
|
||||||
|
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
def test_create_post(db: DbContext, lexicon_with_editor):
|
def test_create_post(db: DbContext, lexicon_with_editor):
|
||||||
|
@ -20,19 +20,16 @@ def test_create_post(db: DbContext, lexicon_with_editor):
|
||||||
kwargs: dict
|
kwargs: dict
|
||||||
|
|
||||||
# ids are integers
|
# ids are integers
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(BackendArgumentTypeError):
|
||||||
kwargs = {**defaults, "user_id": "zero"}
|
kwargs = {**defaults, "user_id": "zero"}
|
||||||
postq.create(**kwargs)
|
postq.create(**kwargs)
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(BackendArgumentTypeError):
|
||||||
kwargs = {**defaults, "lexicon_id": "zero"}
|
kwargs = {**defaults, "lexicon_id": "zero"}
|
||||||
postq.create(**kwargs)
|
postq.create(**kwargs)
|
||||||
|
|
||||||
# empty arguments don't work
|
# empty arguments don't work
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(BackendArgumentTypeError):
|
||||||
kwargs = {**defaults, "lexicon_id": ""}
|
kwargs = {**defaults, "lexicon_id": None}
|
||||||
postq.create(**kwargs)
|
|
||||||
with pytest.raises(ArgumentError):
|
|
||||||
kwargs = {**defaults, "user_id": ""}
|
|
||||||
postq.create(**kwargs)
|
postq.create(**kwargs)
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(ArgumentError):
|
||||||
kwargs = {**defaults, "body": ""}
|
kwargs = {**defaults, "body": ""}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
||||||
|
|
||||||
from amanuensis.backend import userq
|
from amanuensis.backend import userq
|
||||||
from amanuensis.db import DbContext, User
|
from amanuensis.db import DbContext, User
|
||||||
from amanuensis.errors import ArgumentError
|
from amanuensis.errors import ArgumentError, BackendArgumentTypeError
|
||||||
|
|
||||||
|
|
||||||
def test_create_user(db: DbContext):
|
def test_create_user(db: DbContext):
|
||||||
|
@ -33,7 +33,7 @@ def test_create_user(db: DbContext):
|
||||||
userq.create(**kwargs)
|
userq.create(**kwargs)
|
||||||
|
|
||||||
# No password
|
# No password
|
||||||
with pytest.raises(ArgumentError):
|
with pytest.raises(BackendArgumentTypeError):
|
||||||
kwargs = {**defaults, "password": None}
|
kwargs = {**defaults, "password": None}
|
||||||
userq.create(**kwargs)
|
userq.create(**kwargs)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue