référence:
J'ai essayé la vérification de type statique en ajoutant une annotation de type de mypy au code de la commande que j'ai écrite plus tôt en Python.
De nombreux stubs de bibliothèque standard sont déjà définis dans mypy, mais je n'avais pas assez de classes et de méthodes pour les modules argparse, operator et itertools utilisés dans la commande which, j'ai donc essayé de définir les stubs moi-même. C'était un peu difficile car je n'étais pas habitué à la définition de ce talon.
stubs/argparse.py
from typing import Any, List, Sequence, Undefined
class Namespace:
def __init__(self) -> None:
self.commands = Undefined(List[str])
self.is_all = Undefined(bool)
self.is_silent = Undefined(bool)
class ArgumentParser:
def set_defaults(self, **kwargs: Any) -> None: pass
def add_argument(self, *args: Sequence[str], **kwargs: Any) -> None: pass
def parse_args(self, args: Sequence[str], namespace: Namespace = None) -> Namespace: pass
stubs/itertools.py
from typing import Iterable, typevar
_T = typevar('_T')
class chain:
@classmethod
def from_iterable(cls, iterable: Iterable[Iterable[_T]]) -> Iterable[_T]: pass
stubs/operator.py
from typing import Any, Function, Sequence, typevar
_T = typevar('_T')
def itemgetter(item: _T, *items: Sequence[_T]) -> Function[[Any], _T]: pass
Définissez des variables d'environnement pour rendre vos stubs définis visibles par mypy.
python
(mypy)$ export MYPYPATH=./stubs/
En fait, j'étais un peu confus car cela ne fonctionnait pas parce que j'ai écrit l'annotation de type tout en répétant la définition du stub et la vérification de type. Je pense que c'est une question de familiarité.
Enfin, j'ai ajouté l'annotation de type à la source de quelle commande which_with_statally_typed.py?
Le diff avec le code original ressemble à ceci.
python
$ diff -u which.py which_with_statically_typed.py
--- which.py 2014-12-26 12:22:31.000000000 +0900
+++ which_with_statically_typed.py 2014-12-26 16:06:28.000000000 +0900
@@ -7,8 +7,10 @@
from os.path import join as pathjoin
from operator import itemgetter
+from typing import List, Sequence, Tuple # pragma: no flakes
-def search(cmd, paths, is_all=False):
+
+def search(cmd: str, paths: List[str], is_all: bool=False):
for path in paths:
for match in glob.glob(pathjoin(path, cmd)):
if os.access(match, os.X_OK):
@@ -17,7 +19,7 @@
raise StopIteration
-def parse_argument(args=None):
+def parse_argument(args: Sequence[str]=None) -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.set_defaults(is_all=False, is_silent=False, commands=[])
parser.add_argument(
@@ -29,23 +31,23 @@
help="No output, just return 0 if any of the executables are found, "
"or 1 if none are found.")
parser.add_argument("commands", nargs="*")
- args = parser.parse_args(args or sys.argv[1:])
- return args
+ namespace = parser.parse_args(args or sys.argv[1:])
+ return namespace
-def main(cmd_args=None):
+def main(cmd_args: Sequence[str]=None) -> int:
args = parse_argument(cmd_args)
if not args.commands:
print('Usage: python which.py cmd1 [cmd2 ...]')
return 0
env_paths = os.environ['PATH'].split(':')
- result = []
+ result = [] # type: List[Tuple[int, List[str]]]
for cmd in args.commands:
founds = list(search(cmd, env_paths, args.is_all))
result.append((0, founds) if founds else (1, [cmd]))
- status_code = max(map(itemgetter(0), result))
+ status_code = max(map(itemgetter(0), result)) # type: int
if not args.is_silent:
cmd_paths = [paths for ret_val, paths in result if ret_val == 0]
for cmd_path in chain.from_iterable(cmd_paths):
La source entière se trouve sur misc / which.
Recommended Posts