Skip to content

Commit fbe61c0

Browse files
authored
Hardened type checking (#2698)
1 parent 755ba3c commit fbe61c0

File tree

12 files changed

+68
-75
lines changed

12 files changed

+68
-75
lines changed

.pre-commit-config.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ repos:
3232
- flake8-absolute-import
3333
- flake8-black>=0.1.1
3434
- flake8-docstrings>=1.5.0
35-
- flake8-mypy
3635
language_version: python3
3736
- repo: https://github.com/adrienverge/yamllint.git
3837
rev: v1.20.0

molecule/api.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ def __getitem__(self, i):
3131
def get(self, key, default):
3232
return self.__dict__.get(key, default)
3333

34-
def append(self, element):
34+
def append(self, element) -> None:
3535
self.__dict__[str(element)] = element
36-
return super(UserListMap, self).append(element)
36+
super(UserListMap, self).append(element)
3737

3838

3939
@lru_cache()

molecule/command/base.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import glob
2525
import os
2626
import shutil
27+
from typing import Any, Callable
2728

2829
import click
2930
from click_help_colors import HelpColorsCommand, HelpColorsGroup
@@ -229,8 +230,8 @@ def click_group_ex():
229230
)
230231

231232

232-
def click_command_ex():
233+
def click_command_ex() -> Callable[[Callable[..., Any]], click.Command]:
233234
"""Return extended version of click.command()."""
234-
return click.command(
235+
return click.command( # type: ignore
235236
cls=HelpColorsCommand, help_headers_color="yellow", help_options_color="green"
236237
)

molecule/command/init/init.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
LOG = logger.get_logger(__name__)
2727

2828

29-
@base.click_group_ex()
29+
@base.click_group_ex() # type: ignore
3030
def init(): # pragma: no cover
3131
"""Initialize a new role or scenario."""
3232

molecule/command/list.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424
import click
2525
import tabulate
2626

27-
from molecule import logger, scenarios, status, util
27+
from molecule import logger, scenarios, util
2828
from molecule.command import base
29+
from molecule.status import Status
2930

3031
LOG = logger.get_logger(__name__)
3132

@@ -109,7 +110,7 @@ def list(ctx, scenario_name, format): # pragma: no cover
109110
for scenario in s:
110111
statuses.extend(base.execute_subcommand(scenario.config, subcommand))
111112

112-
headers = [util.title(name) for name in status.get_status()._fields]
113+
headers = [util.title(name) for name in Status._fields]
113114
if format == "simple" or format == "plain":
114115
table_format = "simple"
115116
if format == "plain":

molecule/driver/base.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@
2424
import os
2525

2626
import molecule
27-
from molecule import status
28-
29-
Status = status.get_status()
27+
from molecule.status import Status
3028

3129

3230
class Driver(object):
@@ -44,9 +42,9 @@ def __init__(self, config=None):
4442
self._config = config
4543
self._path = os.path.abspath(os.path.dirname(inspect.getfile(self.__class__)))
4644

47-
@property
45+
@property # type: ignore
4846
@abc.abstractmethod
49-
def name(self) -> str: # pragma: no cover
47+
def name(self): # pragma: no cover
5048
"""
5149
Name of the driver and returns a string.
5250
@@ -55,7 +53,8 @@ def name(self) -> str: # pragma: no cover
5553
pass
5654

5755
@name.setter # type: ignore
58-
def name(self, value: str) -> None: # pragma: no cover
56+
@abc.abstractmethod
57+
def name(self, value): # pragma: no cover
5958
"""
6059
Driver name setter and returns None.
6160

molecule/logger.py

+21-21
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from ansible.module_utils.parsing.convert_bool import boolean as to_bool
2828

2929

30-
def should_do_markup():
30+
def should_do_markup() -> bool:
3131
"""Decide about use of ANSI colors."""
3232
py_colors = os.environ.get("PY_COLORS", None)
3333
if py_colors is not None:
@@ -40,7 +40,7 @@ def should_do_markup():
4040
OUT = 101
4141

4242

43-
class LogFilter(object):
43+
class LogFilter(logging.Filter):
4444
"""A custom log filter which excludes log messages above the logged level."""
4545

4646
def __init__(self, level):
@@ -83,7 +83,7 @@ def format(self, record):
8383
return super(TrailingNewlineFormatter, self).format(record)
8484

8585

86-
def get_logger(name=None):
86+
def get_logger(name=None) -> logging.Logger:
8787
"""
8888
Build a logger with the given name and returns the logger.
8989
@@ -93,7 +93,7 @@ def get_logger(name=None):
9393
"""
9494
logging.setLoggerClass(CustomLogger)
9595

96-
logger = logging.getLogger(name)
96+
logger = logging.getLogger(name) # type: logging.Logger
9797
logger.setLevel(logging.DEBUG)
9898

9999
logger.addHandler(_get_info_handler())
@@ -107,83 +107,83 @@ def get_logger(name=None):
107107
return logger
108108

109109

110-
def _get_info_handler():
110+
def _get_info_handler() -> logging.StreamHandler:
111111
handler = logging.StreamHandler(sys.stdout)
112112
handler.setLevel(logging.INFO)
113-
handler.addFilter(LogFilter(logging.INFO))
113+
handler.addFilter(LogFilter(logging.INFO)) # type: ignore
114114
handler.setFormatter(
115115
TrailingNewlineFormatter("--> {}".format(cyan_text("%(message)s")))
116116
)
117117

118118
return handler
119119

120120

121-
def _get_out_handler():
121+
def _get_out_handler() -> logging.StreamHandler:
122122
handler = logging.StreamHandler(sys.stdout)
123123
handler.setLevel(OUT)
124-
handler.addFilter(LogFilter(OUT))
124+
handler.addFilter(LogFilter(OUT)) # type: ignore
125125
handler.setFormatter(TrailingNewlineFormatter(" %(message)s"))
126126

127127
return handler
128128

129129

130-
def _get_warn_handler():
130+
def _get_warn_handler() -> logging.StreamHandler:
131131
handler = logging.StreamHandler(sys.stdout)
132132
handler.setLevel(logging.WARN)
133-
handler.addFilter(LogFilter(logging.WARN))
133+
handler.addFilter(LogFilter(logging.WARN)) # type: ignore
134134
handler.setFormatter(TrailingNewlineFormatter(yellow_text("%(message)s")))
135135

136136
return handler
137137

138138

139-
def _get_error_handler():
139+
def _get_error_handler() -> logging.StreamHandler:
140140
handler = logging.StreamHandler(sys.stderr)
141141
handler.setLevel(logging.ERROR)
142-
handler.addFilter(LogFilter(logging.ERROR))
142+
handler.addFilter(LogFilter(logging.ERROR)) # type: ignore
143143
handler.setFormatter(TrailingNewlineFormatter(red_text("%(message)s")))
144144

145145
return handler
146146

147147

148-
def _get_critical_handler():
148+
def _get_critical_handler() -> logging.StreamHandler:
149149
handler = logging.StreamHandler(sys.stderr)
150150
handler.setLevel(logging.CRITICAL)
151-
handler.addFilter(LogFilter(logging.CRITICAL))
151+
handler.addFilter(LogFilter(logging.CRITICAL)) # type: ignore
152152
handler.setFormatter(TrailingNewlineFormatter(red_text("ERROR: %(message)s")))
153153

154154
return handler
155155

156156

157-
def _get_success_handler():
157+
def _get_success_handler() -> logging.StreamHandler:
158158
handler = logging.StreamHandler(sys.stdout)
159159
handler.setLevel(SUCCESS)
160-
handler.addFilter(LogFilter(SUCCESS))
160+
handler.addFilter(LogFilter(SUCCESS)) # type: ignore
161161
handler.setFormatter(TrailingNewlineFormatter(green_text("%(message)s")))
162162

163163
return handler
164164

165165

166-
def red_text(msg):
166+
def red_text(msg) -> str:
167167
"""Add red markers."""
168168
return color_text(colorama.Fore.RED, msg)
169169

170170

171-
def yellow_text(msg):
171+
def yellow_text(msg) -> str:
172172
"""Add yellow markers."""
173173
return color_text(colorama.Fore.YELLOW, msg)
174174

175175

176-
def green_text(msg):
176+
def green_text(msg) -> str:
177177
"""Add green markers."""
178178
return color_text(colorama.Fore.GREEN, msg)
179179

180180

181-
def cyan_text(msg):
181+
def cyan_text(msg) -> str:
182182
"""Add cyan markers."""
183183
return color_text(colorama.Fore.CYAN, msg)
184184

185185

186-
def color_text(color, msg):
186+
def color_text(color, msg) -> str:
187187
"""Add color markers."""
188188
if should_do_markup():
189189
return "{}{}{}".format(color, msg, colorama.Style.RESET_ALL)

molecule/shell.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@
4444
ENV_FILE = ".env.yml"
4545

4646

47-
def _version_string():
47+
def _version_string() -> str:
4848

4949
v = pkg_resources.parse_version(molecule.__version__)
50-
color = "bright_yellow" if v.is_prerelease else "green"
50+
color = "bright_yellow" if v.is_prerelease else "green" # type: ignore
5151
msg = "molecule %s\n" % _colorize(molecule.__version__, color)
5252
msg += _colorize(
5353
" ansible==%s python==%s.%s"
@@ -61,7 +61,7 @@ def _version_string():
6161
return msg
6262

6363

64-
@click_group_ex()
64+
@click_group_ex() # type: ignore
6565
@click.option(
6666
"--debug/--no-debug",
6767
default=MOLECULE_DEBUG,
@@ -88,7 +88,7 @@ def _version_string():
8888
)
8989
@click.version_option(
9090
prog_name="molecule", version=molecule.__version__, message=_version_string()
91-
)
91+
) # type: ignore
9292
@click.pass_context
9393
def main(ctx, debug, base_config, env_file): # pragma: no cover
9494
"""

molecule/status.py

+10-14
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,15 @@
1919
# DEALINGS IN THE SOFTWARE.
2020
"""Status Module."""
2121

22-
import collections
22+
from typing import NamedTuple
2323

2424

25-
def get_status():
26-
"""Return status."""
27-
return collections.namedtuple(
28-
"Status",
29-
[
30-
"instance_name",
31-
"driver_name",
32-
"provisioner_name",
33-
"scenario_name",
34-
"created",
35-
"converged",
36-
],
37-
)
25+
class Status(NamedTuple):
26+
"""Scenario status information."""
27+
28+
instance_name: str
29+
driver_name: str
30+
provisioner_name: str
31+
scenario_name: str
32+
created: bool
33+
converged: bool

molecule/test/unit/test_status.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,19 @@
2020

2121
import pytest
2222

23-
from molecule import status
23+
from molecule.status import Status
2424

2525

2626
@pytest.fixture
2727
def _instance():
28-
s = status.get_status()
29-
30-
s.instance_name = None
31-
s.driver_name = None
32-
s.provisioner_name = None
33-
s.scenario_name = None
34-
s.created = None
35-
s.converged = None
28+
s = Status(
29+
instance_name=None,
30+
driver_name=None,
31+
provisioner_name=None,
32+
scenario_name=None,
33+
created=None,
34+
converged=None,
35+
)
3636

3737
return s
3838

molecule/util.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ def parallelize(platform):
342342
return [parallelize(platform) for platform in config["platforms"]]
343343

344344

345-
def find_vcs_root(test, dirs=(".git", ".hg", ".svn"), default=None):
345+
def find_vcs_root(test, dirs=(".git", ".hg", ".svn"), default=None) -> str:
346346
"""Return current repository root directory."""
347347
prev, test = None, os.path.abspath(test)
348348
while prev != test:
@@ -352,7 +352,7 @@ def find_vcs_root(test, dirs=(".git", ".hg", ".svn"), default=None):
352352
return default
353353

354354

355-
def lookup_config_file(filename):
355+
def lookup_config_file(filename: str) -> str:
356356
"""Return config file PATH."""
357357
for path in [find_vcs_root(os.getcwd(), default="~"), "~"]:
358358
f = os.path.expanduser("%s/%s" % (path, filename))

mypy.ini

+8-11
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,15 @@
22
python_version = 3.6
33
color_output = True
44
error_summary = True
5-
# TODO(ssbarnea): Remove ignores below:
6-
; disallow_untyped_calls=True
7-
; warn_redundant_casts=True
5+
disallow_untyped_calls=True
6+
warn_redundant_casts=True
87

9-
[mypy-ansiblelint.*]
10-
ignore_missing_imports = True
11-
12-
[mypy-molecule.test]
13-
ignore_errors = True
148

15-
# 3rd party ignores
16-
[mypy-ansible]
9+
# 3rd party ignores, to remove once they add hints
10+
[mypy-ansible.module_utils.parsing.convert_bool]
1711
ignore_missing_imports = True
1812

19-
[mypy-ansible.*]
13+
[mypy-ansible.release]
2014
ignore_missing_imports = True
2115

2216
[mypy-cerberus.*]
@@ -46,6 +40,9 @@ ignore_missing_imports = True
4640
[mypy-pytest]
4741
ignore_missing_imports = True
4842

43+
[mypy-setuptools]
44+
ignore_missing_imports = True
45+
4946
[mypy-sh]
5047
ignore_missing_imports = True
5148

0 commit comments

Comments
 (0)