Repository: DataBiosphere/toil

Issue:
Add type hints to ec2nodes.py
Add type hints to src/toil/lib/ec2nodes.py so it can be checked under mypy during linting.

Refers to #3568.

┆Issue is synchronized with this [Jira Task](https://ucsc-cgl.atlassian.net/browse/TOIL-922)
┆Issue Number: TOIL-922



Discussion:

Resolving patch:
diff --git a/contrib/admin/mypy-with-ignore.py b/contrib/admin/mypy-with-ignore.py
--- a/contrib/admin/mypy-with-ignore.py
+++ b/contrib/admin/mypy-with-ignore.py
@@ -102,7 +102,6 @@ def main():
         'src/toil/lib/iterables.py',
         'src/toil/lib/bioio.py',
         'src/toil/lib/ec2.py',
-        'src/toil/lib/ec2nodes.py',
         'src/toil/lib/expando.py',
         'src/toil/lib/threading.py',
         'src/toil/lib/exceptions.py',
diff --git a/src/toil/lib/ec2nodes.py b/src/toil/lib/ec2nodes.py
--- a/src/toil/lib/ec2nodes.py
+++ b/src/toil/lib/ec2nodes.py
@@ -19,6 +19,8 @@
 
 import requests
 
+from typing import Dict, List, Union, Tuple, Any
+
 logger = logging.getLogger(__name__)
 dirname = os.path.dirname(__file__)
 
@@ -45,14 +47,14 @@
 class InstanceType(object):
     __slots__ = ('name', 'cores', 'memory', 'disks', 'disk_capacity')
 
-    def __init__(self, name, cores, memory, disks, disk_capacity):
+    def __init__(self, name: str, cores: int, memory: float, disks: float, disk_capacity: float):
         self.name = name  # the API name of the instance type
         self.cores = cores  # the number of cores
         self.memory = memory  # RAM in GiB
         self.disks = disks  # the number of ephemeral (aka 'instance store') volumes
         self.disk_capacity = disk_capacity  # the capacity of each ephemeral volume in GiB
 
-    def __str__(self):
+    def __str__(self) -> str:
         return ("Type: {}\n"
                 "Cores: {}\n"
                 "Disks: {}\n"
@@ -65,7 +67,9 @@ def __str__(self):
                 self.memory,
                 self.disk_capacity))
 
-    def __eq__(self, other):
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, InstanceType):
+            return NotImplemented
         if (self.name == other.name and
             self.cores == other.cores and
             self.memory == other.memory and
@@ -75,7 +79,7 @@ def __eq__(self, other):
         return False
 
 
-def isNumber(s):
+def isNumber(s: str) -> bool:
     """
     Determines if a unicode string (that may include commas) is a number.
 
@@ -97,7 +101,7 @@ def isNumber(s):
     return False
 
 
-def parseStorage(storageData):
+def parseStorage(storageData: str) -> Union[List[int], Tuple[Union[int, float], float]]:
     """
     Parses EC2 JSON storage param string into a number.
 
@@ -124,7 +128,7 @@ def parseStorage(storageData):
             raise RuntimeError('EC2 JSON format has likely changed.  Error parsing disk specs.')
 
 
-def parseMemory(memAttribute):
+def parseMemory(memAttribute: str) -> float:
     """
     Returns EC2 'memory' string as a float.
 
@@ -142,7 +146,7 @@ def parseMemory(memAttribute):
         raise RuntimeError('EC2 JSON format has likely changed.  Error parsing memory.')
 
 
-def fetchEC2Index(filename):
+def fetchEC2Index(filename: str) -> None:
     """Downloads and writes the AWS Billing JSON to a file using the AWS pricing API.
 
     See: https://aws.amazon.com/blogs/aws/new-aws-price-list-api/
@@ -162,7 +166,7 @@ def fetchEC2Index(filename):
         raise RuntimeError('Error: ' + str(response) + ' :: ' + str(response.text))
 
 
-def fetchEC2InstanceDict(awsBillingJson, region):
+def fetchEC2InstanceDict(awsBillingJson: Dict[str, Any], region: str) -> Dict[str, InstanceType]:
     """
     Takes a JSON and returns a list of InstanceType objects representing EC2 instance params.
 
@@ -202,7 +206,7 @@ def fetchEC2InstanceDict(awsBillingJson, region):
     return dict((_.name, _) for _ in ec2InstanceList)
 
 
-def updateStaticEC2Instances():
+def updateStaticEC2Instances() -> None:
     """
     Generates a new python file of fetchable EC2 Instances by region with current prices and specs.
 
@@ -234,7 +238,7 @@ def updateStaticEC2Instances():
         awsProductDict = json.loads(f.read())
 
     currentEC2List = []
-    instancesByRegion = {}
+    instancesByRegion: Dict[str, List[str]] = {}
     for regionNickname in EC2Regions:
         currentEC2Dict = fetchEC2InstanceDict(awsProductDict, region=EC2Regions[regionNickname])
         for instanceName, instanceTypeObj in currentEC2Dict.items():


Test patch: