Add annotations to avatar.py, db.py, logging_util.py, unminify.py.

Also, fixed a a small type annotation in users.py because email must
be a string because emails don't support UTF-8 at this time (according
a comment in gravatar_hash in avatar.py).
This commit is contained in:
Max
2016-06-04 19:20:00 -07:00
committed by Tim Abbott
parent 2855c285b4
commit 86fb6467e7
5 changed files with 32 additions and 3 deletions

View File

@@ -3,8 +3,13 @@ from django.conf import settings
import hashlib
from zerver.lib.utils import make_safe_digest
if False:
from zerver.models import UserProfile
from six import text_type
def gravatar_hash(email):
# type: (str) -> str
"""Compute the Gravatar hash for an email address."""
# Non-ASCII characters aren't permitted by the currently active e-mail
# RFCs. However, the IETF has published https://tools.ietf.org/html/rfc4952,
@@ -14,6 +19,7 @@ def gravatar_hash(email):
return make_safe_digest(email.lower(), hashlib.md5)
def user_avatar_hash(email):
# type: (str) -> str
# Salting the user_key may be overkill, but it prevents us from
# basically mimicking Gravatar's hashing scheme, which could lead
# to some abuse scenarios like folks using us as a free Gravatar
@@ -22,12 +28,14 @@ def user_avatar_hash(email):
return make_safe_digest(user_key, hashlib.sha1)
def avatar_url(user_profile):
# type: (UserProfile) -> text_type
return get_avatar_url(
user_profile.avatar_source,
user_profile.email
)
def get_avatar_url(avatar_source, email):
# type: (str, str) -> str
if avatar_source == 'U':
hash_key = user_avatar_hash(email)
if settings.LOCAL_UPLOADS_DIR is not None:

View File

@@ -3,9 +3,16 @@ from __future__ import absolute_import
import time
from psycopg2.extensions import cursor, connection
from typing import Callable, Optional, Iterable, Any, Dict, Union, TypeVar, \
Mapping, Sequence
from six import text_type
CursorObj = TypeVar('CursorObj', bound=cursor)
# Similar to the tracking done in Django's CursorDebugWrapper, but done at the
# psycopg2 cursor level so it works with SQLAlchemy.
def wrapper_execute(self, action, sql, params=()):
# type: (CursorObj, Callable[[text_type, Optional[Union[Iterable[Any], Dict[text_type, Any]]]], CursorObj], text_type, Union[Iterable[Any], Dict[text_type, Any]]) -> CursorObj
start = time.time()
try:
return action(sql, params)
@@ -20,25 +27,30 @@ class TimeTrackingCursor(cursor):
"""A psycopg2 cursor class that tracks the time spent executing queries."""
def execute(self, query, vars=None):
# type: (text_type, Optional[Union[Iterable[Any], Dict[text_type, Any]]]) -> TimeTrackingCursor
return wrapper_execute(self, super(TimeTrackingCursor, self).execute, query, vars)
def executemany(self, query, vars):
# type: (text_type, Iterable[Any]) -> TimeTrackingCursor
return wrapper_execute(self, super(TimeTrackingCursor, self).executemany, query, vars)
class TimeTrackingConnection(connection):
"""A psycopg2 connection class that uses TimeTrackingCursors."""
def __init__(self, *args, **kwargs):
# type: (Sequence[Any], Mapping[text_type, Any]) -> None
self.queries = [] # type: List[Dict[str, str]]
super(TimeTrackingConnection, self).__init__(*args, **kwargs)
def cursor(self, name=None):
# type: (Optional[text_type]) -> TimeTrackingCursor
if name is None:
return super(TimeTrackingConnection, self).cursor(cursor_factory=TimeTrackingCursor)
else:
return super(TimeTrackingConnection, self).cursor(name, cursor_factory=TimeTrackingCursor)
def reset_queries():
# type: () -> None
from django.db import connections
for conn in connections.all():
if conn.connection is not None:

View File

@@ -9,6 +9,7 @@ class _RateLimitFilter(object):
last_error = datetime.min
def filter(self, record):
# type: (logging.LogRecord) -> bool
from django.conf import settings
from django.core.cache import cache
@@ -44,9 +45,11 @@ class EmailLimiter(_RateLimitFilter):
class ReturnTrue(logging.Filter):
def filter(self, record):
# type: (logging.LogRecord) -> bool
return True
class RequireReallyDeployed(logging.Filter):
def filter(self, record):
# type: (logging.LogRecord) -> bool
from django.conf import settings
return settings.PRODUCTION

View File

@@ -4,16 +4,21 @@ import re
import os.path
import sourcemap
from six.moves import map
from six import text_type
from typing import Dict
class SourceMap(object):
'''Map (line, column) pairs from generated to source file.'''
def __init__(self, sourcemap_dir):
# type: (text_type) -> None
self._dir = sourcemap_dir
self._indices = {} # type: Dict[str, sourcemap.SourceMapDecoder]
self._indices = {} # type: Dict[text_type, sourcemap.SourceMapDecoder]
def _index_for(self, minified_src):
# type: (text_type) -> sourcemap.SourceMapDecoder
'''Return the source map index for minified_src, loading it if not
already loaded.'''
if minified_src not in self._indices:
@@ -23,7 +28,8 @@ class SourceMap(object):
return self._indices[minified_src]
def annotate_stacktrace(self, stacktrace):
out = ''
# type: (text_type) -> text_type
out = '' # type: text_type
for ln in stacktrace.splitlines():
out += ln + '\n'
match = re.search(r'/static/min/(.+)(\.[0-9a-f]+)\.js:(\d+):(\d+)', ln)

View File

@@ -85,7 +85,7 @@ def update_user_backend(request, user_profile, email,
return json_success({})
def avatar(request, email):
# type: (HttpRequest, text_type) -> HttpResponse
# type: (HttpRequest, str) -> HttpResponse
try:
user_profile = get_user_profile_by_email(email)
avatar_source = user_profile.avatar_source