You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by ke...@apache.org on 2021/10/08 20:41:35 UTC

[allura] 02/04: Add type hints for all mapped classes' query attrs

This is an automated email from the ASF dual-hosted git repository.

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 1f1fd23c203ed990b80ca27e758ec36b66b09826
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Wed Sep 29 10:01:09 2021 -0400

    Add type hints for all mapped classes' query attrs
---
 Allura/allura/model/artifact.py                | 31 +++++++++++++++++++++++++-
 Allura/allura/model/attachments.py             |  7 ++++++
 Allura/allura/model/auth.py                    | 16 ++++++++++++-
 Allura/allura/model/discuss.py                 | 17 ++++++++++++++
 Allura/allura/model/filesystem.py              |  7 ++++++
 Allura/allura/model/monq_model.py              |  7 ++++++
 Allura/allura/model/multifactor.py             |  8 +++++++
 Allura/allura/model/neighborhood.py            |  9 ++++++++
 Allura/allura/model/notification.py            | 13 ++++++++---
 Allura/allura/model/oauth.py                   | 13 +++++++++++
 Allura/allura/model/project.py                 | 16 ++++++++++++-
 Allura/allura/model/repository.py              | 10 +++++++++
 Allura/allura/model/stats.py                   |  6 +++++
 Allura/allura/model/webhook.py                 |  6 +++++
 ForgeBlog/forgeblog/model/blog.py              | 15 +++++++++++++
 ForgeChat/forgechat/model/chat.py              |  9 ++++++++
 ForgeDiscussion/forgediscussion/model/forum.py | 19 ++++++++++++++++
 ForgeFeedback/forgefeedback/model/feedback.py  | 10 +++++++--
 ForgeFiles/forgefiles/model/files.py           | 13 +++++++++++
 ForgeGit/forgegit/model/git_repo.py            |  7 ++++++
 ForgeSVN/forgesvn/model/svn.py                 |  8 +++++++
 ForgeShortUrl/forgeshorturl/model/shorturl.py  |  6 +++++
 ForgeTracker/forgetracker/model/ticket.py      | 20 +++++++++++++++++
 ForgeUserStats/forgeuserstats/model/stats.py   |  6 +++++
 ForgeWiki/forgewiki/model/wiki.py              | 15 +++++++++++++
 25 files changed, 286 insertions(+), 8 deletions(-)

diff --git a/Allura/allura/model/artifact.py b/Allura/allura/model/artifact.py
index 2fd9cb6..fe5992f 100644
--- a/Allura/allura/model/artifact.py
+++ b/Allura/allura/model/artifact.py
@@ -20,6 +20,7 @@ from __future__ import absolute_import
 import logging
 from collections import defaultdict
 from datetime import datetime
+import typing
 
 import pymongo
 from tg import tmpl_context as c, app_globals as g
@@ -49,6 +50,9 @@ from .notification import MailFooter
 from .filesystem import File
 import six
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 log = logging.getLogger(__name__)
 
 
@@ -83,6 +87,8 @@ class Artifact(MappedClass, SearchIndexable):
             if c.project and not skip_last_updated:
                 c.project.last_updated = datetime.utcnow()
 
+    query: 'Query[Artifact]'
+
     type_s = 'Generic Artifact'
 
     # Artifact base schema
@@ -500,6 +506,8 @@ class Snapshot(Artifact):
                    'author.id',
                    ]
 
+    query: 'Query[Snapshot]'
+
     _id = FieldProperty(S.ObjectId)
     artifact_id = FieldProperty(S.ObjectId)
     artifact_class = FieldProperty(str)
@@ -573,6 +581,8 @@ class VersionedArtifact(Artifact):
         name = str('versioned_artifact')
         history_class = Snapshot
 
+    query: 'Query[VersionedArtifact]'
+
     version = FieldProperty(S.Int, if_missing=0)
 
     def commit(self, update_stats=True):
@@ -678,7 +688,6 @@ class VersionedArtifact(Artifact):
 
 
 class Message(Artifact):
