diff --git a/misc/actions_stubs.py b/misc/actions_stubs.py new file mode 100644 index 000000000000..978af7187ffe --- /dev/null +++ b/misc/actions_stubs.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +import os +import shutil +from typing import Tuple, Any +try: + import click +except ImportError: + print("You need the module \'click\'") + exit(1) + +base_path = os.getcwd() + +# I don't know how to set callables with different args +def apply_all(func: Any, directory: str, extension: str, + to_extension: str='', exclude: Tuple[str]=('',), + recursive: bool=True, debug: bool=False) -> None: + excluded = [x+extension for x in exclude] if exclude else [] + for p, d, files in os.walk(os.path.join(base_path,directory)): + for f in files: + if "{}".format(f) in excluded: + continue + inner_path = os.path.join(p,f) + if not inner_path.endswith(extension): + continue + if to_extension: + new_path = "{}{}".format(inner_path[:-len(extension)],to_extension) + func(inner_path,new_path) + else: + func(inner_path) + if not recursive: + break + +def confirm(resp: bool=False, **kargs) -> bool: + kargs['rest'] = "to this {f2}/*{e2}".format(**kargs) if kargs.get('f2') else '' + prompt = "{act} all files {rec}matching this expression {f1}/*{e1} {rest}".format(**kargs) + prompt.format(**kargs) + prompt = "{} [{}]|{}: ".format(prompt, 'Y' if resp else 'N', 'n' if resp else 'y') + while True: + ans = input(prompt).lower() + if not ans: + return resp + if ans not in ['y','n']: + print( 'Please, enter (y) or (n).') + continue + if ans == 'y': + return True + else: + return False + +actions = ['cp', 'mv', 'rm'] +@click.command(context_settings=dict(help_option_names=['-h', '--help'])) +@click.option('--action', '-a', type=click.Choice(actions), required=True, help="What do I have to do :-)") +@click.option('--dir', '-d', 'directory', default='stubs', help="Directory to start search!") +@click.option('--ext', '-e', 'extension', default='.py', help="Extension \"from\" will be applied the action. Default .py") +@click.option('--to', '-t', 'to_extension', default='.pyi', help="Extension \"to\" will be applied the action if can. Default .pyi") +@click.option('--exclude', '-x', multiple=True, default=('__init__',), help="For every appear, will ignore this files. (can set multiples times)") +@click.option('--not-recursive', '-n', default=True, is_flag=True, help="Set if don't want to walk recursively.") +def main(action: str, directory: str, extension: str, to_extension: str, + exclude: Tuple[str], not_recursive: bool) -> None: + """ + This script helps to copy/move/remove files based on their extension. + + The three actions will ask you for confirmation. + + Examples (by default the script search in stubs directory): + + - Change extension of all stubs from .py to .pyi: + + python -a mv + + - Revert the previous action. + + python -a mv -e .pyi -t .py + + - If you want to ignore "awesome.py" files. + + python -a [cp|mv|rm] -x awesome + + - If you want to ignore "awesome.py" and "__init__.py" files. + + python -a [cp|mv|rm] -x awesome -x __init__ + + - If you want to remove all ".todo" files in "todo" directory, but not recursively: + + python -a rm -e .todo -d todo -r + + """ + if action not in actions: + print("Your action have to be one of this: {}".format(', '.join(actions))) + return + + rec = "[Recursively] " if not_recursive else '' + if not extension.startswith('.'): + extension = ".{}".format(extension) + if not to_extension.startswith('.'): + to_extension = ".{}".format(to_extension) + if directory.endswith('/'): + directory = directory[:-1] + if action == 'cp': + if confirm(act='Copy',rec=rec, f1=directory, e1=extension, f2=directory, e2=to_extension): + apply_all(shutil.copy, directory, extension, to_extension, exclude, not_recursive) + elif action == 'rm': + if confirm(act='Remove',rec=rec, f1=directory, e1=extension): + apply_all(os.remove, directory, extension, exclude=exclude, recursive=not_recursive) + elif action == 'mv': + if confirm(act='Move',rec=rec, f1=directory, e1=extension, f2=directory, e2=to_extension): + apply_all(shutil.move, directory, extension, to_extension, exclude, not_recursive) + + +if __name__ == '__main__': + main() diff --git a/mypy/build.py b/mypy/build.py index 1d515ac9caeb..19dfc88c70eb 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -42,7 +42,6 @@ MODULE = 'module' # Build/run module as a script TEST_BUILTINS = 'test-builtins' # Use stub builtins to speed up tests - # State ids. These describe the states a source file / module can be in a # build. @@ -228,6 +227,7 @@ def default_lib_path(data_dir: str, target: int, pyversion: int, def lookup_program(module: str, lib_path: List[str]) -> str: + # Modules are .py and not .pyi path = find_module(module, lib_path) if path: return path @@ -856,14 +856,16 @@ def read_module_source_from_file(id: str, def find_module(id: str, lib_path: List[str]) -> str: """Return the path of the module source file, or None if not found.""" + extensions = ['.pyi', '.py'] for pathitem in lib_path: - comp = id.split('.') - path = os.path.join(pathitem, os.sep.join(comp[:-1]), comp[-1] + '.py') - text = '' - if not os.path.isfile(path): - path = os.path.join(pathitem, os.sep.join(comp), '__init__.py') - if os.path.isfile(path) and verify_module(id, path): - return path + for extension in extensions: + comp = id.split('.') + path = os.path.join(pathitem, os.sep.join(comp[:-1]), comp[-1] + extension) + text = '' + if not os.path.isfile(path): + path = os.path.join(pathitem, os.sep.join(comp), '__init__.py') + if os.path.isfile(path) and verify_module(id, path): + return path return None diff --git a/mypy/test/data/check-modules.test b/mypy/test/data/check-modules.test index b021a44f1ced..45a5dd17e05a 100644 --- a/mypy/test/data/check-modules.test +++ b/mypy/test/data/check-modules.test @@ -1,3 +1,4 @@ + -- Test cases for the type checker. [case testAccessImportedDefinitions] @@ -137,6 +138,24 @@ def f() -> None: pass [out] main: In class "C": +[case testImportWithStub] +import _m +_m.f("hola") +[file _m.pyi] +def f(c:str) -> None: pass +[out] + + +[case testImportWithStubIncompatibleType] +import _m +_m.f("hola") +_m.f(12) # E: Argument 1 to "f" has incompatible type "int"; expected "str" +[file _m.py] +def f(c): + print(c) +[file _m.pyi] +def f(c:str) -> None: pass + [case testInvalidOperationsOnModules] import m import typing diff --git a/setup.py b/setup.py index aacd85951c4c..d88e9cb16ef5 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,7 @@ for stub_dir in stub_dirs: target = os.path.join('lib', 'mypy', 'stubs', py_version, stub_dir) files = glob.glob(os.path.join(base, stub_dir, '*.py')) + files += glob.glob(os.path.join(base, stub_dir, '*.pyi')) stubs.append((target, files)) classifiers = [ diff --git a/travis.sh b/travis.sh old mode 100644 new mode 100755