11# Copyright (c) 2010-2012 testtools developers. See LICENSE for details.
22
3- import sys
3+ from collections .abc import Callable
4+ from typing import Any , TypeVar
45
6+ T = TypeVar ("T" )
7+ K = TypeVar ("K" )
8+ V = TypeVar ("V" )
9+ R = TypeVar ("R" )
510
6- def try_import (name , alternative = None , error_callback = None ):
7- """Attempt to import a module, with a fallback.
811
9- Attempt to import ``name``. If it fails, return ``alternative``. When
10- supporting multiple versions of Python or optional dependencies, it is
11- useful to be able to try to import a module.
12-
13- :param name: The name of the object to import, e.g. ``os.path`` or
14- ``os.path.join``.
15- :param alternative: The value to return if no module can be imported.
16- Defaults to None.
17- :param error_callback: If non-None, a callable that is passed the
18- ImportError when the module cannot be loaded.
19- """
20- module_segments = name .split ("." )
21- last_error = None
22- remainder = []
23-
24- # module_name will be what successfully imports. We cannot walk from the
25- # __import__ result because in import loops (A imports A.B, which imports
26- # C, which calls try_import("A.B")) A.B will not yet be set.
27- while module_segments :
28- module_name = "." .join (module_segments )
29- try :
30- __import__ (module_name )
31- except ImportError :
32- last_error = sys .exc_info ()[1 ]
33- remainder .append (module_segments .pop ())
34- continue
35- else :
36- break
37- else :
38- if last_error is not None and error_callback is not None :
39- error_callback (last_error )
40- return alternative
41-
42- module = sys .modules [module_name ]
43- nonexistent = object ()
44- for segment in reversed (remainder ):
45- module = getattr (module , segment , nonexistent )
46- if module is nonexistent :
47- if last_error is not None and error_callback is not None :
48- error_callback (last_error )
49- return alternative
50-
51- return module
52-
53-
54- def map_values (function , dictionary ):
12+ def map_values (function : Callable [[V ], R ], dictionary : dict [K , V ]) -> dict [K , R ]:
5513 """Map ``function`` across the values of ``dictionary``.
5614
5715 :return: A dict with the same keys as ``dictionary``, where the value
@@ -60,17 +18,17 @@ def map_values(function, dictionary):
6018 return {k : function (dictionary [k ]) for k in dictionary }
6119
6220
63- def filter_values (function , dictionary ) :
21+ def filter_values (function : Callable [[ V ], bool ], dictionary : dict [ K , V ]) -> dict [ K , V ] :
6422 """Filter ``dictionary`` by its values using ``function``."""
6523 return {k : v for k , v in dictionary .items () if function (v )}
6624
6725
68- def dict_subtract (a , b ) :
26+ def dict_subtract (a : dict [ K , V ], b : dict [ K , Any ]) -> dict [ K , V ] :
6927 """Return the part of ``a`` that's not in ``b``."""
7028 return {k : a [k ] for k in set (a ) - set (b )}
7129
7230
73- def list_subtract (a , b ) :
31+ def list_subtract (a : list [ T ] , b : list [ T ]) -> list [ T ] :
7432 """Return a list ``a`` without the elements of ``b``.
7533
7634 If a particular value is in ``a`` twice and ``b`` once then the returned
0 commit comments