From 79b17eb75be982ff077b9305d1310491af6ba9eb Mon Sep 17 00:00:00 2001 From: Peter Schmid Date: Wed, 2 Oct 2019 16:27:43 +0200 Subject: [PATCH 1/3] PS: annotated all function definitions in main with type hints, including some notes and links, as well as a todo regarding mypy not knowing numpy (there is a workaround, but may be tricky to fix this properly) --- secretsanta/main/core.py | 14 +++++++++++--- secretsanta/main/funs.py | 13 ++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/secretsanta/main/core.py b/secretsanta/main/core.py index b5f75c6..a277fa3 100644 --- a/secretsanta/main/core.py +++ b/secretsanta/main/core.py @@ -1,3 +1,5 @@ +from smtplib import SMTP # Note: Required for the type hint to work +from typing import Dict, Tuple from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText @@ -13,11 +15,15 @@ class SecretSanta: # getter-only decorator @property, exposing the helper attribute. # It is afterwards accessed like any attribute, i.e. without (). @property - def helper(self): + def helper(self: "SecretSanta") -> str: return self._helper + # "SectretSanta" string type hint follows Python > 3.7 convention, see + # https://stackoverflow.com/questions/33533148/how-do-i-specify-that-the-return-type-of-a-method-is-the-same-as-the-class-itsel + # for more modern ways to use a class within its own definition as a type hint + # constructor - def __init__(self, email, person): + def __init__(self: "SecretSanta", email: str, person: str) -> None: # https://stackoverflow.com/questions/5599254/how-to-use-sphinxs-autodoc-to-document-a-classs-init-self-method """ init method @@ -35,7 +41,9 @@ def __init__(self, email, person): self.email = email self.person = person - def send(self, subject, from_address, message, mailserver, test=False): + # Note: Instead of returning a dictionary, it may also raise an error (see SMTP.sendmail documentation) + def send(self: "SecretSanta", subject: str, from_address: str, + message: str, mailserver: SMTP, test: bool = False) -> Dict[str, Tuple[int, bytes]]: """ send method diff --git a/secretsanta/main/funs.py b/secretsanta/main/funs.py index 8036738..850e693 100644 --- a/secretsanta/main/funs.py +++ b/secretsanta/main/funs.py @@ -1,13 +1,17 @@ import smtplib +from typing import Optional, Dict, Tuple, Union import numpy as np import datetime from contextlib import suppress from secretsanta.main.core import SecretSanta # PyCharm: ctrl-p inside parentheses shows function args! +sendmailDictOrInt = Union[Dict[str, Tuple[int, bytes]], int] -def make_santa_dict(dictionary, seed=None, verbose=False): +# Todo: figure out how to deal with missing library stub file for module 'numpy' +# workaround: append --ignore-missing-imports to the mypy call (see https://github.com/python/mypy/issues/3905) +def make_santa_dict(dictionary: Dict[str, str], seed: Optional[int] = None, verbose: bool = False) -> Dict[str, str]: # type triple-quotes and press enter to generate empty docstring stub """ creates a randomized 'santa' dictionary from an initial dictionary of names with associated email addresses @@ -63,7 +67,8 @@ def make_santa_dict(dictionary, seed=None, verbose=False): return senddict -def send_santa_dict(smtpserverwithport, sender, pwd, senddict, test=False): +def send_santa_dict(smtpserverwithport: str, sender: str, pwd: str, + senddict: Dict[str, str], test: bool = False) -> sendmailDictOrInt: """ loops over a 'santa' dictionary and sends respective emails @@ -83,7 +88,9 @@ def send_santa_dict(smtpserverwithport, sender, pwd, senddict, test=False): server.login(sender, pwd) subj = 'Secret Santa %d' % datetime.datetime.now().year - check = 0 + # Note: Need to explicitly state type to avoid 'incompatible types in assignment' error, + # see https://stackoverflow.com/questions/43910979/mypy-error-incompatible-types-in-assignment + check = 0 # type: sendmailDictOrInt for name in senddict: obj = SecretSanta(senddict.get(name), name) From 3ef2f023b56e87d387242dcda845b53e43ba0c40 Mon Sep 17 00:00:00 2001 From: spoltier Date: Wed, 16 Oct 2019 11:32:24 +0200 Subject: [PATCH 2/3] miraisolutions/meetings#88 addressing PR feedback after discussing with Peter --- secretsanta/main/core.py | 2 +- secretsanta/main/funs.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/secretsanta/main/core.py b/secretsanta/main/core.py index a277fa3..4ddee53 100644 --- a/secretsanta/main/core.py +++ b/secretsanta/main/core.py @@ -18,7 +18,7 @@ class SecretSanta: def helper(self: "SecretSanta") -> str: return self._helper - # "SectretSanta" string type hint follows Python > 3.7 convention, see + # "SecretSanta" string type hint follows Python 3.6 convention, see # https://stackoverflow.com/questions/33533148/how-do-i-specify-that-the-return-type-of-a-method-is-the-same-as-the-class-itsel # for more modern ways to use a class within its own definition as a type hint diff --git a/secretsanta/main/funs.py b/secretsanta/main/funs.py index 850e693..eea3a5b 100644 --- a/secretsanta/main/funs.py +++ b/secretsanta/main/funs.py @@ -55,7 +55,7 @@ def make_santa_dict(dictionary: Dict[str, str], seed: Optional[int] = None, verb picked = np.random.choice(pick, 1)[0] if verbose: print(picked) - senddict[name] = dictionary.get(picked) + senddict[name] = dictionary[picked] names.remove(picked) # if swap is necessary ... @@ -90,10 +90,10 @@ def send_santa_dict(smtpserverwithport: str, sender: str, pwd: str, subj = 'Secret Santa %d' % datetime.datetime.now().year # Note: Need to explicitly state type to avoid 'incompatible types in assignment' error, # see https://stackoverflow.com/questions/43910979/mypy-error-incompatible-types-in-assignment - check = 0 # type: sendmailDictOrInt + check: sendmailDictOrInt = 0 for name in senddict: - obj = SecretSanta(senddict.get(name), name) + obj = SecretSanta(senddict[name], name) check = obj.send(subj, sender, 'Lucky you! You got the lovely', server, test) server.quit() From 839a1c78cbb265b59756c96e13bfe05ca8feff8f Mon Sep 17 00:00:00 2001 From: spoltier Date: Wed, 16 Oct 2019 12:06:57 +0200 Subject: [PATCH 3/3] miraisolutions/meetings#88 hint for type hints in PyCharm --- secretsanta/main/funs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/secretsanta/main/funs.py b/secretsanta/main/funs.py index eea3a5b..25255a3 100644 --- a/secretsanta/main/funs.py +++ b/secretsanta/main/funs.py @@ -37,6 +37,7 @@ def make_santa_dict(dictionary: Dict[str, str], seed: Optional[int] = None, verb # print(dictionary.get(name)) if verbose: print(name) + # PyCharm: Alt+Enter (on variable definition) -> Add type hint - automatically generates type given assignment pick = names.copy() if len(pick) == 2: swapname1 = name