Skip to content

Commit

Permalink
Drop redundant mapped_column args; further typing fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
jace committed Dec 30, 2023
1 parent 36038dd commit 74fb3fb
Show file tree
Hide file tree
Showing 35 changed files with 340 additions and 413 deletions.
17 changes: 7 additions & 10 deletions funnel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
baseframe.init_app(
app,
requires=['funnel'],
theme='funnel', # type: ignore[arg-type]
theme='funnel', # type: ignore[arg-type] # FIXME
error_handlers=False,
)

Expand All @@ -162,7 +162,7 @@
transports.init()

# Register JS and CSS assets on both apps
app.assets.register( # type: ignore[attr-defined]
app.assets.register( # type: ignore[attr-defined] # FIXME
'js_fullcalendar',
Bundle(
assets.require(
Expand All @@ -178,15 +178,15 @@
filters='rjsmin',
),
)
app.assets.register( # type: ignore[attr-defined]
app.assets.register( # type: ignore[attr-defined] # FIXME
'css_fullcalendar',
Bundle(
assets.require('jquery.fullcalendar.css', 'spectrum.css'),
output='css/fullcalendar.packed.css',
filters='cssmin',
),
)
app.assets.register( # type: ignore[attr-defined]
app.assets.register( # type: ignore[attr-defined] # FIXME
'js_schedules',
Bundle(
assets.require('schedules.js'),
Expand All @@ -199,12 +199,9 @@

# --- Serve static files with WhiteNoise -----------------------------------------------

app.wsgi_app = WhiteNoise( # type: ignore[method-assign]
app.wsgi_app, root=app.static_folder, prefix=app.static_url_path
)
app.wsgi_app.add_files( # type: ignore[attr-defined]
baseframe.static_folder, prefix=baseframe.static_url_path
)
_wn = WhiteNoise(app.wsgi_app, root=app.static_folder, prefix=app.static_url_path)
_wn.add_files(baseframe.static_folder, prefix=baseframe.static_url_path)
app.wsgi_app = _wn # type: ignore[method-assign]

# --- Init SQLAlchemy mappers ----------------------------------------------------------

Expand Down
6 changes: 3 additions & 3 deletions funnel/devtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def _signature_without_annotations(func) -> inspect.Signature:
)


def install_mock(func: Callable, mock: Callable) -> None:
def install_mock(func: Any, mock: Any) -> None:
"""
Patch all existing references to :attr:`func` with :attr:`mock`.
Expand All @@ -161,9 +161,9 @@ def install_mock(func: Callable, mock: Callable) -> None:
# Use weakref to dereference func from local namespace
func = weakref.ref(func)
gc.collect()
refs = gc.get_referrers(func()) # type: ignore[misc] # Typeshed says not callable
refs = gc.get_referrers(func())
# Recover func from the weakref so we can do an `is` match in referrers
func = func() # type: ignore[misc]
func = func()
for ref in refs:
if isinstance(ref, dict):
# We have a namespace dict. Iterate through contents to find the reference
Expand Down
6 changes: 2 additions & 4 deletions funnel/forms/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
class AccountSelectField(forms.AutocompleteField):
"""Render an autocomplete field for selecting an account."""

data: Account | None # type: ignore[assignment]
data: Account | None # type: ignore[assignment] # FIXME
widget = forms.Select2Widget()
multiple = False
widget_autocomplete = True
Expand Down Expand Up @@ -231,9 +231,7 @@ def image_url_validator() -> forms.validators.ValidUrl:
"""Customise ValidUrl for hosted image URL validation."""
return forms.validators.ValidUrl(
allowed_schemes=lambda: app.config.get('IMAGE_URL_SCHEMES', ('https',)),
allowed_domains=lambda: app.config.get( # type: ignore[arg-type, return-value]
'IMAGE_URL_DOMAINS'
),
allowed_domains=lambda: app.config.get('IMAGE_URL_DOMAINS'),
message_schemes=__("A https:// URL is required"),
message_domains=__("Images must be hosted at images.hasgeek.com"),
)
Expand Down
5 changes: 4 additions & 1 deletion funnel/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ class GeonameModel(ModelBase, DeclarativeBase):
# This must be set _before_ any of the models using db.Model are imported
TimestampMixin.__with_timezone__ = True

db: SQLAlchemy = SQLAlchemy(query_class=Query, metadata=Model.metadata) # type: ignore[arg-type]
db: SQLAlchemy = SQLAlchemy(
query_class=Query, # type: ignore[arg-type]
metadata=Model.metadata,
)
Model.init_flask_sqlalchemy(db)
GeonameModel.init_flask_sqlalchemy(db)

Expand Down
66 changes: 21 additions & 45 deletions funnel/models/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class Account(UuidMixin, BaseMixin[int, 'Account'], Model):
username: Mapped[str | None] = sa_orm.synonym('name')

#: Argon2 or Bcrypt hash of the user's password
pw_hash: Mapped[str | None] = sa_orm.mapped_column(sa.Unicode, nullable=True)
pw_hash: Mapped[str | None] = sa_orm.mapped_column()
#: Timestamp for when the user's password last changed
pw_set_at: Mapped[datetime | None] = sa_orm.mapped_column(
sa.TIMESTAMP(timezone=True), nullable=True
Expand All @@ -221,17 +221,13 @@ class Account(UuidMixin, BaseMixin[int, 'Account'], Model):
read={'owner'},
)
#: Update timezone automatically from browser activity
auto_timezone: Mapped[bool] = sa_orm.mapped_column(
sa.Boolean, default=True, nullable=False
)
auto_timezone: Mapped[bool] = sa_orm.mapped_column(default=True)
#: User's preferred/last known locale
locale: Mapped[Locale | None] = with_roles(
sa_orm.mapped_column(LocaleType, nullable=True), read={'owner'}
)
#: Update locale automatically from browser activity
auto_locale: Mapped[bool] = sa_orm.mapped_column(
sa.Boolean, default=True, nullable=False
)
auto_locale: Mapped[bool] = sa_orm.mapped_column(default=True)
#: User's state code (active, suspended, merged, deleted)
_state: Mapped[int] = sa_orm.mapped_column(
'state',
Expand Down Expand Up @@ -259,7 +255,7 @@ class Account(UuidMixin, BaseMixin[int, 'Account'], Model):
)

tagline: Mapped[str | None] = sa_orm.mapped_column(
sa.Unicode, sa.CheckConstraint("tagline <> ''"), nullable=True
sa.CheckConstraint("tagline <> ''")
)
description, description_text, description_html = MarkdownCompositeDocument.create(
'description', default='', nullable=False
Expand All @@ -279,20 +275,18 @@ class Account(UuidMixin, BaseMixin[int, 'Account'], Model):

#: Protected accounts cannot be deleted
is_protected: Mapped[bool] = with_roles(
immutable(sa_orm.mapped_column(sa.Boolean, default=False, nullable=False)),
immutable(sa_orm.mapped_column(default=False)),
read={'owner', 'admin'},
)
#: Verified accounts get listed on the home page and are not considered throwaway
#: accounts for spam control. There are no other privileges at this time
is_verified: Mapped[bool] = with_roles(
sa_orm.mapped_column(sa.Boolean, default=False, nullable=False, index=True),
sa_orm.mapped_column(default=False, index=True),
read={'all'},
)

#: Revision number maintained by SQLAlchemy, starting at 1
revisionid: Mapped[int] = with_roles(
sa_orm.mapped_column(sa.Integer, nullable=False), read={'all'}
)
revisionid: Mapped[int] = with_roles(sa_orm.mapped_column(), read={'all'})

search_vector: Mapped[str] = sa_orm.mapped_column(
TSVectorType(
Expand Down Expand Up @@ -1081,9 +1075,7 @@ def _set_password(self, password: str | None):
# Also see :meth:`password_is` for transparent upgrade
self.pw_set_at = sa.func.utcnow()
# Expire passwords after one year. TODO: make this configurable
self.pw_expires_at = self.pw_set_at + sa.cast( # type: ignore[assignment]
'1 year', sa.Interval
)
self.pw_expires_at = sa.func.utcnow() + sa.cast('1 year', sa.Interval)

#: Write-only property (passwords cannot be read back in plain text)
password = property(fset=_set_password, doc=_set_password.__doc__)
Expand Down Expand Up @@ -1900,7 +1892,7 @@ class AccountOldId(UuidMixin, BaseMixin[UUID, Account], Model):
)
#: User id of new user
account_id: Mapped[int] = sa_orm.mapped_column(
sa.ForeignKey('account.id'), nullable=False
sa.ForeignKey('account.id'), default=None, nullable=False
)
#: New account
account: Mapped[Account] = relationship(
Expand Down Expand Up @@ -2056,7 +2048,7 @@ class Team(UuidMixin, BaseMixin[int, Account], Model):
)
#: Organization
account_id: Mapped[int] = sa_orm.mapped_column(
sa.ForeignKey('account.id'), nullable=False, index=True
sa.ForeignKey('account.id'), default=None, nullable=False, index=True
)
account: Mapped[Account] = with_roles(
relationship(foreign_keys=[account_id], back_populates='teams'),
Expand All @@ -2069,9 +2061,7 @@ class Team(UuidMixin, BaseMixin[int, Account], Model):
grants={'member'},
)

is_public: Mapped[bool] = sa_orm.mapped_column(
sa.Boolean, nullable=False, default=False
)
is_public: Mapped[bool] = sa_orm.mapped_column(default=False)

# --- Backrefs
client_permissions: Mapped[list[AuthClientTeamPermissions]] = relationship(
Expand Down Expand Up @@ -2128,18 +2118,13 @@ class AccountEmail(EmailAddressMixin, BaseMixin[int, Account], Model):
__email_is_exclusive__ = True
__email_for__ = 'account'

# Tell mypy that these are not optional
email_address: Mapped[EmailAddress] # type: ignore[assignment]

account_id: Mapped[int] = sa_orm.mapped_column(
sa.ForeignKey('account.id'), nullable=False
sa.ForeignKey('account.id'), default=None, nullable=False
)
account: Mapped[Account] = relationship(back_populates='emails')
user: Mapped[Account] = sa_orm.synonym('account')

private: Mapped[bool] = sa_orm.mapped_column(
sa.Boolean, nullable=False, default=False
)
private: Mapped[bool] = sa_orm.mapped_column(default=False)

__datasets__ = {
'primary': {'member', 'email', 'private', 'type'},
Expand Down Expand Up @@ -2306,21 +2291,16 @@ class AccountEmailClaim(EmailAddressMixin, BaseMixin[int, Account], Model):
__email_for__ = 'account'
__email_is_exclusive__ = False

# Tell mypy that these are not optional
email_address: Mapped[EmailAddress] # type: ignore[assignment]

account_id: Mapped[int] = sa_orm.mapped_column(
sa.ForeignKey('account.id'), nullable=False
sa.ForeignKey('account.id'), default=None, nullable=False
)
account: Mapped[Account] = relationship(back_populates='emailclaims')
user: Mapped[Account] = sa_orm.synonym('account')
verification_code: Mapped[str] = sa_orm.mapped_column(
sa.String(44), nullable=False, default=newsecret
sa.String(44), nullable=False, insert_default=newsecret, default=None
)

private: Mapped[bool] = sa_orm.mapped_column(
sa.Boolean, nullable=False, default=False
)
private: Mapped[bool] = sa_orm.mapped_column(default=False)

__table_args__ = (sa.UniqueConstraint('account_id', 'email_address_id'),)

Expand Down Expand Up @@ -2496,14 +2476,12 @@ class AccountPhone(PhoneNumberMixin, BaseMixin[int, Account], Model):
__phone_for__ = 'account'

account_id: Mapped[int] = sa_orm.mapped_column(
sa.ForeignKey('account.id'), nullable=False
sa.ForeignKey('account.id'), default=None
)
account: Mapped[Account] = relationship(back_populates='phones')
user: Mapped[Account] = sa_orm.synonym('account')

private: Mapped[bool] = sa_orm.mapped_column(
sa.Boolean, nullable=False, default=False
)
private: Mapped[bool] = sa_orm.mapped_column(default=False)

__datasets__ = {
'primary': {'member', 'phone', 'private', 'type'},
Expand Down Expand Up @@ -2682,7 +2660,7 @@ class AccountExternalId(BaseMixin[int, Account], Model):
__at_username_services__: ClassVar[list[str]] = []
#: Foreign key to user table
account_id: Mapped[int] = sa_orm.mapped_column(
sa.ForeignKey('account.id'), nullable=False
sa.ForeignKey('account.id'), default=None
)
#: User that this connected account belongs to
account: Mapped[Account] = relationship(back_populates='externalids')
Expand Down Expand Up @@ -2719,17 +2697,15 @@ class AccountExternalId(BaseMixin[int, Account], Model):
sa.UnicodeText, nullable=True
)
#: OAuth2 token expiry in seconds, as sent by service provider
oauth_expires_in: Mapped[int | None] = sa_orm.mapped_column(
sa.Integer, nullable=True
)
oauth_expires_in: Mapped[int | None] = sa_orm.mapped_column()
#: OAuth2 token expiry timestamp, estimate from created_at + oauth_expires_in
oauth_expires_at: Mapped[datetime | None] = sa_orm.mapped_column(
sa.TIMESTAMP(timezone=True), nullable=True, index=True
)

#: Timestamp of when this connected account was last (re-)authorised by the user
last_used_at: Mapped[datetime] = sa_orm.mapped_column(
sa.TIMESTAMP(timezone=True), default=sa.func.utcnow(), nullable=False
sa.TIMESTAMP(timezone=True), insert_default=sa.func.utcnow(), default=None
)

__table_args__ = (
Expand Down
6 changes: 2 additions & 4 deletions funnel/models/account_membership.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ class AccountMembership(ImmutableUserMembershipMixin, Model):

#: Organization that this membership is being granted on
account_id: Mapped[int] = sa_orm.mapped_column(
sa.Integer,
sa.ForeignKey('account.id', ondelete='CASCADE'),
default=None,
nullable=False,
)
account: Mapped[Account] = with_roles(
Expand All @@ -91,9 +91,7 @@ class AccountMembership(ImmutableUserMembershipMixin, Model):
parent: Mapped[Account] = sa_orm.synonym('account')

# Organization roles:
is_owner: Mapped[bool] = immutable(
sa_orm.mapped_column(sa.Boolean, nullable=False, default=False)
)
is_owner: Mapped[bool] = immutable(sa_orm.mapped_column(default=False))

@cached_property
def offered_roles(self) -> set[str]:
Expand Down
Loading

0 comments on commit 74fb3fb

Please sign in to comment.