Browse Source

Use postponed evaluation of type annotations

The __future__ import will be able to be removed as of Python 3.10.
merge-requests/126/merge
Deimos 4 years ago
parent
commit
8144a8b789
  1. 3
      tildes/tildes/lib/datetime.py
  2. 7
      tildes/tildes/lib/ratelimit.py
  3. 7
      tildes/tildes/models/comment/comment_notification_query.py
  4. 15
      tildes/tildes/models/comment/comment_query.py
  5. 5
      tildes/tildes/models/group/group_query.py
  6. 21
      tildes/tildes/models/model_query.py
  7. 13
      tildes/tildes/models/pagination.py
  8. 7
      tildes/tildes/models/topic/topic.py
  9. 31
      tildes/tildes/models/topic/topic_query.py

3
tildes/tildes/lib/datetime.py

@ -3,6 +3,7 @@
"""Functions/classes related to dates and times."""
from __future__ import annotations
import re
from datetime import datetime, timedelta, timezone
from typing import Any, Optional
@ -30,7 +31,7 @@ class SimpleHoursPeriod:
raise ValueError("Time period is too large")
@classmethod
def from_short_form(cls, short_form: str) -> "SimpleHoursPeriod":
def from_short_form(cls, short_form: str) -> SimpleHoursPeriod:
"""Initialize a period from a "short form" string (e.g. "2h", "4d")."""
if not cls._SHORT_FORM_REGEX.match(short_form):
raise ValueError("Invalid time period")

7
tildes/tildes/lib/ratelimit.py

@ -3,6 +3,7 @@
"""Classes and constants related to rate-limited actions."""
from __future__ import annotations
from datetime import timedelta
from ipaddress import ip_address
from typing import Any, List, Optional, Sequence
@ -58,7 +59,7 @@ class RateLimitResult:
)
@classmethod
def unlimited_result(cls) -> "RateLimitResult":
def unlimited_result(cls) -> RateLimitResult:
"""Return a "blank" result representing an unlimited action."""
return cls(
is_allowed=True,
@ -68,7 +69,7 @@ class RateLimitResult:
)
@classmethod
def from_redis_cell_result(cls, result: List[int]) -> "RateLimitResult":
def from_redis_cell_result(cls, result: List[int]) -> RateLimitResult:
"""Convert the response from CL.THROTTLE command to a RateLimitResult.
CL.THROTTLE responds with an array of 5 integers:
@ -98,7 +99,7 @@ class RateLimitResult:
)
@classmethod
def merged_result(cls, results: Sequence["RateLimitResult"]) -> "RateLimitResult":
def merged_result(cls, results: Sequence["RateLimitResult"]) -> RateLimitResult:
"""Merge any number of RateLimitResults into a single result.
Basically, the merged result should be the "most restrictive" combination of all

7
tildes/tildes/models/comment/comment_notification_query.py

@ -3,6 +3,7 @@
"""Contains the CommentNotificationQuery class."""
from __future__ import annotations
from typing import Any
from pyramid.request import Request
@ -32,7 +33,7 @@ class CommentNotificationQuery(PaginatedQuery):
.subquery()
)
def _attach_extra_data(self) -> "CommentNotificationQuery":
def _attach_extra_data(self) -> CommentNotificationQuery:
"""Attach the user's comment votes to the query."""
vote_subquery = (
self.request.query(CommentVote)
@ -45,7 +46,7 @@ class CommentNotificationQuery(PaginatedQuery):
)
return self.add_columns(vote_subquery)
def join_all_relationships(self) -> "CommentNotificationQuery":
def join_all_relationships(self) -> CommentNotificationQuery:
"""Eagerly join the comment, topic, and group to the notification."""
# pylint: disable=self-cls-assignment
self = self.options(
@ -69,7 +70,7 @@ class CommentNotificationQuery(PaginatedQuery):
return notification
def get_page(self, per_page: int) -> "CommentNotificationResults":
def get_page(self, per_page: int) -> CommentNotificationResults:
"""Get a page worth of results from the query (`per page` items)."""
return CommentNotificationResults(self, per_page)

15
tildes/tildes/models/comment/comment_query.py

@ -3,6 +3,7 @@
"""Contains the CommentQuery class."""
from __future__ import annotations
from typing import Any
from pyramid.request import Request
@ -31,7 +32,7 @@ class CommentQuery(PaginatedQuery):
self._only_bookmarked = False
self._only_user_voted = False
def _attach_extra_data(self) -> "CommentQuery":
def _attach_extra_data(self) -> CommentQuery:
"""Attach the extra user data to the query."""
# pylint: disable=protected-access
if not self.request.user:
@ -39,7 +40,7 @@ class CommentQuery(PaginatedQuery):
return self._attach_vote_data()._attach_bookmark_data()
def _attach_vote_data(self) -> "CommentQuery":
def _attach_vote_data(self) -> CommentQuery:
"""Join the data related to whether the user has voted on the comment."""
query = self.join(
CommentVote,
@ -53,7 +54,7 @@ class CommentQuery(PaginatedQuery):
return query
def _attach_bookmark_data(self) -> "CommentQuery":
def _attach_bookmark_data(self) -> CommentQuery:
"""Join the data related to whether the user has bookmarked the comment."""
query = self.join(
CommentBookmark,
@ -86,7 +87,7 @@ class CommentQuery(PaginatedQuery):
def apply_sort_option(
self, sort: CommentSortOption, desc: bool = True
) -> "CommentQuery":
) -> CommentQuery:
"""Apply a CommentSortOption sorting method (generative)."""
if sort == CommentSortOption.VOTES:
self._sort_column = Comment.num_votes
@ -97,18 +98,18 @@ class CommentQuery(PaginatedQuery):
return self
def search(self, query: str) -> "CommentQuery":
def search(self, query: str) -> CommentQuery:
"""Restrict the comments to ones that match a search query (generative)."""
return self.filter(
Comment.search_tsv.op("@@")(func.websearch_to_tsquery(query))
)
def only_bookmarked(self) -> "CommentQuery":
def only_bookmarked(self) -> CommentQuery:
"""Restrict the comments to ones that the user has bookmarked (generative)."""
self._only_bookmarked = True
return self
def only_user_voted(self) -> "CommentQuery":
def only_user_voted(self) -> CommentQuery:
"""Restrict the comments to ones that the user has voted on (generative)."""
self._only_user_voted = True
return self

5
tildes/tildes/models/group/group_query.py

@ -3,6 +3,7 @@
"""Contains the GroupQuery class."""
from __future__ import annotations
from typing import Any
from pyramid.request import Request
@ -24,14 +25,14 @@ class GroupQuery(ModelQuery):
"""
super().__init__(Group, request)
def _attach_extra_data(self) -> "GroupQuery":
def _attach_extra_data(self) -> GroupQuery:
"""Attach the extra user data to the query."""
if not self.request.user:
return self
return self._attach_subscription_data()
def _attach_subscription_data(self) -> "GroupQuery":
def _attach_subscription_data(self) -> GroupQuery:
"""Add a subquery to include whether the user is subscribed."""
subscription_subquery = (
self.request.query(GroupSubscription)

21
tildes/tildes/models/model_query.py

@ -4,6 +4,7 @@
"""Contains the ModelQuery class, a specialized SQLAlchemy Query subclass."""
# pylint: disable=self-cls-assignment
from __future__ import annotations
from typing import Any, Iterator, TypeVar
from pyramid.request import Request
@ -40,11 +41,11 @@ class ModelQuery(Query):
results = super().__iter__()
return iter([self._process_result(result) for result in results])
def _attach_extra_data(self) -> "ModelQuery":
def _attach_extra_data(self) -> ModelQuery:
"""Override to attach extra data to query before execution."""
return self
def _finalize(self) -> "ModelQuery":
def _finalize(self) -> ModelQuery:
"""Finalize the query before it's executed."""
# pylint: disable=protected-access
@ -59,7 +60,7 @@ class ModelQuery(Query):
._filter_removed_if_necessary()
)
def _before_compile_listener(self) -> "ModelQuery":
def _before_compile_listener(self) -> ModelQuery:
"""Do any final adjustments to the query before it's compiled.
Note that this method cannot be overridden by subclasses because of the way it
@ -68,21 +69,21 @@ class ModelQuery(Query):
"""
return self._finalize()
def _filter_deleted_if_necessary(self) -> "ModelQuery":
def _filter_deleted_if_necessary(self) -> ModelQuery:
"""Filter out deleted rows unless they were explicitly included."""
if not self.filter_deleted:
return self
return self.filter(self.model_cls.is_deleted == False) # noqa
def _filter_removed_if_necessary(self) -> "ModelQuery":
def _filter_removed_if_necessary(self) -> ModelQuery:
"""Filter out removed rows unless they were explicitly included."""
if not self.filter_removed:
return self
return self.filter(self.model_cls.is_removed == False) # noqa
def lock_based_on_request_method(self) -> "ModelQuery":
def lock_based_on_request_method(self) -> ModelQuery:
"""Lock the rows if request method implies it's needed (generative).
Applying this function to a query will cause the database to acquire a row-level
@ -98,19 +99,19 @@ class ModelQuery(Query):
return self
def include_deleted(self) -> "ModelQuery":
def include_deleted(self) -> ModelQuery:
"""Specify that deleted rows should be included (generative)."""
self.filter_deleted = False
return self
def include_removed(self) -> "ModelQuery":
def include_removed(self) -> ModelQuery:
"""Specify that removed rows should be included (generative)."""
self.filter_removed = False
return self
def join_all_relationships(self) -> "ModelQuery":
def join_all_relationships(self) -> ModelQuery:
"""Eagerly join all lazy relationships (generative).
This is useful for being able to load an item "fully" in a single query and
@ -120,7 +121,7 @@ class ModelQuery(Query):
return self
def undefer_all_columns(self) -> "ModelQuery":
def undefer_all_columns(self) -> ModelQuery:
"""Undefer all columns (generative)."""
self = self.options(undefer("*"))

13
tildes/tildes/models/pagination.py

@ -3,6 +3,7 @@
"""Contains the PaginatedQuery and PaginatedResults classes."""
from __future__ import annotations
from itertools import chain
from typing import Any, Iterator, List, Optional, Sequence, TypeVar
@ -85,14 +86,14 @@ class PaginatedQuery(ModelQuery):
"""
return bool(self.before_id)
def anchor_type(self, anchor_type: str) -> "PaginatedQuery":
def anchor_type(self, anchor_type: str) -> PaginatedQuery:
"""Set the type of the "anchor" (before/after item) (generative)."""
anchor_table_name = anchor_type + "s"
self._anchor_table = self.model_cls.metadata.tables.get(anchor_table_name)
return self
def after_id36(self, id36: str) -> "PaginatedQuery":
def after_id36(self, id36: str) -> PaginatedQuery:
"""Restrict the query to results after an id36 (generative)."""
if self.before_id:
raise ValueError("Can't set both before and after restrictions")
@ -101,7 +102,7 @@ class PaginatedQuery(ModelQuery):
return self
def before_id36(self, id36: str) -> "PaginatedQuery":
def before_id36(self, id36: str) -> PaginatedQuery:
"""Restrict the query to results before an id36 (generative)."""
if self.after_id:
raise ValueError("Can't set both before and after restrictions")
@ -110,7 +111,7 @@ class PaginatedQuery(ModelQuery):
return self
def _apply_before_or_after(self) -> "PaginatedQuery":
def _apply_before_or_after(self) -> PaginatedQuery:
"""Apply the "before" or "after" restrictions if necessary."""
# pylint: disable=assignment-from-no-return
if not (self.after_id or self.before_id):
@ -165,7 +166,7 @@ class PaginatedQuery(ModelQuery):
.subquery()
)
def _finalize(self) -> "PaginatedQuery":
def _finalize(self) -> PaginatedQuery:
"""Finalize the query before execution."""
query = super()._finalize()
@ -185,7 +186,7 @@ class PaginatedQuery(ModelQuery):
return query
def get_page(self, per_page: int) -> "PaginatedResults":
def get_page(self, per_page: int) -> PaginatedResults:
"""Get a page worth of results from the query (`per page` items)."""
return PaginatedResults(self, per_page)

7
tildes/tildes/models/topic/topic.py

@ -3,6 +3,7 @@
"""Contains the Topic class."""
from __future__ import annotations
from datetime import datetime, timedelta
from itertools import chain
from pathlib import PurePosixPath
@ -217,7 +218,7 @@ class Topic(DatabaseModel):
return f'<Topic "{self.title}" ({self.topic_id})>'
@classmethod
def _create_base_topic(cls, group: Group, author: User, title: str) -> "Topic":
def _create_base_topic(cls, group: Group, author: User, title: str) -> Topic:
"""Create the "base" for a new topic."""
new_topic = cls()
new_topic.group = group
@ -234,7 +235,7 @@ class Topic(DatabaseModel):
@classmethod
def create_text_topic(
cls, group: Group, author: User, title: str, markdown: str = ""
) -> "Topic":
) -> Topic:
"""Create a new text topic."""
new_topic = cls._create_base_topic(group, author, title)
new_topic.topic_type = TopicType.TEXT
@ -245,7 +246,7 @@ class Topic(DatabaseModel):
@classmethod
def create_link_topic(
cls, group: Group, author: User, title: str, link: str
) -> "Topic":
) -> Topic:
"""Create a new link topic."""
new_topic = cls._create_base_topic(group, author, title)
new_topic.topic_type = TopicType.LINK

31
tildes/tildes/models/topic/topic_query.py

@ -3,6 +3,7 @@
"""Contains the TopicQuery class."""
from __future__ import annotations
from typing import Any, Sequence
from pyramid.request import Request
@ -40,7 +41,7 @@ class TopicQuery(PaginatedQuery):
self.filter_ignored = False
def _attach_extra_data(self) -> "TopicQuery":
def _attach_extra_data(self) -> TopicQuery:
"""Attach the extra user data to the query."""
if not self.request.user:
return self
@ -53,7 +54,7 @@ class TopicQuery(PaginatedQuery):
._attach_ignored_data()
)
def _finalize(self) -> "TopicQuery":
def _finalize(self) -> TopicQuery:
"""Finalize the query before it's executed."""
# pylint: disable=self-cls-assignment
self = super()._finalize()
@ -63,7 +64,7 @@ class TopicQuery(PaginatedQuery):
return self
def _attach_vote_data(self) -> "TopicQuery":
def _attach_vote_data(self) -> TopicQuery:
"""Join the data related to whether the user has voted on the topic."""
query = self.join(
TopicVote,
@ -77,7 +78,7 @@ class TopicQuery(PaginatedQuery):
return query
def _attach_bookmark_data(self) -> "TopicQuery":
def _attach_bookmark_data(self) -> TopicQuery:
"""Join the data related to whether the user has bookmarked the topic."""
query = self.join(
TopicBookmark,
@ -91,7 +92,7 @@ class TopicQuery(PaginatedQuery):
return query
def _attach_visit_data(self) -> "TopicQuery":
def _attach_visit_data(self) -> TopicQuery:
"""Join the data related to the user's last visit to the topic(s)."""
# subquery using LATERAL to select only the newest visit for each topic
lateral_subquery = (
@ -116,7 +117,7 @@ class TopicQuery(PaginatedQuery):
return query
def _attach_ignored_data(self) -> "TopicQuery":
def _attach_ignored_data(self) -> TopicQuery:
"""Join the data related to whether the user has ignored the topic."""
query = self.join(
TopicIgnore,
@ -160,7 +161,7 @@ class TopicQuery(PaginatedQuery):
def apply_sort_option(
self, sort: TopicSortOption, is_desc: bool = True
) -> "TopicQuery":
) -> TopicQuery:
"""Apply a TopicSortOption sorting method (generative)."""
if sort == TopicSortOption.VOTES:
self._sort_column = Topic.num_votes
@ -179,7 +180,7 @@ class TopicQuery(PaginatedQuery):
def inside_groups(
self, groups: Sequence[Group], include_subgroups: bool = True
) -> "TopicQuery":
) -> TopicQuery:
"""Restrict the topics to inside specific groups (generative)."""
if include_subgroups:
query_paths = [group.path for group in groups]
@ -191,7 +192,7 @@ class TopicQuery(PaginatedQuery):
return self.filter(Topic.group_id.in_(group_ids)) # type: ignore
def inside_time_period(self, period: SimpleHoursPeriod) -> "TopicQuery":
def inside_time_period(self, period: SimpleHoursPeriod) -> TopicQuery:
"""Restrict the topics to inside a time period (generative)."""
# if the time period is too long, this will crash by creating a datetime outside
# the valid range - catch that and just don't filter by time period at all if
@ -203,7 +204,7 @@ class TopicQuery(PaginatedQuery):
return self.filter(Topic.created_time > start_time)
def has_tag(self, tag: str) -> "TopicQuery":
def has_tag(self, tag: str) -> TopicQuery:
"""Restrict the topics to ones with a specific tag (generative).
Note that this method searches for topics that have any tag that contains
@ -214,7 +215,7 @@ class TopicQuery(PaginatedQuery):
# pylint: disable=protected-access
return self.filter(Topic.tags.lquery(query)) # type: ignore
def search(self, query: str) -> "TopicQuery":
def search(self, query: str) -> TopicQuery:
"""Restrict the topics to ones that match a search query (generative)."""
# Replace "." with space, since tags are stored as space-separated strings
# in the search index.
@ -223,24 +224,24 @@ class TopicQuery(PaginatedQuery):
return self.filter(Topic.search_tsv.op("@@")(func.websearch_to_tsquery(query)))
def only_bookmarked(self) -> "TopicQuery":
def only_bookmarked(self) -> TopicQuery:
"""Restrict the topics to ones that the user has bookmarked (generative)."""
self._only_bookmarked = True
return self
def only_user_voted(self) -> "TopicQuery":
def only_user_voted(self) -> TopicQuery:
"""Restrict the topics to ones that the user has voted on (generative)."""
self._only_user_voted = True
return self
def only_ignored(self) -> "TopicQuery":
def only_ignored(self) -> TopicQuery:
"""Restrict the topics to ones that the user has ignored (generative)."""
# pylint: disable=self-cls-assignment
self._only_ignored = True
return self
def exclude_ignored(self) -> "TopicQuery":
def exclude_ignored(self) -> TopicQuery:
"""Specify that ignored topics should be excluded (generative)."""
self.filter_ignored = True

Loading…
Cancel
Save