-
     """
     A message
 
@@ -690,6 +699,9 @@ class Message(Artifact):
     class __mongometa__:
         session = artifact_orm_session
         name = str('message')
+
+    query: 'Query[Message]'
+
     type_s = 'Generic Message'
 
     _id = FieldProperty(str, if_missing=h.gen_message_id)
@@ -738,6 +750,9 @@ class AwardFile(File):
     class __mongometa__:
         session = main_orm_session
         name = str('award_file')
+
+    query: 'Query[AwardFile]'
+
     award_id = FieldProperty(S.ObjectId)
 
 
@@ -747,6 +762,9 @@ class Award(Artifact):
         session = main_orm_session
         name = str('award')
         indexes = ['short']
+
+    query: 'Query[Award]'
+
     type_s = 'Generic Award'
 
     from .project import Neighborhood
@@ -790,6 +808,9 @@ class AwardGrant(Artifact):
         session = main_orm_session
         name = str('grant')
         indexes = ['short']
+
+    query: 'Query[AwardGrant]'
+
     type_s = 'Generic Award Grant'
 
     _id = FieldProperty(S.ObjectId)
@@ -876,6 +897,8 @@ class Feed(MappedClass):
              ('pubdate', pymongo.DESCENDING)),
         ]
 
+    query: 'Query[Feed]'
+
     _id = FieldProperty(S.ObjectId)
     ref_id = ForeignIdProperty('ArtifactReference')
     neighborhood_id = ForeignIdProperty('Neighborhood')
@@ -1001,6 +1024,8 @@ class VotableArtifact(MappedClass):
         session = main_orm_session
         name = str('vote')
 
+    query: 'Query[VotableArtifact]'
+
     votes = FieldProperty(int, if_missing=0)
     votes_up = FieldProperty(int, if_missing=0)
     votes_down = FieldProperty(int, if_missing=0)
@@ -1129,6 +1154,8 @@ class MovedArtifact(Artifact):
         session = artifact_orm_session
         name = str('moved_artifact')
 
+    query: 'Query[MovedArtifact]'
+
     _id = FieldProperty(S.ObjectId)
     app_config_id = ForeignIdProperty(
         'AppConfig', if_missing=lambda: c.app.config._id)
@@ -1145,6 +1172,8 @@ class SpamCheckResult(MappedClass):
             ('user_id', 'result'),
         ]
 
+    query: 'Query[SpamCheckResult]'
+
     _id = FieldProperty(S.ObjectId)
     ref_id = ForeignIdProperty('ArtifactReference')
     ref = RelationProperty('ArtifactReference', via='ref_id')
diff --git a/Allura/allura/model/attachments.py b/Allura/allura/model/attachments.py
index 36a8f66..d362ea0 100644
--- a/Allura/allura/model/attachments.py
+++ b/Allura/allura/model/attachments.py
@@ -17,6 +17,8 @@
 
 from __future__ import unicode_literals
 from __future__ import absolute_import
+import typing
+
 from tg import tmpl_context as c
 from ming.orm import FieldProperty
 from ming import schema as S
@@ -26,6 +28,9 @@ from allura.lib import helpers as h
 from .session import project_orm_session
 from .filesystem import File
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 
 class BaseAttachment(File):
     thumbnail_size = (255, 255)
@@ -38,6 +43,8 @@ class BaseAttachment(File):
         session = project_orm_session
         indexes = ['artifact_id', 'app_config_id']
 
+    query: 'Query[BaseAttachment]'
+
     artifact_id = FieldProperty(S.ObjectId)
     app_config_id = FieldProperty(S.ObjectId)
     type = FieldProperty(str)
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index e846a81..f33bb38 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -19,7 +19,7 @@ from __future__ import unicode_literals
 from __future__ import absolute_import
 import logging
 import calendar
-from typing import ClassVar
+import typing
 
 import six
 from markupsafe import Markup
@@ -55,6 +55,10 @@ from .session import project_orm_session
 from .timeline import ActivityNode, ActivityObject
 
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 log = logging.getLogger(__name__)
 
 
@@ -78,6 +82,8 @@ class EmailAddress(MappedClass):
         indexes = ['nonce', ]
         unique_indexes = [('email', 'claimed_by_user_id'), ]
 
+    query: 'Query[EmailAddress]'
+
     _id = FieldProperty(S.ObjectId)
     email = FieldProperty(str)
     claimed_by_user_id = FieldProperty(S.ObjectId, if_missing=None)
@@ -187,6 +193,8 @@ class AuthGlobals(MappedClass):
         name = str('auth_globals')
         session = main_orm_session
 
+    query: 'Query[AuthGlobals]'
+
     _id = FieldProperty(int)
     next_uid = FieldProperty(int, if_missing=10000)
 
@@ -241,6 +249,8 @@ class User(MappedClass, ActivityNode, ActivityObject, SearchIndexable):
             dict(fields=('tool_data.phone_verification.number_hash',), sparse=True),
         ]
 
+    query: 'Query[User]'
+
     type_s = 'User'
 
     _id = FieldProperty(S.ObjectId)
@@ -848,6 +858,8 @@ class ProjectRole(MappedClass):
             ('roles',),
         ]
 
+    query: 'Query[ProjectRole]'
+
     _id = FieldProperty(S.ObjectId)
     user_id = AlluraUserProperty(if_missing=None)
     project_id = ForeignIdProperty('Project', if_missing=None)
@@ -1064,6 +1076,8 @@ class UserLoginDetails(MappedClass):
         unique_indexes = [('user_id', 'ip', 'ua'),  # DuplicateKeyError checked in add_login_detail
                           ]
 
+    query: 'Query[UserLoginDetails]'
+
     _id = FieldProperty(S.ObjectId)
     user_id = AlluraUserProperty(required=True)
     ip = FieldProperty(str)
diff --git a/Allura/allura/model/discuss.py b/Allura/allura/model/discuss.py
index 2db1b5c..f0b4bc1 100644
--- a/Allura/allura/model/discuss.py
+++ b/Allura/allura/model/discuss.py
@@ -20,6 +20,7 @@ from __future__ import absolute_import
 import os
 import logging
 from datetime import datetime
+import typing
 
 import jinja2
 import pymongo
@@ -46,6 +47,9 @@ from .types import MarkdownCache
 from six.moves import range
 from six.moves import map
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 log = logging.getLogger(__name__)
 
 
@@ -53,6 +57,9 @@ class Discussion(Artifact, ActivityObject):
 
     class __mongometa__:
         name = str('discussion')
+
+    query: 'Query[Discussion]'
+
     type_s = 'Discussion'
 
     parent_id = FieldProperty(schema.Deprecated)
@@ -146,6 +153,9 @@ class Thread(Artifact, ActivityObject):
              ('mod_date', pymongo.DESCENDING)),
             ('discussion_id',),
         ]
+
+    query: 'Query[Thread]'
+
     type_s = 'Thread'
 
     _id = FieldProperty(str, if_missing=lambda: h.nonce(10))
@@ -466,6 +476,8 @@ class PostHistory(Snapshot):
     class __mongometa__:
         name = str('post_history')
 
+    query: 'Query[PostHistory]'
+
     artifact_id = ForeignIdProperty('Post')
 
     @classmethod
@@ -506,6 +518,9 @@ class Post(Message, VersionedArtifact, ActivityObject, ReactableArtifact):
             ('discussion_id', 'status', 'timestamp'),
             'thread_id'
         ]
+
+    query: 'Query[Post]'
+
     type_s = 'Post'
 
     thread_id = ForeignIdProperty(Thread)
@@ -827,6 +842,8 @@ class DiscussionAttachment(BaseAttachment):
         polymorphic_identity = str('DiscussionAttachment')
         indexes = ['filename', 'discussion_id', 'thread_id', 'post_id']
 
+    query: 'Query[DiscussionAttachment]'
+
     discussion_id = FieldProperty(schema.ObjectId)
     thread_id = FieldProperty(str)
     post_id = FieldProperty(str)
diff --git a/Allura/allura/model/filesystem.py b/Allura/allura/model/filesystem.py
index dac761d..32d3296 100644
--- a/Allura/allura/model/filesystem.py
+++ b/Allura/allura/model/filesystem.py
@@ -21,6 +21,7 @@ import os
 import re
 from io import BytesIO
 import logging
+import typing
 
 import PIL
 from gridfs import GridFS
@@ -33,6 +34,10 @@ from .session import project_orm_session
 from allura.lib import utils
 from io import open
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 log = logging.getLogger(__name__)
 
 SUPPORTED_BY_PIL = set([
@@ -52,6 +57,8 @@ class File(MappedClass):
         name = str('fs')
         indexes = ['filename']
 
+    query: 'Query[File]'
+
     _id = FieldProperty(schema.ObjectId)
     file_id = FieldProperty(schema.ObjectId)
     filename = FieldProperty(str, if_missing='unknown')
diff --git a/Allura/allura/model/monq_model.py b/Allura/allura/model/monq_model.py
index 2af31ed..b14095f 100644
--- a/Allura/allura/model/monq_model.py
+++ b/Allura/allura/model/monq_model.py
@@ -22,6 +22,7 @@ import time
 import traceback
 import logging
 from datetime import datetime, timedelta
+import typing
 
 import pymongo
 from tg import tmpl_context as c, app_globals as g
@@ -37,6 +38,10 @@ from ming.orm.declarative import MappedClass
 from allura.lib.helpers import log_output, null_contextmanager
 from .session import task_orm_session
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 log = logging.getLogger(__name__)
 
 
@@ -81,6 +86,8 @@ class MonQTask(MappedClass):
             ],
         ]
 
+    query: 'Query[MonQTask]'
+
     _id = FieldProperty(S.ObjectId)
     state = FieldProperty(S.OneOf(*states))
     priority = FieldProperty(int)
diff --git a/Allura/allura/model/multifactor.py b/Allura/allura/model/multifactor.py
index 0d232b9..a0a87cd 100644
--- a/Allura/allura/model/multifactor.py
+++ b/Allura/allura/model/multifactor.py
@@ -18,6 +18,7 @@
 from __future__ import unicode_literals
 from __future__ import absolute_import
 import logging
+import typing
 
 from ming import schema as S
 from ming.odm import FieldProperty
@@ -25,6 +26,9 @@ from ming.odm.declarative import MappedClass
 
 from .session import main_orm_session
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 log = logging.getLogger(__name__)
 
 
@@ -38,6 +42,8 @@ class TotpKey(MappedClass):
         name = str('multifactor_totp')
         unique_indexes = ['user_id']
 
+    query: 'Query[TotpKey]'
+
     _id = FieldProperty(S.ObjectId)
     user_id = FieldProperty(S.ObjectId, required=True)
     key = FieldProperty(S.Binary, required=True)  # S.Binary ok?  ming 0.5.x make_safe doesn't know about bytes/Binary
@@ -53,6 +59,8 @@ class RecoveryCode(MappedClass):
         name = str('multifactor_recovery_code')
         indexes = ['user_id']
 
+    query: 'Query[RecoveryCode]'
+
     _id = FieldProperty(S.ObjectId)
     user_id = FieldProperty(S.ObjectId, required=True)
     code = FieldProperty(str, required=True)
diff --git a/Allura/allura/model/neighborhood.py b/Allura/allura/model/neighborhood.py
index 24fbe81..9b57ee3 100644
--- a/Allura/allura/model/neighborhood.py
+++ b/Allura/allura/model/neighborhood.py
@@ -21,6 +21,7 @@ import re
 import json
 import logging
 from collections import OrderedDict
+import typing
 
 from ming import schema as S
 from ming.orm import FieldProperty, RelationProperty
@@ -36,6 +37,10 @@ from .session import main_orm_session
 from .filesystem import File
 from .types import MarkdownCache
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 log = logging.getLogger(__name__)
 
 
@@ -45,6 +50,8 @@ class NeighborhoodFile(File):
         session = main_orm_session
         indexes = ['neighborhood_id']
 
+    query: 'Query[NeighborhoodFile]'
+
     neighborhood_id = FieldProperty(S.ObjectId)
 
 
@@ -68,6 +75,8 @@ class Neighborhood(MappedClass):
         name = str('neighborhood')
         unique_indexes = ['url_prefix']
 
+    query: 'Query[Neighborhood]'
+
     _id = FieldProperty(S.ObjectId)
     name = FieldProperty(str)
     # e.g. http://adobe.openforge.com/ or projects/
diff --git a/Allura/allura/model/notification.py b/Allura/allura/model/notification.py
index bff46ec..558f426 100644
--- a/Allura/allura/model/notification.py
+++ b/Allura/allura/model/notification.py
@@ -40,6 +40,7 @@ import logging
 from bson import ObjectId
 from datetime import datetime, timedelta
 from collections import defaultdict
+import typing
 
 from tg import tmpl_context as c, app_globals as g
 from tg import config
@@ -61,6 +62,9 @@ from .auth import User, AlluraUserProperty
 import six
 from six.moves import filter
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 
 log = logging.getLogger(__name__)
 
@@ -68,7 +72,6 @@ MAILBOX_QUIESCENT = None  # Re-enable with [#1384]: timedelta(minutes=10)
 
 
 class Notification(MappedClass):
-
     '''
     Temporarily store notifications that will be emailed or displayed as a web flash.
     This does not contain any recipient information.
@@ -79,6 +82,8 @@ class Notification(MappedClass):
         name = str('notification')
         indexes = ['project_id']
 
+    query: 'Query[Notification]'
+
     _id = FieldProperty(str, if_missing=h.gen_message_id)
 
     # Classify notifications
@@ -382,7 +387,6 @@ class Notification(MappedClass):
 
 
 class Mailbox(MappedClass):
-
     '''
     Holds a queue of notifications for an artifact, or a user (webflash messages)
     for a subscriber.
@@ -405,6 +409,8 @@ class Mailbox(MappedClass):
             ('project_id', 'app_config_id', 'artifact_index_id', 'topic'),
         ]
 
+    query: 'Query[Mailbox]'
+
     _id = FieldProperty(S.ObjectId)
     user_id = AlluraUserProperty(if_missing=lambda: c.user._id)
     project_id = ForeignIdProperty('Project', if_missing=lambda: c.project._id)
@@ -709,7 +715,6 @@ class MailFooter(object):
 
 
 class SiteNotification(MappedClass):
-
     """
     Storage for site-wide notification.
     """
@@ -721,6 +726,8 @@ class SiteNotification(MappedClass):
             ('active', '_id'),
         ]
 
+    query: 'Query[SiteNotification]'
+
     _id = FieldProperty(S.ObjectId)
     content = FieldProperty(str, if_missing='')
     active = FieldProperty(bool, if_missing=True)
diff --git a/Allura/allura/model/oauth.py b/Allura/allura/model/oauth.py
index 7dc5da9..c661bb0 100644
--- a/Allura/allura/model/oauth.py
+++ b/Allura/allura/model/oauth.py
@@ -18,6 +18,7 @@
 from __future__ import unicode_literals
 from __future__ import absolute_import
 import logging
+import typing
 
 import oauth2 as oauth
 from tg import tmpl_context as c, app_globals as g
@@ -34,6 +35,10 @@ from .session import main_orm_session
 from .types import MarkdownCache
 from .auth import AlluraUserProperty
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 log = logging.getLogger(__name__)
 
 
@@ -46,6 +51,8 @@ class OAuthToken(MappedClass):
         polymorphic_on = 'type'
         polymorphic_identity = None
 
+    query: 'Query[OAuthToken]'
+
     _id = FieldProperty(S.ObjectId)
     type = FieldProperty(str)
     api_key = FieldProperty(str, if_missing=lambda: h.nonce(20))
@@ -65,6 +72,8 @@ class OAuthConsumerToken(OAuthToken):
         name = str('oauth_consumer_token')
         unique_indexes = [('name', 'user_id')]
 
+    query: 'Query[OAuthConsumerToken]'
+
     type = FieldProperty(str, if_missing='consumer')
     user_id = AlluraUserProperty(if_missing=lambda: c.user._id)
     name = FieldProperty(str)
@@ -108,6 +117,8 @@ class OAuthRequestToken(OAuthToken):
     class __mongometa__:
         polymorphic_identity = str('request')
 
+    query: 'Query[OAuthRequestToken]'
+
     type = FieldProperty(str, if_missing='request')
     consumer_token_id = ForeignIdProperty('OAuthConsumerToken')
     user_id = AlluraUserProperty(if_missing=lambda: c.user._id)
@@ -122,6 +133,8 @@ class OAuthAccessToken(OAuthToken):
     class __mongometa__:
         polymorphic_identity = str('access')
 
+    query: 'Query[OAuthAccessToken]'
+
     type = FieldProperty(str, if_missing='access')
     consumer_token_id = ForeignIdProperty('OAuthConsumerToken')
     request_token_id = ForeignIdProperty('OAuthToken')
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index 49af70a..de1ff2f 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -22,7 +22,7 @@ import logging
 from calendar import timegm
 from collections import Counter, OrderedDict
 from hashlib import sha256
-
+import typing
 from datetime import datetime
 from copy import deepcopy
 import six.moves.urllib.request
@@ -69,6 +69,10 @@ from .filesystem import File
 import six
 from six.moves import map
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 log = logging.getLogger(__name__)
 
 # max sitemap entries per tool type
@@ -83,6 +87,8 @@ class ProjectFile(File):
         session = main_orm_session
         indexes = [('project_id', 'category')]
 
+    query: 'Query[ProjectFile]'
+
     project_id = FieldProperty(S.ObjectId)
     category = FieldProperty(str)
     caption = FieldProperty(str)
@@ -95,6 +101,8 @@ class ProjectCategory(MappedClass):
         session = main_orm_session
         name = str('project_category')
 
+    query: 'Query[ProjectCategory]'
+
     _id = FieldProperty(S.ObjectId)
     parent_id = FieldProperty(S.ObjectId, if_missing=None)
     name = FieldProperty(str)
@@ -130,6 +138,8 @@ class TroveCategory(MappedClass):
         extensions = [TroveCategoryMapperExtension]
         indexes = ['trove_cat_id', 'trove_parent_id', 'shortname', 'fullpath']
 
+    query: 'Query[TroveCategory]'
+
     _id = FieldProperty(S.ObjectId)
     trove_cat_id = FieldProperty(int, if_missing=None)
     trove_parent_id = FieldProperty(int, if_missing=None)
@@ -210,6 +220,8 @@ class Project(SearchIndexable, MappedClass, ActivityNode, ActivityObject):
             ('neighborhood_id', 'is_nbhd_project', 'deleted')]
         unique_indexes = [('neighborhood_id', 'shortname')]
 
+    query: 'Query[Project]'
+
     type_s = 'Project'
 
     # Project schema
@@ -1361,6 +1373,8 @@ class AppConfig(MappedClass, ActivityObject):
             'options.import_id',
             ('options.mount_point', 'project_id')]
 
+    query: 'Query[AppConfig]'
+
     # AppConfig schema
     _id = FieldProperty(S.ObjectId)
     project_id = ForeignIdProperty(Project)
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 0c87dd8..2712d5b 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -34,6 +34,7 @@ from threading import Thread
 from six.moves.queue import Queue
 from itertools import chain, islice
 from difflib import SequenceMatcher
+import typing
 
 import tg
 from paste.deploy.converters import asint, asbool
@@ -64,6 +65,9 @@ from .session import repository_orm_session
 from io import open
 from six.moves import range
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 
 log = logging.getLogger(__name__)
 config = utils.ConfigProxy(
@@ -351,6 +355,9 @@ class Repository(Artifact, ActivityObject):
     class __mongometa__:
         name = str('generic-repository')
         indexes = ['upstream_repo.name']
+
+    query: 'Query[Repository]'
+
     _impl = None
     repo_id = 'repo'
     type_s = 'Repository'
@@ -801,6 +808,9 @@ class MergeRequest(VersionedArtifact, ActivityObject):
         name = str('merge-request')
         indexes = ['commit_id', 'creator_id']
         unique_indexes = [('app_config_id', 'request_number')]
+
+    query: 'Query[MergeRequest]'
+
     type_s = 'MergeRequest'
 
     request_number = FieldProperty(int)
diff --git a/Allura/allura/model/stats.py b/Allura/allura/model/stats.py
index cb53f60..dffe19f 100644
--- a/Allura/allura/model/stats.py
+++ b/Allura/allura/model/stats.py
@@ -20,6 +20,7 @@ from __future__ import absolute_import
 
 import six
 from datetime import datetime
+import typing
 from tg import config
 from paste.deploy.converters import asbool
 
@@ -35,6 +36,9 @@ from allura.lib import helpers as h
 from six.moves import range
 from functools import reduce
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 
 class Stats(MappedClass):
 
@@ -43,6 +47,8 @@ class Stats(MappedClass):
         session = main_orm_session
         unique_indexes = ['_id']
 
+    query: 'Query[Stats]'
+
     _id = FieldProperty(S.ObjectId)
 
     visible = FieldProperty(bool, if_missing=True)
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
index 7eae3ed..d399a25 100644
--- a/Allura/allura/model/webhook.py
+++ b/Allura/allura/model/webhook.py
@@ -19,6 +19,7 @@ from __future__ import unicode_literals
 from __future__ import absolute_import
 import datetime as dt
 import json
+import typing
 
 from ming.odm import FieldProperty, session
 from paste.deploy.converters import asint
@@ -28,12 +29,17 @@ from allura.model import Artifact
 from allura.lib import helpers as h
 import six
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 
 class Webhook(Artifact):
     class __mongometa__:
         name = str('webhook')
         unique_indexes = [('app_config_id', 'type', 'hook_url')]
 
+    query: 'Query[Webhook]'
+
     type = FieldProperty(str)
     hook_url = FieldProperty(str)
     secret = FieldProperty(str)
diff --git a/ForgeBlog/forgeblog/model/blog.py b/ForgeBlog/forgeblog/model/blog.py
index d2b56bb..b0e9630 100644
--- a/ForgeBlog/forgeblog/model/blog.py
+++ b/ForgeBlog/forgeblog/model/blog.py
@@ -22,6 +22,7 @@ import functools
 import re
 from datetime import datetime
 from random import randint
+import typing
 
 from tg import tmpl_context as c, app_globals as g
 from tg import config as tg_config
@@ -38,6 +39,10 @@ from allura.lib import helpers as h
 from allura.lib import utils
 import six
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 config = utils.ConfigProxy(
     common_suffix='forgemail.domain')
 
@@ -49,6 +54,8 @@ class Globals(MappedClass):
         session = M.project_orm_session
         indexes = ['app_config_id']
 
+    query: 'Query[Globals]'
+
     type_s = 'BlogGlobals'
     _id = FieldProperty(schema.ObjectId)
     app_config_id = ForeignIdProperty(
@@ -60,6 +67,9 @@ class BlogPostSnapshot(M.Snapshot):
 
     class __mongometa__:
         name = str('blog_post_snapshot')
+
+    query: 'Query[BlogPostSnapshot]'
+
     type_s = 'Blog Post Snapshot'
 
     def original(self):
@@ -114,6 +124,8 @@ class BlogPost(M.VersionedArtifact, ActivityObject):
             ('neighborhood_id', 'state', 'timestamp'),
         ]
 
+    query: 'Query[BlogPost]'
+
     type_s = 'Blog Post'
 
     title = FieldProperty(str, if_missing='Untitled')
@@ -337,6 +349,9 @@ class BlogAttachment(M.BaseAttachment):
 
     class __mongometa__:
         polymorphic_identity = str('BlogAttachment')
+
+    query: 'Query[BlogAttachment]'
+
     attachment_type = FieldProperty(str, if_missing='BlogAttachment')
 
 
diff --git a/ForgeChat/forgechat/model/chat.py b/ForgeChat/forgechat/model/chat.py
index a2079fe..09b90e8 100644
--- a/ForgeChat/forgechat/model/chat.py
+++ b/ForgeChat/forgechat/model/chat.py
@@ -18,6 +18,7 @@
 from __future__ import unicode_literals
 from __future__ import absolute_import
 from datetime import datetime
+import typing
 
 from ming import schema as S
 from ming.orm import FieldProperty, Mapper
@@ -26,6 +27,9 @@ from ming.orm.declarative import MappedClass
 from allura import model as M
 from allura.model.types import MarkdownCache
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 
 class ChatChannel(MappedClass):
 
@@ -35,6 +39,8 @@ class ChatChannel(MappedClass):
         indexes = ['project_id']
         unique_indexes = ['channel']
 
+    query: 'Query[ChatChannel]'
+
     _id = FieldProperty(S.ObjectId)
     project_id = FieldProperty(S.ObjectId)
     app_config_id = FieldProperty(S.ObjectId)
@@ -46,6 +52,9 @@ class ChatMessage(M.Artifact):
     class __mongometa__:
         name = str('chat_message')
         indexes = ['timestamp']
+
+    query: 'Query[ChatMessage]'
+
     type_s = 'Chat Message'
 
     timestamp = FieldProperty(datetime, if_missing=datetime.utcnow)
diff --git a/ForgeDiscussion/forgediscussion/model/forum.py b/ForgeDiscussion/forgediscussion/model/forum.py
index 30440f2..a80d1c8 100644
--- a/ForgeDiscussion/forgediscussion/model/forum.py
+++ b/ForgeDiscussion/forgediscussion/model/forum.py
@@ -20,6 +20,7 @@ from __future__ import absolute_import
 import re
 import logging
 from itertools import chain
+import typing
 
 import pymongo
 from tg import tmpl_context as c
@@ -34,6 +35,10 @@ from allura.model.notification import MailFooter
 from allura.lib import utils
 from allura.lib import helpers as h
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 config = utils.ConfigProxy(
     common_suffix='forgemail.domain')
 
@@ -44,6 +49,9 @@ class Forum(M.Discussion):
 
     class __mongometa__:
         name = str('forum')
+
+    query: 'Query[Forum]'
+
     type_s = 'Discussion'
 
     parent_id = FieldProperty(schema.ObjectId, if_missing=None)
@@ -145,6 +153,9 @@ class ForumThread(M.Thread):
             'discussion_id',
             'import_id',  # may be used by external legacy systems
         ]
+
+    query: 'Query[ForumThread]'
+
     type_s = 'Thread'
 
     discussion_id = ForeignIdProperty(Forum)
@@ -212,6 +223,8 @@ class ForumPostHistory(M.PostHistory):
     class __mongometa__:
         name = str('post_history')
 
+    query: 'Query[ForumPostHistory]'
+
     artifact_id = ForeignIdProperty('ForumPost')
 
 
@@ -228,6 +241,9 @@ class ForumPost(M.Post):
                 ('timestamp', pymongo.DESCENDING),
             ),
         ]
+
+    query: 'Query[ForumPost]'
+
     type_s = 'Post'
 
     discussion_id = ForeignIdProperty(Forum)
@@ -255,6 +271,9 @@ class ForumAttachment(M.DiscussionAttachment):
 
     class __mongometa__:
         polymorphic_identity = str('ForumAttachment')
+
+    query: 'Query[ForumAttachment]'
+
     attachment_type = FieldProperty(str, if_missing='ForumAttachment')
 
 
diff --git a/ForgeFeedback/forgefeedback/model/feedback.py b/ForgeFeedback/forgefeedback/model/feedback.py
index 51f53fa..2e30ca8 100644
--- a/ForgeFeedback/forgefeedback/model/feedback.py
+++ b/ForgeFeedback/forgefeedback/model/feedback.py
@@ -21,9 +21,9 @@ import logging
 import six.moves.urllib.request
 import six.moves.urllib.parse
 import six.moves.urllib.error
-# Non-stdlib imports
-
+import typing
 from datetime import datetime
+
 from bson import ObjectId
 from tg import tmpl_context as c
 from ming import schema
@@ -38,6 +38,9 @@ from allura.model.project import ProjectRole
 from allura.model.timeline import ActivityObject
 from allura.lib import helpers as h
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 log = logging.getLogger(__name__)
 
 
@@ -49,7 +52,10 @@ class Feedback(VersionedArtifact, ActivityObject):
             ('project_id', 'reported_by_id'),
         ]
 
+    query: 'Query[Feedback]'
+
     type_s = 'Feedback'
+
     _id = FieldProperty(schema.ObjectId)
     created_date = FieldProperty(datetime, if_missing=datetime.utcnow)
     rating = FieldProperty(str, if_missing='')
diff --git a/ForgeFiles/forgefiles/model/files.py b/ForgeFiles/forgefiles/model/files.py
index ecfef2b..c5af9de 100755
--- a/ForgeFiles/forgefiles/model/files.py
+++ b/ForgeFiles/forgefiles/model/files.py
@@ -24,6 +24,7 @@ from datetime import datetime
 
 from six.moves.urllib.parse import quote
 import re
+import typing
 
 from ming import schema as S
 from ming.orm import Mapper
@@ -38,6 +39,10 @@ from allura.model.filesystem import File
 from allura.model.timeline import ActivityObject
 from allura.lib import helpers as h
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 README_RE = re.compile(r'^README(\.[^.]*)?$', re.IGNORECASE)
 
 
@@ -49,7 +54,10 @@ class Upload(VersionedArtifact, ActivityObject):
         name = 'upload'
         session = project_orm_session
 
+    query: 'Query[Upload]'
+
     type_s = 'Upload'
+
     _id = FieldProperty(S.ObjectId)
     filename = FieldProperty(str)
     filetype = FieldProperty(str)
@@ -79,7 +87,10 @@ class UploadFolder(VersionedArtifact, ActivityObject):
         name = 'upload_folder'
         session = project_orm_session
 
+    query: 'Query[UploadFolder]'
+
     type_s = 'UploadFolder'
+
     _id = FieldProperty(S.ObjectId)
     folder_name = FieldProperty(str)
     project_id = ForeignIdProperty('Project', if_missing=lambda: c.project._id)
@@ -153,6 +164,8 @@ class UploadFiles(File):
             if c.project and not skip_last_updated:
                 c.project.last_updated = datetime.utcnow()
 
+    query: 'Query[UploadFiles]'
+
     artifact_id = FieldProperty(S.ObjectId)
     app_config_id = FieldProperty(S.ObjectId)
     type = FieldProperty(str)
diff --git a/ForgeGit/forgegit/model/git_repo.py b/ForgeGit/forgegit/model/git_repo.py
index 7c5827b..07662ca 100644
--- a/ForgeGit/forgegit/model/git_repo.py
+++ b/ForgeGit/forgegit/model/git_repo.py
@@ -25,6 +25,7 @@ import tempfile
 from datetime import datetime
 from contextlib import contextmanager
 from time import time
+import typing
 
 import tg
 import git
@@ -44,6 +45,10 @@ from allura import model as M
 from io import open
 from six.moves import zip
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 log = logging.getLogger(__name__)
 
 gitdb.util.mman = gitdb.util.mman.__class__(
@@ -70,6 +75,8 @@ class Repository(M.Repository):
     class __mongometa__:
         name = str('git-repository')
 
+    query: 'Query[Repository]'
+
     @LazyProperty
     def _impl(self):
         return GitImplementation(self)
diff --git a/ForgeSVN/forgesvn/model/svn.py b/ForgeSVN/forgesvn/model/svn.py
index 5b40b5d..1fc18bb 100644
--- a/ForgeSVN/forgesvn/model/svn.py
+++ b/ForgeSVN/forgesvn/model/svn.py
@@ -31,6 +31,7 @@ from io import BytesIO
 from datetime import datetime
 import tempfile
 from shutil import rmtree
+import typing
 
 import six
 import tg
@@ -52,6 +53,10 @@ from io import open
 from six.moves import range
 from six.moves import map
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 log = logging.getLogger(__name__)
 
 
@@ -62,6 +67,9 @@ class Repository(M.Repository):
 
     class __mongometa__:
         name = str('svn-repository')
+
+    query: 'Query[Repository]'
+
     branches = FieldProperty([dict(name=str, object_id=str)])
     _refresh_precompute = False
 
diff --git a/ForgeShortUrl/forgeshorturl/model/shorturl.py b/ForgeShortUrl/forgeshorturl/model/shorturl.py
index 90aa038..0f21bc0 100644
--- a/ForgeShortUrl/forgeshorturl/model/shorturl.py
+++ b/ForgeShortUrl/forgeshorturl/model/shorturl.py
@@ -17,6 +17,7 @@
 
 from __future__ import unicode_literals
 from __future__ import absolute_import
+import typing
 import pymongo
 from tg import config
 from tg import tmpl_context as c
@@ -25,6 +26,9 @@ from datetime import datetime
 from allura.model.auth import User
 from allura import model as M
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 
 class ShortUrl(M.Artifact):
 
@@ -32,6 +36,8 @@ class ShortUrl(M.Artifact):
         name = str('short_urls')
         unique_indexes = [('short_name', 'app_config_id')]
 
+    query: 'Query[ShortUrl]'
+
     type_s = 'ShortUrl'
     full_url = FieldProperty(str)
     short_name = FieldProperty(str)
diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index 6242ba8..404223f 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -25,6 +25,7 @@ import json
 import difflib
 from datetime import datetime, timedelta
 import os
+import typing
 
 from bson import ObjectId
 import six
@@ -80,6 +81,9 @@ from allura.lib.security import require_access
 from allura.tasks import mail_tasks
 from forgetracker import search as tsearch
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 
 log = logging.getLogger(__name__)
 
@@ -103,7 +107,10 @@ class Globals(MappedClass):
         session = project_orm_session
         indexes = ['app_config_id']
 
+    query: 'Query[Globals]'
+
     type_s = 'Globals'
+
     _id = FieldProperty(schema.ObjectId)
     app_config_id = ForeignIdProperty(
         AppConfig, if_missing=lambda: c.app.config._id)
@@ -549,6 +556,8 @@ class TicketHistory(Snapshot):
     class __mongometa__:
         name = str('ticket_history')
 
+    query: 'Query[TicketHistory]'
+
     def original(self):
         return Ticket.query.get(_id=self.artifact_id)
 
@@ -595,7 +604,10 @@ class Bin(Artifact, ActivityObject):
     class __mongometa__:
         name = str('bin')
 
+    query: 'Query[Bin]'
+
     type_s = 'Bin'
+
     _id = FieldProperty(schema.ObjectId)
     summary = FieldProperty(str, required=True, allow_none=False)
     terms = FieldProperty(str, if_missing='')
@@ -646,7 +658,10 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
             ('app_config_id', 'ticket_num'),
         ]
 
+    query: 'Query[Ticket]'
+
     type_s = 'Ticket'
+
     _id = FieldProperty(schema.ObjectId)
     created_date = FieldProperty(datetime, if_missing=datetime.utcnow)
 
@@ -1377,6 +1392,9 @@ class TicketAttachment(BaseAttachment):
 
     class __mongometa__:
         polymorphic_identity = str('TicketAttachment')
+
+    query: 'Query[TicketAttachment]'
+
     attachment_type = FieldProperty(str, if_missing='TicketAttachment')
 
 
@@ -1389,6 +1407,8 @@ class MovedTicket(MovedArtifact):
             ('app_config_id', 'ticket_num'),
         ]
 
+    query: 'Query[MovedTicket]'
+
     ticket_num = FieldProperty(int, required=True, allow_none=False)
 
     def url(self):
diff --git a/ForgeUserStats/forgeuserstats/model/stats.py b/ForgeUserStats/forgeuserstats/model/stats.py
index 7c0f050..c428c78 100644
--- a/ForgeUserStats/forgeuserstats/model/stats.py
+++ b/ForgeUserStats/forgeuserstats/model/stats.py
@@ -20,6 +20,7 @@ from __future__ import absolute_import
 from ming.orm import FieldProperty
 from ming import schema as S
 from datetime import datetime, timedelta
+import typing
 from ming.orm import Mapper
 from tg import request
 
@@ -27,6 +28,9 @@ from allura.lib import plugin
 from allura.model.session import main_orm_session
 from allura.model import Stats
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
 
 class UserStats(Stats):
 
@@ -35,6 +39,8 @@ class UserStats(Stats):
         session = main_orm_session
         unique_indexes = ['_id', 'user_id']
 
+    query: 'Query[UserStats]'
+
     tot_logins_count = FieldProperty(int, if_missing=0)
     last_login = FieldProperty(datetime)
     lastmonthlogins = FieldProperty([datetime])
diff --git a/ForgeWiki/forgewiki/model/wiki.py b/ForgeWiki/forgewiki/model/wiki.py
index 48be17f..5d14871 100644
--- a/ForgeWiki/forgewiki/model/wiki.py
+++ b/ForgeWiki/forgewiki/model/wiki.py
@@ -20,6 +20,7 @@ from __future__ import absolute_import
 from datetime import datetime
 import difflib
 import os
+import typing
 
 # g is a namespace for globally accessable app helpers
 from tg import app_globals as g
@@ -49,6 +50,10 @@ from allura.model.types import MarkdownCache
 from allura.lib import helpers as h
 from allura.lib import utils
 
+if typing.TYPE_CHECKING:
+    from ming.odm.mapper import Query
+
+
 config = utils.ConfigProxy(
     common_suffix='forgemail.domain')
 
@@ -60,7 +65,10 @@ class Globals(MappedClass):
         session = project_orm_session
         indexes = ['app_config_id']
 
+    query: 'Query[Globals]'
+
     type_s = 'WikiGlobals'
+
     _id = FieldProperty(schema.ObjectId)
     app_config_id = ForeignIdProperty(
         'AppConfig', if_missing=lambda: context.app.config._id)
@@ -72,6 +80,8 @@ class PageHistory(Snapshot):
     class __mongometa__:
         name = str('page_history')
 
+    query: 'Query[PageHistory]'
+
     def original(self):
         return Page.query.get(_id=self.artifact_id)
 
@@ -110,6 +120,8 @@ class Page(VersionedArtifact, ActivityObject):
         history_class = PageHistory
         unique_indexes = [('app_config_id', 'title')]
 
+    query: 'Query[Page]'
+
     title = FieldProperty(str)
     text = FieldProperty(schema.String, if_missing='')
     text_cache = FieldProperty(MarkdownCache)
@@ -283,6 +295,9 @@ class WikiAttachment(BaseAttachment):
 
     class __mongometa__:
         polymorphic_identity = str('WikiAttachment')
+
+    query: 'Query[WikiAttachment]'
+
     attachment_type = FieldProperty(str, if_missing='WikiAttachment')
 
 Mapper.compile_all()