black style pass

This commit is contained in:
Tim Van Baak 2021-06-02 20:05:33 -07:00
parent 667173329c
commit de09030b1c
15 changed files with 91 additions and 85 deletions

View File

@ -8,11 +8,7 @@ from amanuensis.db import *
from amanuensis.errors import ArgumentError from amanuensis.errors import ArgumentError
def create( def create(db: DbContext, lexicon_id: int, user_id: int, character_id: int) -> Article:
db: DbContext,
lexicon_id: int,
user_id: int,
character_id: int) -> Article:
""" """
Create a new article in a lexicon. Create a new article in a lexicon.
""" """
@ -37,8 +33,7 @@ def create(
# and the character belongs to the lexicon # and the character belongs to the lexicon
if character_id is not None: if character_id is not None:
character: Character = db( character: Character = db(
select(Character) select(Character).where(Character.id == character_id)
.where(Character.id == character_id)
).scalar_one_or_none() ).scalar_one_or_none()
if not character: if not character:
raise ArgumentError('Character does not exist') raise ArgumentError('Character does not exist')

View File

@ -9,11 +9,8 @@ from amanuensis.errors import ArgumentError
def create( def create(
db: DbContext, db: DbContext, lexicon_id: int, user_id: int, name: str, signature: str
lexicon_id: int, ) -> Character:
user_id: int,
name: str,
signature: str) -> Character:
""" """
Create a new character for a user. Create a new character for a user.
""" """
@ -50,7 +47,10 @@ def create(
.where(Character.lexicon_id == lexicon_id) .where(Character.lexicon_id == lexicon_id)
.where(Character.user_id == user_id) .where(Character.user_id == user_id)
).scalar() ).scalar()
if mem.lexicon.character_limit is not None and num_user_chars >= mem.lexicon.character_limit: if (
mem.lexicon.character_limit is not None
and num_user_chars >= mem.lexicon.character_limit
):
raise ArgumentError('User is at character limit') raise ArgumentError('User is at character limit')
new_character = Character( new_character = Character(

View File

@ -13,11 +13,7 @@ from amanuensis.errors import ArgumentError
RE_ALPHANUM_DASH_UNDER = re.compile(r'^[A-Za-z0-9-_]*$') RE_ALPHANUM_DASH_UNDER = re.compile(r'^[A-Za-z0-9-_]*$')
def create( def create(db: DbContext, name: str, title: str, prompt: str) -> Lexicon:
db: DbContext,
name: str,
title: str,
prompt: str) -> Lexicon:
""" """
Create a new lexicon. Create a new lexicon.
""" """
@ -27,7 +23,9 @@ def create(
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):
raise ArgumentError('Lexicon name may only contain alphanumerics, dash, and underscore') raise ArgumentError(
'Lexicon name may only contain alphanumerics, dash, and underscore'
)
# Verify title # Verify title
if title is not None and not isinstance(name, str): if title is not None and not isinstance(name, str):
@ -38,10 +36,7 @@ def create(
raise ArgumentError('Lexicon prompt must be a string') raise ArgumentError('Lexicon prompt must be a string')
# 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( if db(select(func.count(Lexicon.id)).where(Lexicon.name == name)).scalar() > 0:
select(func.count(Lexicon.id))
.where(Lexicon.name == name)
).scalar() > 0:
raise ArgumentError('Lexicon name is already taken') raise ArgumentError('Lexicon name is already taken')
new_lexicon = Lexicon( new_lexicon = Lexicon(

View File

@ -8,11 +8,7 @@ from amanuensis.db import DbContext, Membership
from amanuensis.errors import ArgumentError from amanuensis.errors import ArgumentError
def create( def create(db: DbContext, user_id: int, lexicon_id: int, is_editor: bool) -> Membership:
db: DbContext,
user_id: int,
lexicon_id: int,
is_editor: bool) -> Membership:
""" """
Create a new user membership in a lexicon. Create a new user membership in a lexicon.
""" """
@ -25,11 +21,14 @@ def create(
raise ArgumentError('is_editor') raise ArgumentError('is_editor')
# Verify user has not already joined lexicon # Verify user has not already joined lexicon
if db( if (
select(func.count(Membership.id)) db(
.where(Membership.user_id == user_id) select(func.count(Membership.id))
.where(Membership.lexicon_id == lexicon_id) .where(Membership.user_id == user_id)
).scalar() > 0: .where(Membership.lexicon_id == lexicon_id)
).scalar()
> 0
):
raise ArgumentError('User is already a member of lexicon') raise ArgumentError('User is already a member of lexicon')
new_membership = Membership( new_membership = Membership(

View File

@ -9,11 +9,8 @@ from sqlalchemy import select, func
from amanuensis.db import DbContext, Post from amanuensis.db import DbContext, Post
from amanuensis.errors import ArgumentError from amanuensis.errors import ArgumentError
def create(
db: DbContext, def create(db: DbContext, lexicon_id: int, user_id: int, body: str) -> Post:
lexicon_id: int,
user_id: int,
body: str) -> Post:
""" """
Create a new post Create a new post
""" """
@ -32,11 +29,7 @@ def create(
if not body.strip(): if not body.strip():
raise ArgumentError('Post body cannot be empty.') raise ArgumentError('Post body cannot be empty.')
new_post = Post( new_post = Post(lexicon_id=lexicon_id, user_id=user_id, body=body)
lexicon_id=lexicon_id,
user_id=user_id,
body=body
)
db.session.add(new_post) db.session.add(new_post)
db.session.commit() db.session.commit()
return new_post return new_post

View File

@ -11,7 +11,7 @@ from amanuensis.db import DbContext, User
from amanuensis.errors import ArgumentError from amanuensis.errors import ArgumentError
RE_NO_LETTERS = re.compile(r'^[0-9-_]*$') RE_NO_LETTERS = re.compile(r'^[0-9-_]*$')
RE_ALPHANUM_DASH_UNDER = re.compile(r'^[A-Za-z0-9-_]*$') RE_ALPHANUM_DASH_UNDER = re.compile(r'^[A-Za-z0-9-_]*$')
@ -21,7 +21,8 @@ def create(
password: str, password: str,
display_name: str, display_name: str,
email: str, email: str,
is_site_admin: bool) -> User: is_site_admin: bool,
) -> User:
""" """
Create a new user. Create a new user.
""" """
@ -33,7 +34,9 @@ def create(
if RE_NO_LETTERS.match(username): if RE_NO_LETTERS.match(username):
raise ArgumentError('Username must contain a letter') raise ArgumentError('Username must contain a letter')
if not RE_ALPHANUM_DASH_UNDER.match(username): if not RE_ALPHANUM_DASH_UNDER.match(username):
raise ArgumentError('Username may only contain alphanumerics, dash, and underscore') raise ArgumentError(
'Username may only contain alphanumerics, dash, and underscore'
)
# Verify password # Verify password
if not isinstance(password, str): if not isinstance(password, str):
@ -51,10 +54,7 @@ def create(
raise ArgumentError('Email must be a string') raise ArgumentError('Email must be a string')
# Query the db to make sure the username isn't taken # Query the db to make sure the username isn't taken
if db( if db(select(func.count(User.id)).where(User.username == username)).scalar() > 0:
select(func.count(User.id))
.where(User.username == username)
).scalar() > 0:
raise ArgumentError('Username is already taken') raise ArgumentError('Username is already taken')
new_user = User( new_user = User(

View File

@ -28,4 +28,4 @@ __all__ = [
'ArticleContentRuleType', 'ArticleContentRuleType',
'ArticleContentRule', 'ArticleContentRule',
'Post', 'Post',
] ]

View File

@ -8,22 +8,25 @@ from sqlalchemy.orm import sessionmaker
# Define naming conventions for generated constraints # Define naming conventions for generated constraints
metadata = MetaData(naming_convention={ metadata = MetaData(
"ix": "ix_%(column_0_label)s", naming_convention={
"uq": "uq_%(table_name)s_%(column_0_name)s", "ix": "ix_%(column_0_label)s",
"ck": "ck_%(table_name)s_%(constraint_name)s", "uq": "uq_%(table_name)s_%(column_0_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", "ck": "ck_%(table_name)s_%(constraint_name)s",
"pk": "pk_%(table_name)s" "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
}) "pk": "pk_%(table_name)s",
}
)
# Base class for ORM models # Base class for ORM models
ModelBase = declarative_base(metadata=metadata) ModelBase = declarative_base(metadata=metadata)
class DbContext(): class DbContext:
def __init__(self, db_uri, debug=False): def __init__(self, db_uri, debug=False):
# Create an engine and enable foreign key constraints in sqlite # Create an engine and enable foreign key constraints in sqlite
self.engine = create_engine(db_uri, echo=debug) self.engine = create_engine(db_uri, echo=debug)
@event.listens_for(self.engine, "connect") @event.listens_for(self.engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record): def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor() cursor = dbapi_connection.cursor()

View File

@ -28,6 +28,7 @@ class Uuid(TypeDecorator):
""" """
A uuid backed by a char(32) field in sqlite. A uuid backed by a char(32) field in sqlite.
""" """
impl = CHAR(32) impl = CHAR(32)
def process_bind_param(self, value, dialect): def process_bind_param(self, value, dialect):
@ -51,6 +52,7 @@ class User(ModelBase):
""" """
Represents a single user of Amanuensis. Represents a single user of Amanuensis.
""" """
__tablename__ = 'user' __tablename__ = 'user'
############# #############
@ -104,6 +106,7 @@ class Lexicon(ModelBase):
""" """
Represents a single game of Lexicon. Represents a single game of Lexicon.
""" """
__tablename__ = 'lexicon' __tablename__ = 'lexicon'
############# #############
@ -131,7 +134,9 @@ class Lexicon(ModelBase):
created = Column(DateTime, nullable=False, server_default=text('CURRENT_TIMESTAMP')) created = Column(DateTime, nullable=False, server_default=text('CURRENT_TIMESTAMP'))
# The timestamp of the last change in game state # The timestamp of the last change in game state
last_updated = Column(DateTime, nullable=False, server_default=text('CURRENT_TIMESTAMP')) last_updated = Column(
DateTime, nullable=False, server_default=text('CURRENT_TIMESTAMP')
)
# The timestamp the first turn was started # The timestamp the first turn was started
# This is NULL until the game starts # This is NULL until the game starts
@ -234,10 +239,9 @@ class Membership(ModelBase):
""" """
Represents a user's participation in a Lexicon game. Represents a user's participation in a Lexicon game.
""" """
__tablename__ = 'membership' __tablename__ = 'membership'
__table_args__ = ( __table_args__ = (UniqueConstraint('user_id', 'lexicon_id'),)
UniqueConstraint('user_id', 'lexicon_id'),
)
################### ###################
# Membership keys # # Membership keys #
@ -295,6 +299,7 @@ class Character(ModelBase):
""" """
Represents a character played by a uaser in a Lexicon game. Represents a character played by a uaser in a Lexicon game.
""" """
__tablename__ = 'character' __tablename__ = 'character'
################## ##################
@ -333,6 +338,7 @@ class ArticleState(enum.Enum):
""" """
The step of the editorial process an article is in. The step of the editorial process an article is in.
""" """
DRAFT = 0 DRAFT = 0
SUBMITTED = 1 SUBMITTED = 1
APPROVED = 2 APPROVED = 2
@ -342,6 +348,7 @@ class Article(ModelBase):
""" """
Represents a single article in a lexicon. Represents a single article in a lexicon.
""" """
__tablename__ = 'article' __tablename__ = 'article'
################ ################
@ -386,7 +393,9 @@ class Article(ModelBase):
#################### ####################
# Timestamp the content of the article was last updated # Timestamp the content of the article was last updated
last_updated = Column(DateTime, nullable=False, server_default=text('CURRENT_TIMESTAMP')) last_updated = Column(
DateTime, nullable=False, server_default=text('CURRENT_TIMESTAMP')
)
# Timestamp the article was last submitted # Timestamp the article was last submitted
# This is NULL until the article is submitted # This is NULL until the article is submitted
@ -420,6 +429,7 @@ class IndexType(enum.Enum):
""" """
The title-matching behavior of an article index. The title-matching behavior of an article index.
""" """
CHAR = 0 CHAR = 0
RANGE = 1 RANGE = 1
PREFIX = 2 PREFIX = 2
@ -430,6 +440,7 @@ class ArticleIndex(ModelBase):
""" """
Represents an index definition. Represents an index definition.
""" """
__tablename__ = 'article_index' __tablename__ = 'article_index'
############## ##############
@ -472,6 +483,7 @@ class ArticleIndexRule(ModelBase):
A character with multiple index rules may write in any index that satisfies A character with multiple index rules may write in any index that satisfies
a rule. A character with no index rules may write in any index. a rule. A character with no index rules may write in any index.
""" """
__tablename__ = 'article_index_rule' __tablename__ = 'article_index_rule'
################### ###################
@ -510,6 +522,7 @@ class ArticleContentRuleType(enum.Enum):
""" """
The possible article content rules. The possible article content rules.
""" """
# Whether characters can cite themselves # Whether characters can cite themselves
ALLOW_SELF_CITE = 0 ALLOW_SELF_CITE = 0
# Whether characters can write new articles instead of phantoms # Whether characters can write new articles instead of phantoms
@ -543,6 +556,7 @@ class ArticleContentRule(ModelBase):
""" """
Represents a restriction on the content of an article for a turn. Represents a restriction on the content of an article for a turn.
""" """
__tablename__ = 'article_content_rule' __tablename__ = 'article_content_rule'
##################### #####################
@ -584,6 +598,7 @@ class Post(ModelBase):
""" """
Represents a post in the game feed. Represents a post in the game feed.
""" """
__tablename__ = 'post' __tablename__ = 'post'
############# #############

View File

@ -2,6 +2,7 @@
Submodule of custom exception types Submodule of custom exception types
""" """
class AmanuensisError(Exception): class AmanuensisError(Exception):
"""Base class for exceptions in amanuensis""" """Base class for exceptions in amanuensis"""

View File

@ -21,6 +21,7 @@ def db():
@pytest.fixture @pytest.fixture
def make_user(db: DbContext): def make_user(db: DbContext):
"""Provides a factory function for creating users, with valid default values.""" """Provides a factory function for creating users, with valid default values."""
def user_factory(state={'nonce': 1}, **kwargs): def user_factory(state={'nonce': 1}, **kwargs):
default_kwargs = { default_kwargs = {
'username': f'test_user_{state["nonce"]}', 'username': f'test_user_{state["nonce"]}',
@ -32,39 +33,45 @@ def make_user(db: DbContext):
state['nonce'] += 1 state['nonce'] += 1
updated_kwargs = {**default_kwargs, **kwargs} updated_kwargs = {**default_kwargs, **kwargs}
return userq.create(db, **updated_kwargs) return userq.create(db, **updated_kwargs)
return user_factory return user_factory
@pytest.fixture @pytest.fixture
def make_lexicon(db: DbContext): def make_lexicon(db: DbContext):
"""Provides a factory function for creating lexicons, with valid default values.""" """Provides a factory function for creating lexicons, with valid default values."""
def lexicon_factory(state={'nonce': 1}, **kwargs): def lexicon_factory(state={'nonce': 1}, **kwargs):
default_kwargs = { default_kwargs = {
'name': f'Test_{state["nonce"]}', 'name': f'Test_{state["nonce"]}',
'title': None, 'title': None,
'prompt': f'Test Lexicon game {state["nonce"]}' 'prompt': f'Test Lexicon game {state["nonce"]}',
} }
state['nonce'] += 1 state['nonce'] += 1
updated_kwargs = {**default_kwargs, **kwargs} updated_kwargs = {**default_kwargs, **kwargs}
return lexiq.create(db, **updated_kwargs) return lexiq.create(db, **updated_kwargs)
return lexicon_factory return lexicon_factory
@pytest.fixture @pytest.fixture
def make_membership(db: DbContext): def make_membership(db: DbContext):
"""Provides a factory function for creating memberships, with valid default values.""" """Provides a factory function for creating memberships, with valid default values."""
def membership_factory(**kwargs): def membership_factory(**kwargs):
default_kwargs = { default_kwargs = {
'is_editor': False, 'is_editor': False,
} }
updated_kwargs = {**default_kwargs, **kwargs} updated_kwargs = {**default_kwargs, **kwargs}
return memq.create(db, **updated_kwargs) return memq.create(db, **updated_kwargs)
return membership_factory return membership_factory
@pytest.fixture @pytest.fixture
def make_character(db: DbContext): def make_character(db: DbContext):
"""Provides a factory function for creating characters, with valid default values.""" """Provides a factory function for creating characters, with valid default values."""
def character_factory(state={'nonce': 1}, **kwargs): def character_factory(state={'nonce': 1}, **kwargs):
default_kwargs = { default_kwargs = {
'name': f'Character {state["nonce"]}', 'name': f'Character {state["nonce"]}',
@ -73,6 +80,7 @@ def make_character(db: DbContext):
state['nonce'] += 1 state['nonce'] += 1
updated_kwargs = {**default_kwargs, **kwargs} updated_kwargs = {**default_kwargs, **kwargs}
return charq.create(db, **updated_kwargs) return charq.create(db, **updated_kwargs)
return character_factory return character_factory
@ -87,11 +95,8 @@ class TestFactory:
@pytest.fixture @pytest.fixture
def make( def make(
db: DbContext, db: DbContext, make_user, make_lexicon, make_membership, make_character
make_user, ) -> TestFactory:
make_lexicon,
make_membership,
make_character) -> TestFactory:
"""Fixture that groups all factory fixtures together.""" """Fixture that groups all factory fixtures together."""
return TestFactory( return TestFactory(
db, db,
@ -109,6 +114,8 @@ def lexicon_with_editor(make):
assert editor assert editor
lexicon = make.lexicon() lexicon = make.lexicon()
assert lexicon assert lexicon
membership = make.membership(user_id=editor.id, lexicon_id=lexicon.id, is_editor=True) membership = make.membership(
user_id=editor.id, lexicon_id=lexicon.id, is_editor=True
)
assert membership assert membership
return (lexicon, editor) return (lexicon, editor)

View File

@ -52,7 +52,9 @@ def test_character_limits(db: DbContext, lexicon_with_editor):
# Creating a second character should fail # Creating a second character should fail
with pytest.raises(ArgumentError): with pytest.raises(ArgumentError):
char2 = charq.create(db, lexicon.id, user.id, 'Test Character 2', signature=None) char2 = charq.create(
db, lexicon.id, user.id, 'Test Character 2', signature=None
)
assert char2 assert char2
# Raising the limit to 2 should allow a second character # Raising the limit to 2 should allow a second character
@ -63,7 +65,9 @@ def test_character_limits(db: DbContext, lexicon_with_editor):
# Creating a third character should fail # Creating a third character should fail
with pytest.raises(ArgumentError): with pytest.raises(ArgumentError):
char3 = charq.create(db, lexicon.id, user.id, 'Test Character 3', signature=None) char3 = charq.create(
db, lexicon.id, user.id, 'Test Character 3', signature=None
)
assert char3 assert char3
# Setting the limit to null should allow a third character # Setting the limit to null should allow a third character

View File

@ -9,11 +9,7 @@ from amanuensis.errors import ArgumentError
def test_create_lexicon(db: DbContext): def test_create_lexicon(db: DbContext):
"""Test new game creation.""" """Test new game creation."""
kwargs = { kwargs = {'name': 'Test', 'title': None, 'prompt': 'A test Lexicon game'}
'name': 'Test',
'title': None,
'prompt': 'A test Lexicon game'
}
# Test name constraints # Test name constraints
with pytest.raises(ArgumentError): with pytest.raises(ArgumentError):
lexiq.create(db, **{**kwargs, 'name': None}) lexiq.create(db, **{**kwargs, 'name': None})

View File

@ -11,11 +11,7 @@ def test_create_post(db: DbContext, lexicon_with_editor):
lexicon, editor = lexicon_with_editor lexicon, editor = lexicon_with_editor
# argument dictionary for post object # argument dictionary for post object
kwargs = { kwargs = {'lexicon_id': lexicon.id, 'user_id': editor.id, 'body': 'body'}
'lexicon_id': lexicon.id,
'user_id': editor.id,
'body': 'body'
}
# ids are integers # ids are integers
with pytest.raises(ArgumentError): with pytest.raises(ArgumentError):

View File

@ -12,14 +12,16 @@ def test_create_user(db: DbContext):
'password': 'password', 'password': 'password',
'display_name': 'User Name', 'display_name': 'User Name',
'email': 'user@example.com', 'email': 'user@example.com',
'is_site_admin': False 'is_site_admin': False,
} }
# Test length constraints # Test length constraints
with pytest.raises(ArgumentError): with pytest.raises(ArgumentError):
userq.create(db, **{**kwargs, 'username': 'me'}) userq.create(db, **{**kwargs, 'username': 'me'})
with pytest.raises(ArgumentError): with pytest.raises(ArgumentError):
userq.create(db, **{**kwargs, 'username': 'the right honorable user-name, esquire'}) userq.create(
db, **{**kwargs, 'username': 'the right honorable user-name, esquire'}
)
# Test allowed characters # Test allowed characters
with pytest.raises(ArgumentError): with pytest.raises(ArgumentError):
userq.create(db, **{**kwargs, 'username': 'user name'}) userq.create(db, **{**kwargs, 'username': 'user name'})