Adopt black as a code formatter #6

Merged
Jaculabilis merged 3 commits from tvb/black-style into develop 2021-06-03 03:25:23 +00:00
15 changed files with 91 additions and 85 deletions
Showing only changes of commit de09030b1c - Show all commits

View File

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

View File

@ -9,11 +9,8 @@ from amanuensis.errors import ArgumentError
def create(
db: DbContext,
lexicon_id: int,
user_id: int,
name: str,
signature: str) -> Character:
db: DbContext, lexicon_id: int, user_id: int, name: str, signature: str
) -> Character:
"""
Create a new character for a user.
"""
@ -50,7 +47,10 @@ def create(
.where(Character.lexicon_id == lexicon_id)
.where(Character.user_id == user_id)
).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')
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-_]*$')
def create(
db: DbContext,
name: str,
title: str,
prompt: str) -> Lexicon:
def create(db: DbContext, name: str, title: str, prompt: str) -> Lexicon:
"""
Create a new lexicon.
"""
@ -27,7 +23,9 @@ def create(
if not name.strip():
raise ArgumentError('Lexicon name must not be blank')
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
if title is not None and not isinstance(name, str):
@ -38,10 +36,7 @@ def create(
raise ArgumentError('Lexicon prompt must be a string')
# 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:
raise ArgumentError('Lexicon name is already taken')
new_lexicon = Lexicon(

View File

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

View File

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

View File

@ -11,7 +11,7 @@ from amanuensis.db import DbContext, User
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-_]*$')
@ -21,7 +21,8 @@ def create(
password: str,
display_name: str,
email: str,
is_site_admin: bool) -> User:
is_site_admin: bool,
) -> User:
"""
Create a new user.
"""
@ -33,7 +34,9 @@ def create(
if RE_NO_LETTERS.match(username):
raise ArgumentError('Username must contain a letter')
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
if not isinstance(password, str):
@ -51,10 +54,7 @@ def create(
raise ArgumentError('Email must be a string')
# 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:
raise ArgumentError('Username is already taken')
new_user = User(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -52,7 +52,9 @@ def test_character_limits(db: DbContext, lexicon_with_editor):
# Creating a second character should fail
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
# 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
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
# 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):
"""Test new game creation."""
kwargs = {
'name': 'Test',
'title': None,
'prompt': 'A test Lexicon game'
}
kwargs = {'name': 'Test', 'title': None, 'prompt': 'A test Lexicon game'}
# Test name constraints
with pytest.raises(ArgumentError):
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
# argument dictionary for post object
kwargs = {
'lexicon_id': lexicon.id,
'user_id': editor.id,
'body': 'body'
}
kwargs = {'lexicon_id': lexicon.id, 'user_id': editor.id, 'body': 'body'}
# ids are integers
with pytest.raises(ArgumentError):

View File

@ -12,14 +12,16 @@ def test_create_user(db: DbContext):
'password': 'password',
'display_name': 'User Name',
'email': 'user@example.com',
'is_site_admin': False
'is_site_admin': False,
}
# Test length constraints
with pytest.raises(ArgumentError):
userq.create(db, **{**kwargs, 'username': 'me'})
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
with pytest.raises(ArgumentError):
userq.create(db, **{**kwargs, 'username': 'user name'})