mirror of https://github.com/zulip/zulip.git
python: Convert more variable type annotations to Python 3.6 style.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
708c6f4f11
commit
8cdf2801f7
|
@ -1024,7 +1024,7 @@ def ad_hoc_queries() -> List[Dict[str, str]]:
|
||||||
@require_server_admin
|
@require_server_admin
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def get_activity(request: HttpRequest) -> HttpResponse:
|
def get_activity(request: HttpRequest) -> HttpResponse:
|
||||||
duration_content, realm_minutes = user_activity_intervals() # type: Tuple[mark_safe, Dict[str, float]]
|
duration_content, realm_minutes = user_activity_intervals()
|
||||||
counts_content: str = realm_summary_table(realm_minutes)
|
counts_content: str = realm_summary_table(realm_minutes)
|
||||||
data = [
|
data = [
|
||||||
('Counts', counts_content),
|
('Counts', counts_content),
|
||||||
|
|
|
@ -102,9 +102,7 @@ automatically in Zulip's automated test suite. The code there will
|
||||||
look something like this:
|
look something like this:
|
||||||
|
|
||||||
``` python
|
``` python
|
||||||
def render_message(client):
|
def render_message(client: Client) -> None:
|
||||||
# type: (Client) -> None
|
|
||||||
|
|
||||||
# {code_example|start}
|
# {code_example|start}
|
||||||
# Render a message
|
# Render a message
|
||||||
request = {
|
request = {
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
[mypy](http://mypy-lang.org/) is a compile-time static type checker
|
[mypy](http://mypy-lang.org/) is a compile-time static type checker
|
||||||
for Python, allowing optional, gradual typing of Python code. Zulip
|
for Python, allowing optional, gradual typing of Python code. Zulip
|
||||||
was fully annotated with mypy's Python 2 syntax in 2016, before our
|
was fully annotated with mypy's Python 2 syntax in 2016, before our
|
||||||
migration to Python 3 in late 2017. In 2018, we migrated essentially
|
migration to Python 3 in late 2017. In 2018 and 2020, we migrated
|
||||||
the entire codebase to the nice PEP-484 (Python 3 only) syntax for
|
essentially the entire codebase to the nice PEP 484 (Python 3 only)
|
||||||
static types:
|
and PEP 526 (Python 3.6) syntax for static types:
|
||||||
|
|
||||||
```
|
```
|
||||||
user_dict = {} # type: Dict[str, UserProfile]
|
user_dict: Dict[str, UserProfile] = {}
|
||||||
|
|
||||||
def get_user(email: str, realm: Realm) -> UserProfile:
|
def get_user(email: str, realm: Realm) -> UserProfile:
|
||||||
... # Actual code of the function here
|
... # Actual code of the function here
|
||||||
|
|
|
@ -173,9 +173,9 @@ boolean field, `mandatory_topics`, to the Realm model in
|
||||||
|
|
||||||
class Realm(models.Model):
|
class Realm(models.Model):
|
||||||
# ...
|
# ...
|
||||||
emails_restricted_to_domains = models.BooleanField(default=True) # type: bool
|
emails_restricted_to_domains: bool = models.BooleanField(default=True)
|
||||||
invite_required = models.BooleanField(default=False) # type: bool
|
invite_required: bool = models.BooleanField(default=False)
|
||||||
+ mandatory_topics = models.BooleanField(default=False) # type: bool
|
+ mandatory_topics: bool = models.BooleanField(default=False)
|
||||||
```
|
```
|
||||||
|
|
||||||
The Realm model also contains an attribute, `property_types`, which
|
The Realm model also contains an attribute, `property_types`, which
|
||||||
|
@ -405,11 +405,14 @@ annotation).
|
||||||
|
|
||||||
# zerver/views/realm.py
|
# zerver/views/realm.py
|
||||||
|
|
||||||
def update_realm(request, user_profile, name=REQ(validator=check_string, default=None),
|
def update_realm(
|
||||||
# ...,
|
request: HttpRequest,
|
||||||
+ mandatory_topics=REQ(validator=check_bool, default=None),
|
user_profile: UserProfile,
|
||||||
# ...):
|
name: Optional[str] = REQ(validator=check_string, default=None),
|
||||||
+ # type: (HttpRequest, UserProfile, ..., Optional[bool], ...
|
# ...
|
||||||
|
+ mandatory_topics: Optional[bool] = REQ(validator=check_bool, default=None),
|
||||||
|
# ...
|
||||||
|
):
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -68,8 +68,7 @@ views, as an introduction to view decorators.
|
||||||
```py
|
```py
|
||||||
|
|
||||||
@require_post
|
@require_post
|
||||||
def accounts_register(request):
|
def accounts_register(request: HttpRequest) -> HttpResponse:
|
||||||
# type: (HttpRequest) -> HttpResponse
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This decorator ensures that the request was a POST--here, we're
|
This decorator ensures that the request was a POST--here, we're
|
||||||
|
@ -91,8 +90,7 @@ specific to Zulip.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
@zulip_login_required
|
@zulip_login_required
|
||||||
def home(request):
|
def home(request: HttpRequest) -> HttpResponse:
|
||||||
# type: (HttpRequest) -> HttpResponse
|
|
||||||
```
|
```
|
||||||
|
|
||||||
[login-required-link]: https://docs.djangoproject.com/en/1.8/topics/auth/default/#django.contrib.auth.decorators.login_required
|
[login-required-link]: https://docs.djangoproject.com/en/1.8/topics/auth/default/#django.contrib.auth.decorators.login_required
|
||||||
|
@ -261,29 +259,15 @@ For example, in [zerver/views/realm.py](https://github.com/zulip/zulip/blob/mast
|
||||||
```py
|
```py
|
||||||
@require_realm_admin
|
@require_realm_admin
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def update_realm(request, user_profile, name=REQ(validator=check_string, default=None), ...)):
|
def update_realm(
|
||||||
# type: (HttpRequest, UserProfile, ...) -> HttpResponse
|
request: HttpRequest, user_profile: UserProfile,
|
||||||
|
name: Optional[str]=REQ(validator=check_string, default=None),
|
||||||
|
# ...
|
||||||
|
):
|
||||||
realm = user_profile.realm
|
realm = user_profile.realm
|
||||||
data = {} # type: Dict[str, Any]
|
# ...
|
||||||
if name is not None and realm.name != name:
|
do_set_realm_property(realm, k, v)
|
||||||
do_set_realm_name(realm, name)
|
# ...
|
||||||
data['name'] = 'updated'
|
|
||||||
```
|
|
||||||
|
|
||||||
and in [zerver/lib/actions.py](https://github.com/zulip/zulip/blob/master/zerver/lib/actions.py):
|
|
||||||
|
|
||||||
```py
|
|
||||||
def do_set_realm_name(realm, name):
|
|
||||||
# type: (Realm, str) -> None
|
|
||||||
realm.name = name
|
|
||||||
realm.save(update_fields=['name'])
|
|
||||||
event = dict(
|
|
||||||
type="realm",
|
|
||||||
op="update",
|
|
||||||
property='name',
|
|
||||||
value=name,
|
|
||||||
)
|
|
||||||
send_event(realm, event, active_user_ids(realm))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`realm.save()` actually saves the changes to the realm to the
|
`realm.save()` actually saves the changes to the realm to the
|
||||||
|
|
|
@ -53,7 +53,7 @@ with open('/var/lib/nagios_state/last_postgres_backup', 'w') as f:
|
||||||
f.write(now.isoformat())
|
f.write(now.isoformat())
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|
||||||
backups = {} # type: Dict[datetime, str]
|
backups: Dict[datetime, str] = {}
|
||||||
lines = run(['env-wal-e', 'backup-list']).split("\n")
|
lines = run(['env-wal-e', 'backup-list']).split("\n")
|
||||||
for line in lines[1:]:
|
for line in lines[1:]:
|
||||||
if line:
|
if line:
|
||||||
|
|
|
@ -392,7 +392,7 @@ refactor them.
|
||||||
from zulip_bots.test_lib import StubBotTestCase
|
from zulip_bots.test_lib import StubBotTestCase
|
||||||
|
|
||||||
class TestHelpBot(StubBotTestCase):
|
class TestHelpBot(StubBotTestCase):
|
||||||
bot_name = "helloworld" # type: str
|
bot_name: str = "helloworld"
|
||||||
|
|
||||||
def test_bot(self) -> None:
|
def test_bot(self) -> None:
|
||||||
dialog = [
|
dialog = [
|
||||||
|
|
|
@ -417,26 +417,26 @@ python_rules = RuleList(
|
||||||
},
|
},
|
||||||
'description': 'Comment-style function type annotation. Use Python3 style annotations instead.',
|
'description': 'Comment-style function type annotation. Use Python3 style annotations instead.',
|
||||||
},
|
},
|
||||||
{'pattern': r' = models[.].*null=True.*\) # type: (?!Optional)',
|
{'pattern': r': *(?!Optional)[^ ].*= models[.].*null=True',
|
||||||
'include_only': {"zerver/models.py"},
|
'include_only': {"zerver/models.py"},
|
||||||
'description': 'Model variable with null=true not annotated as Optional.',
|
'description': 'Model variable with null=true not annotated as Optional.',
|
||||||
'good_lines': ['desc = models.TextField(null=True) # type: Optional[Text]',
|
'good_lines': ['desc: Optional[Text] = models.TextField(null=True)',
|
||||||
'stream = models.ForeignKey(Stream, null=True, on_delete=CASCADE) # type: Optional[Stream]',
|
'stream: Optional[Stream] = models.ForeignKey(Stream, null=True, on_delete=CASCADE)',
|
||||||
'desc = models.TextField() # type: Text',
|
'desc: Text = models.TextField()',
|
||||||
'stream = models.ForeignKey(Stream, on_delete=CASCADE) # type: Stream'],
|
'stream: Stream = models.ForeignKey(Stream, on_delete=CASCADE)'],
|
||||||
'bad_lines': ['desc = models.CharField(null=True) # type: Text',
|
'bad_lines': ['desc: Text = models.CharField(null=True)',
|
||||||
'stream = models.ForeignKey(Stream, null=True, on_delete=CASCADE) # type: Stream'],
|
'stream: Stream = models.ForeignKey(Stream, null=True, on_delete=CASCADE)'],
|
||||||
},
|
},
|
||||||
{'pattern': r' = models[.].* # type: Optional', # Optional tag
|
{'pattern': r': *Optional.*= models[.].*\)',
|
||||||
'exclude_pattern': 'null=True',
|
'exclude_pattern': 'null=True',
|
||||||
'include_only': {"zerver/models.py"},
|
'include_only': {"zerver/models.py"},
|
||||||
'description': 'Model variable annotated with Optional but variable does not have null=true.',
|
'description': 'Model variable annotated with Optional but variable does not have null=true.',
|
||||||
'good_lines': ['desc = models.TextField(null=True) # type: Optional[Text]',
|
'good_lines': ['desc: Optional[Text] = models.TextField(null=True)',
|
||||||
'stream = models.ForeignKey(Stream, null=True, on_delete=CASCADE) # type: Optional[Stream]',
|
'stream: Optional[Stream] = models.ForeignKey(Stream, null=True, on_delete=CASCADE)',
|
||||||
'desc = models.TextField() # type: Text',
|
'desc: Text = models.TextField()',
|
||||||
'stream = models.ForeignKey(Stream, on_delete=CASCADE) # type: Stream'],
|
'stream: Stream = models.ForeignKey(Stream, on_delete=CASCADE)'],
|
||||||
'bad_lines': ['desc = models.TextField() # type: Optional[Text]',
|
'bad_lines': ['desc: Optional[Text] = models.TextField()',
|
||||||
'stream = models.ForeignKey(Stream, on_delete=CASCADE) # type: Optional[Stream]'],
|
'stream: Optional[Stream] = models.ForeignKey(Stream, on_delete=CASCADE)'],
|
||||||
},
|
},
|
||||||
{'pattern': r'[\s([]Text([^\s\w]|$)',
|
{'pattern': r'[\s([]Text([^\s\w]|$)',
|
||||||
'exclude': {
|
'exclude': {
|
||||||
|
|
|
@ -30,9 +30,9 @@ INACTIVE_ENTRY = (
|
||||||
|
|
||||||
FILE_TEMPLATE = (
|
FILE_TEMPLATE = (
|
||||||
"from typing import Any, Dict\n\n"
|
"from typing import Any, Dict\n\n"
|
||||||
"EMOJI_NAME_MAPS = {"
|
"EMOJI_NAME_MAPS: Dict[str, Dict[str, Any]] = {"
|
||||||
"%(emoji_entries)s\n"
|
"%(emoji_entries)s\n"
|
||||||
"} # type: Dict[str, Dict[str, Any]]\n"
|
"}\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
emoji_names: Set[str] = set()
|
emoji_names: Set[str] = set()
|
||||||
|
|
|
@ -10,7 +10,7 @@ from typing import Dict, Iterable, List
|
||||||
def alert_words_in_realm(realm: Realm) -> Dict[int, List[str]]:
|
def alert_words_in_realm(realm: Realm) -> Dict[int, List[str]]:
|
||||||
user_ids_and_words = AlertWord.objects.filter(
|
user_ids_and_words = AlertWord.objects.filter(
|
||||||
realm=realm, user_profile__is_active=True).values("user_profile_id", "word")
|
realm=realm, user_profile__is_active=True).values("user_profile_id", "word")
|
||||||
user_ids_with_words = dict() # type: Dict[int, List[str]]
|
user_ids_with_words: Dict[int, List[str]] = dict()
|
||||||
for id_and_word in user_ids_and_words:
|
for id_and_word in user_ids_and_words:
|
||||||
user_ids_with_words.setdefault(id_and_word["user_profile_id"], [])
|
user_ids_with_words.setdefault(id_and_word["user_profile_id"], [])
|
||||||
user_ids_with_words[id_and_word["user_profile_id"]].append(id_and_word["word"])
|
user_ids_with_words[id_and_word["user_profile_id"]].append(id_and_word["word"])
|
||||||
|
|
|
@ -68,7 +68,7 @@ class JsonableError(Exception):
|
||||||
data_fields = ['widget_name']
|
data_fields = ['widget_name']
|
||||||
|
|
||||||
def __init__(self, widget_name: str) -> None:
|
def __init__(self, widget_name: str) -> None:
|
||||||
self.widget_name = widget_name # type: str
|
self.widget_name: str = widget_name
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def msg_format() -> str:
|
def msg_format() -> str:
|
||||||
|
|
|
@ -33,7 +33,10 @@ def move_missed_message_addresses_to_database(apps: StateApps, schema_editor: Da
|
||||||
redis_client.delete(key)
|
redis_client.delete(key)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
user_profile_id, recipient_id, subject_b = result # type: (bytes, bytes, bytes)
|
user_profile_id: bytes
|
||||||
|
recipient_id: bytes
|
||||||
|
subject_id: bytes
|
||||||
|
user_profile_id, recipient_id, subject_b = result
|
||||||
topic_name = subject_b.decode('utf-8')
|
topic_name = subject_b.decode('utf-8')
|
||||||
|
|
||||||
# The data model for missed-message emails has changed in two
|
# The data model for missed-message emails has changed in two
|
||||||
|
|
|
@ -28,7 +28,7 @@ def move_back_to_user_profile(apps: StateApps, schema_editor: DatabaseSchemaEdit
|
||||||
UserProfile = apps.get_model('zerver', 'UserProfile')
|
UserProfile = apps.get_model('zerver', 'UserProfile')
|
||||||
|
|
||||||
user_ids_and_words = AlertWord.objects.all().values("user_profile_id", "word")
|
user_ids_and_words = AlertWord.objects.all().values("user_profile_id", "word")
|
||||||
user_ids_with_words = dict() # type: Dict[int, List[str]]
|
user_ids_with_words: Dict[int, List[str]] = dict()
|
||||||
|
|
||||||
for id_and_word in user_ids_and_words:
|
for id_and_word in user_ids_and_words:
|
||||||
user_ids_with_words.setdefault(id_and_word["user_profile_id"], [])
|
user_ids_with_words.setdefault(id_and_word["user_profile_id"], [])
|
||||||
|
|
|
@ -2894,10 +2894,10 @@ class AlertWord(models.Model):
|
||||||
# never move to another realm, so it's static, and having Realm
|
# never move to another realm, so it's static, and having Realm
|
||||||
# here optimizes the main query on this table, which is fetching
|
# here optimizes the main query on this table, which is fetching
|
||||||
# all the alert words in a realm.
|
# all the alert words in a realm.
|
||||||
realm = models.ForeignKey(Realm, db_index=True, on_delete=CASCADE) # type: Realm
|
realm: Realm = models.ForeignKey(Realm, db_index=True, on_delete=CASCADE)
|
||||||
user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) # type: UserProfile
|
user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE)
|
||||||
# Case-insensitive name for the alert word.
|
# Case-insensitive name for the alert word.
|
||||||
word = models.TextField() # type: str
|
word: str = models.TextField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("user_profile", "word")
|
unique_together = ("user_profile", "word")
|
||||||
|
|
|
@ -2229,11 +2229,13 @@ class GoogleAuthBackendTest(SocialAuthBase):
|
||||||
|
|
||||||
def test_redirect_to_next_url_for_log_into_subdomain(self) -> None:
|
def test_redirect_to_next_url_for_log_into_subdomain(self) -> None:
|
||||||
def test_redirect_to_next_url(next: str='') -> HttpResponse:
|
def test_redirect_to_next_url(next: str='') -> HttpResponse:
|
||||||
data = {'full_name': 'Hamlet',
|
data: ExternalAuthDataDict = {
|
||||||
|
'full_name': 'Hamlet',
|
||||||
'email': self.example_email("hamlet"),
|
'email': self.example_email("hamlet"),
|
||||||
'subdomain': 'zulip',
|
'subdomain': 'zulip',
|
||||||
'is_signup': False,
|
'is_signup': False,
|
||||||
'redirect_to': next} # type: ExternalAuthDataDict
|
'redirect_to': next,
|
||||||
|
}
|
||||||
user_profile = self.example_user('hamlet')
|
user_profile = self.example_user('hamlet')
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
'zerver.views.auth.authenticate',
|
'zerver.views.auth.authenticate',
|
||||||
|
@ -2254,22 +2256,26 @@ class GoogleAuthBackendTest(SocialAuthBase):
|
||||||
self.assertEqual(res.url, 'http://zulip.testserver/#narrow/stream/7-test-here')
|
self.assertEqual(res.url, 'http://zulip.testserver/#narrow/stream/7-test-here')
|
||||||
|
|
||||||
def test_log_into_subdomain_when_token_is_malformed(self) -> None:
|
def test_log_into_subdomain_when_token_is_malformed(self) -> None:
|
||||||
data = {'full_name': 'Full Name',
|
data: ExternalAuthDataDict = {
|
||||||
|
'full_name': 'Full Name',
|
||||||
'email': self.example_email("hamlet"),
|
'email': self.example_email("hamlet"),
|
||||||
'subdomain': 'zulip',
|
'subdomain': 'zulip',
|
||||||
'is_signup': False,
|
'is_signup': False,
|
||||||
'redirect_to': ''} # type: ExternalAuthDataDict
|
'redirect_to': '',
|
||||||
|
}
|
||||||
with mock.patch("logging.warning") as mock_warn:
|
with mock.patch("logging.warning") as mock_warn:
|
||||||
result = self.get_log_into_subdomain(data, force_token='nonsense')
|
result = self.get_log_into_subdomain(data, force_token='nonsense')
|
||||||
mock_warn.assert_called_once_with("log_into_subdomain: Malformed token given: %s", "nonsense")
|
mock_warn.assert_called_once_with("log_into_subdomain: Malformed token given: %s", "nonsense")
|
||||||
self.assertEqual(result.status_code, 400)
|
self.assertEqual(result.status_code, 400)
|
||||||
|
|
||||||
def test_log_into_subdomain_when_token_not_found(self) -> None:
|
def test_log_into_subdomain_when_token_not_found(self) -> None:
|
||||||
data = {'full_name': 'Full Name',
|
data: ExternalAuthDataDict = {
|
||||||
|
'full_name': 'Full Name',
|
||||||
'email': self.example_email("hamlet"),
|
'email': self.example_email("hamlet"),
|
||||||
'subdomain': 'zulip',
|
'subdomain': 'zulip',
|
||||||
'is_signup': False,
|
'is_signup': False,
|
||||||
'redirect_to': ''} # type: ExternalAuthDataDict
|
'redirect_to': '',
|
||||||
|
}
|
||||||
with mock.patch("logging.warning") as mock_warn:
|
with mock.patch("logging.warning") as mock_warn:
|
||||||
token = generate_random_token(ExternalAuthResult.LOGIN_TOKEN_LENGTH)
|
token = generate_random_token(ExternalAuthResult.LOGIN_TOKEN_LENGTH)
|
||||||
result = self.get_log_into_subdomain(data, force_token=token)
|
result = self.get_log_into_subdomain(data, force_token=token)
|
||||||
|
@ -2283,11 +2289,13 @@ class GoogleAuthBackendTest(SocialAuthBase):
|
||||||
existing_user.email = 'whatever@zulip.com'
|
existing_user.email = 'whatever@zulip.com'
|
||||||
existing_user.save()
|
existing_user.save()
|
||||||
|
|
||||||
data = {'full_name': 'Full Name',
|
data: ExternalAuthDataDict = {
|
||||||
|
'full_name': 'Full Name',
|
||||||
'email': 'existing@zulip.com',
|
'email': 'existing@zulip.com',
|
||||||
'subdomain': 'zulip',
|
'subdomain': 'zulip',
|
||||||
'is_signup': True,
|
'is_signup': True,
|
||||||
'redirect_to': ''} # type: ExternalAuthDataDict
|
'redirect_to': '',
|
||||||
|
}
|
||||||
result = self.get_log_into_subdomain(data)
|
result = self.get_log_into_subdomain(data)
|
||||||
|
|
||||||
# Should simply get logged into the existing account:
|
# Should simply get logged into the existing account:
|
||||||
|
@ -2295,11 +2303,13 @@ class GoogleAuthBackendTest(SocialAuthBase):
|
||||||
self.assert_logged_in_user_id(existing_user.id)
|
self.assert_logged_in_user_id(existing_user.id)
|
||||||
|
|
||||||
def test_log_into_subdomain_when_is_signup_is_true_and_new_user(self) -> None:
|
def test_log_into_subdomain_when_is_signup_is_true_and_new_user(self) -> None:
|
||||||
data = {'full_name': 'New User Name',
|
data: ExternalAuthDataDict = {
|
||||||
|
'full_name': 'New User Name',
|
||||||
'email': 'new@zulip.com',
|
'email': 'new@zulip.com',
|
||||||
'subdomain': 'zulip',
|
'subdomain': 'zulip',
|
||||||
'is_signup': True,
|
'is_signup': True,
|
||||||
'redirect_to': ''} # type: ExternalAuthDataDict
|
'redirect_to': '',
|
||||||
|
}
|
||||||
result = self.get_log_into_subdomain(data)
|
result = self.get_log_into_subdomain(data)
|
||||||
self.assertEqual(result.status_code, 302)
|
self.assertEqual(result.status_code, 302)
|
||||||
confirmation = Confirmation.objects.all().first()
|
confirmation = Confirmation.objects.all().first()
|
||||||
|
@ -2318,11 +2328,13 @@ class GoogleAuthBackendTest(SocialAuthBase):
|
||||||
self.assert_in_success_response(['id_full_name'], result)
|
self.assert_in_success_response(['id_full_name'], result)
|
||||||
|
|
||||||
def test_log_into_subdomain_when_is_signup_is_false_and_new_user(self) -> None:
|
def test_log_into_subdomain_when_is_signup_is_false_and_new_user(self) -> None:
|
||||||
data = {'full_name': 'New User Name',
|
data: ExternalAuthDataDict = {
|
||||||
|
'full_name': 'New User Name',
|
||||||
'email': 'new@zulip.com',
|
'email': 'new@zulip.com',
|
||||||
'subdomain': 'zulip',
|
'subdomain': 'zulip',
|
||||||
'is_signup': False,
|
'is_signup': False,
|
||||||
'redirect_to': ''} # type: ExternalAuthDataDict
|
'redirect_to': '',
|
||||||
|
}
|
||||||
result = self.get_log_into_subdomain(data)
|
result = self.get_log_into_subdomain(data)
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
self.assert_in_response('No account found for', result)
|
self.assert_in_response('No account found for', result)
|
||||||
|
@ -2346,11 +2358,13 @@ class GoogleAuthBackendTest(SocialAuthBase):
|
||||||
self.assert_in_success_response(['id_full_name'], result)
|
self.assert_in_success_response(['id_full_name'], result)
|
||||||
|
|
||||||
def test_log_into_subdomain_when_using_invite_link(self) -> None:
|
def test_log_into_subdomain_when_using_invite_link(self) -> None:
|
||||||
data = {'full_name': 'New User Name',
|
data: ExternalAuthDataDict = {
|
||||||
|
'full_name': 'New User Name',
|
||||||
'email': 'new@zulip.com',
|
'email': 'new@zulip.com',
|
||||||
'subdomain': 'zulip',
|
'subdomain': 'zulip',
|
||||||
'is_signup': True,
|
'is_signup': True,
|
||||||
'redirect_to': ''} # type: ExternalAuthDataDict
|
'redirect_to': '',
|
||||||
|
}
|
||||||
|
|
||||||
realm = get_realm("zulip")
|
realm = get_realm("zulip")
|
||||||
realm.invite_required = True
|
realm.invite_required = True
|
||||||
|
@ -2406,11 +2420,13 @@ class GoogleAuthBackendTest(SocialAuthBase):
|
||||||
self.assertEqual(sorted(new_streams), stream_names)
|
self.assertEqual(sorted(new_streams), stream_names)
|
||||||
|
|
||||||
def test_log_into_subdomain_when_email_is_none(self) -> None:
|
def test_log_into_subdomain_when_email_is_none(self) -> None:
|
||||||
data = {'full_name': None,
|
data: ExternalAuthDataDict = {
|
||||||
|
'full_name': None,
|
||||||
'email': None,
|
'email': None,
|
||||||
'subdomain': 'zulip',
|
'subdomain': 'zulip',
|
||||||
'is_signup': False,
|
'is_signup': False,
|
||||||
'redirect_to': ''} # type: ExternalAuthDataDict
|
'redirect_to': '',
|
||||||
|
}
|
||||||
|
|
||||||
with mock.patch('logging.warning') as mock_warn:
|
with mock.patch('logging.warning') as mock_warn:
|
||||||
result = self.get_log_into_subdomain(data)
|
result = self.get_log_into_subdomain(data)
|
||||||
|
@ -2418,9 +2434,11 @@ class GoogleAuthBackendTest(SocialAuthBase):
|
||||||
mock_warn.assert_called_once()
|
mock_warn.assert_called_once()
|
||||||
|
|
||||||
def test_user_cannot_log_into_wrong_subdomain(self) -> None:
|
def test_user_cannot_log_into_wrong_subdomain(self) -> None:
|
||||||
data = {'full_name': 'Full Name',
|
data: ExternalAuthDataDict = {
|
||||||
|
'full_name': 'Full Name',
|
||||||
'email': self.example_email("hamlet"),
|
'email': self.example_email("hamlet"),
|
||||||
'subdomain': 'zephyr'} # type: ExternalAuthDataDict
|
'subdomain': 'zephyr',
|
||||||
|
}
|
||||||
result = self.get_log_into_subdomain(data)
|
result = self.get_log_into_subdomain(data)
|
||||||
self.assert_json_error(result, "Invalid subdomain")
|
self.assert_json_error(result, "Invalid subdomain")
|
||||||
|
|
||||||
|
|
|
@ -677,7 +677,8 @@ class SlackImporter(ZulipTestCase):
|
||||||
realm: Dict[str, Any] = {'zerver_subscription': []}
|
realm: Dict[str, Any] = {'zerver_subscription': []}
|
||||||
user_list: List[Dict[str, Any]] = []
|
user_list: List[Dict[str, Any]] = []
|
||||||
reactions = [{"name": "grinning", "users": ["U061A5N1G"], "count": 1}]
|
reactions = [{"name": "grinning", "users": ["U061A5N1G"], "count": 1}]
|
||||||
attachments = uploads = [] # type: List[Dict[str, Any]]
|
attachments: List[Dict[str, Any]] = []
|
||||||
|
uploads: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
zerver_usermessage = [{'id': 3}, {'id': 5}, {'id': 6}, {'id': 9}]
|
zerver_usermessage = [{'id': 3}, {'id': 5}, {'id': 6}, {'id': 9}]
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,9 @@ def _transform_commits_list_to_common_format(commits: List[Dict[str, Any]]) -> L
|
||||||
def beanstalk_decoder(view_func: ViewFuncT) -> ViewFuncT:
|
def beanstalk_decoder(view_func: ViewFuncT) -> ViewFuncT:
|
||||||
@wraps(view_func)
|
@wraps(view_func)
|
||||||
def _wrapped_view_func(request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
|
def _wrapped_view_func(request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
|
||||||
auth_type, encoded_value = request.META['HTTP_AUTHORIZATION'].split() # type: str, str
|
auth_type: str
|
||||||
|
encoded_value: str
|
||||||
|
auth_type, encoded_value = request.META['HTTP_AUTHORIZATION'].split()
|
||||||
if auth_type.lower() == "basic":
|
if auth_type.lower() == "basic":
|
||||||
email, api_key = base64.b64decode(encoded_value).decode('utf-8').split(":")
|
email, api_key = base64.b64decode(encoded_value).decode('utf-8').split(":")
|
||||||
email = email.replace('%40', '@')
|
email = email.replace('%40', '@')
|
||||||
|
|
Loading…
Reference in New Issue