7
7
import re
8
8
import sys
9
9
import time
10
+ from pydoc import locate
10
11
11
- from typing import Any , Dict , List , Mapping , Optional , Set , Tuple
12
+ from typing import Any , Dict , List , Mapping , Optional , Set , Tuple , Callable
12
13
13
14
from mypy import build
14
15
from mypy import defaults
15
16
from mypy import experiments
17
+ from mypy import hooks
16
18
from mypy import util
17
19
from mypy .build import BuildSource , BuildResult , PYTHON_EXTENSIONS
18
20
from mypy .errors import CompileError
@@ -548,6 +550,19 @@ def get_init_file(dir: str) -> Optional[str]:
548
550
return None
549
551
550
552
553
+ def load_hook (prefix : str , hook_name : str , hook_path : str ) -> Optional [Callable ]:
554
+ # FIXME: no stubs for pydoc. should we write stubs or a simple replacement for locate?
555
+ obj = locate (hook_path )
556
+ if obj is None :
557
+ print ("%s: Could not find hook %s at %s" %
558
+ (prefix , hook_name , hook_path ), file = sys .stderr )
559
+ if not callable (obj ):
560
+ print ("%s: Hook %s at %s is not callable" %
561
+ (prefix , hook_name , hook_path ), file = sys .stderr )
562
+ return None
563
+ return obj
564
+
565
+
551
566
# For most options, the type of the default value set in options.py is
552
567
# sufficient, and we don't have to do anything here. This table
553
568
# exists to specify types for values initialized to None or container
@@ -603,19 +618,33 @@ def parse_config_file(options: Options, filename: Optional[str]) -> None:
603
618
else :
604
619
section = parser ['mypy' ]
605
620
prefix = '%s: [%s]' % (file_read , 'mypy' )
606
- updates , report_dirs = parse_section (prefix , options , section )
621
+ updates , report_dirs , hook_funcs = parse_section (prefix , options , section )
607
622
for k , v in updates .items ():
608
623
setattr (options , k , v )
624
+
625
+ # bind hook functions to hooks module
626
+ for k , v in hook_funcs .items ():
627
+ hook_func = load_hook (prefix , k , v )
628
+ if hook_func is not None :
629
+ # FIXME: dynamically check loaded function annotations against those in `hooks`?
630
+ setattr (hooks , k , hook_func )
631
+ # look for an options section for this hook
632
+ if k in parser :
633
+ hooks .options [k ] = dict (parser [k ])
609
634
options .report_dirs .update (report_dirs )
610
635
611
636
for name , section in parser .items ():
612
637
if name .startswith ('mypy-' ):
613
638
prefix = '%s: [%s]' % (file_read , name )
614
- updates , report_dirs = parse_section (prefix , options , section )
639
+ updates , report_dirs , hook_funcs = parse_section (prefix , options , section )
615
640
if report_dirs :
616
641
print ("%s: Per-module sections should not specify reports (%s)" %
617
642
(prefix , ', ' .join (s + '_report' for s in sorted (report_dirs ))),
618
643
file = sys .stderr )
644
+ if hook_funcs :
645
+ print ("%s: Per-module sections should not specify hooks (%s)" %
646
+ (prefix , ', ' .join (sorted (hook_funcs ))),
647
+ file = sys .stderr )
619
648
if set (updates ) - Options .PER_MODULE_OPTIONS :
620
649
print ("%s: Per-module sections should only specify per-module flags (%s)" %
621
650
(prefix , ', ' .join (sorted (set (updates ) - Options .PER_MODULE_OPTIONS ))),
@@ -632,16 +661,27 @@ def parse_config_file(options: Options, filename: Optional[str]) -> None:
632
661
633
662
634
663
def parse_section (prefix : str , template : Options ,
635
- section : Mapping [str , str ]) -> Tuple [Dict [str , object ], Dict [str , str ]]:
664
+ section : Mapping [str , str ]) -> Tuple [Dict [str , object ],
665
+ Dict [str , str ], Dict [str , str ]]:
636
666
"""Parse one section of a config file.
637
667
638
668
Returns a dict of option values encountered, and a dict of report directories.
639
669
"""
640
670
results = {} # type: Dict[str, object]
641
671
report_dirs = {} # type: Dict[str, str]
672
+ hook_funcs = {} # type: Dict[str, str]
642
673
for key in section :
643
674
key = key .replace ('-' , '_' )
644
- if key in config_types :
675
+ if key .startswith ('hooks.' ):
676
+ dv = section .get (key )
677
+ key = key [6 :]
678
+ if not hasattr (hooks , key ):
679
+ print ("%s: Unrecognized hook: %s = %s" % (prefix , key , dv ),
680
+ file = sys .stderr )
681
+ else :
682
+ hook_funcs [key ] = dv
683
+ continue
684
+ elif key in config_types :
645
685
ct = config_types [key ]
646
686
else :
647
687
dv = getattr (template , key , None )
@@ -685,7 +725,7 @@ def parse_section(prefix: str, template: Options,
685
725
if 'follow_imports' not in results :
686
726
results ['follow_imports' ] = 'error'
687
727
results [key ] = v
688
- return results , report_dirs
728
+ return results , report_dirs , hook_funcs
689
729
690
730
691
731
def fail (msg : str ) -> None :
0 commit comments