##// END OF EJS Templates
Merge pull request #12282 from Carreau/completer-typing
Matthias Bussonnier -
r25717:4999ce9c merge
parent child Browse files
Show More
@@ -0,0 +1,4 b''
1 [mypy]
2 python_version = 3.6
3 ignore_missing_imports = True
4 follow_imports = silent
@@ -40,6 +40,7 b' install:'
40 40 - pip install trio curio --upgrade --upgrade-strategy eager
41 41 - pip install pytest 'matplotlib !=3.2.0' mypy
42 42 - pip install codecov check-manifest --upgrade
43 - pip install mypy
43 44
44 45 script:
45 46 - check-manifest
@@ -50,7 +51,8 b' script:'
50 51 fi
51 52 - cd /tmp && iptest --coverage xml && cd -
52 53 - pytest IPython
53 - mypy --ignore-missing-imports -m IPython.terminal.ptutils
54 - mypy IPython/terminal/ptutils.py
55 - mypy IPython/core/c*.py
54 56 # On the latest Python (on Linux) only, make sure that the docs build.
55 57 - |
56 58 if [[ "$TRAVIS_PYTHON_VERSION" == "3.7" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
@@ -126,7 +126,7 b' import warnings'
126 126 from contextlib import contextmanager
127 127 from importlib import import_module
128 128 from types import SimpleNamespace
129 from typing import Iterable, Iterator, List, Tuple
129 from typing import Iterable, Iterator, List, Tuple, Union, Any, Sequence, Dict, NamedTuple, Pattern, Optional
130 130
131 131 from IPython.core.error import TryNext
132 132 from IPython.core.inputtransformer2 import ESC_MAGIC
@@ -156,6 +156,14 b' except ImportError:'
156 156 # Globals
157 157 #-----------------------------------------------------------------------------
158 158
159 # ranges where we have most of the valid unicode names. We could be more finer
160 # grained but is it worth it for performace While unicode have character in the
161 # rage 0, 0x110000, we seem to have name for about 10% of those. (131808 as I
162 # write this). With below range we cover them all, with a density of ~67%
163 # biggest next gap we consider only adds up about 1% density and there are 600
164 # gaps that would need hard coding.
165 _UNICODE_RANGES = [(32, 0x2fa1e), (0xe0001, 0xe01f0)]
166
159 167 # Public API
160 168 __all__ = ['Completer','IPCompleter']
161 169
@@ -745,7 +753,7 b' def get__all__entries(obj):'
745 753 return [w for w in words if isinstance(w, str)]
746 754
747 755
748 def match_dict_keys(keys: List[str], prefix: str, delims: str):
756 def match_dict_keys(keys: List[Union[str, bytes]], prefix: str, delims: str) -> Tuple[str, int, List[str]]:
749 757 """Used by dict_key_matches, matching the prefix to a list of keys
750 758
751 759 Parameters
@@ -766,22 +774,25 b' def match_dict_keys(keys: List[str], prefix: str, delims: str):'
766 774 ``matches`` a list of replacement/completion
767 775
768 776 """
777 keys = [k for k in keys if isinstance(k, (str, bytes))]
769 778 if not prefix:
770 return None, 0, [repr(k) for k in keys
779 return '', 0, [repr(k) for k in keys
771 780 if isinstance(k, (str, bytes))]
772 781 quote_match = re.search('["\']', prefix)
782 assert quote_match is not None # silence mypy
773 783 quote = quote_match.group()
774 784 try:
775 785 prefix_str = eval(prefix + quote, {})
776 786 except Exception:
777 return None, 0, []
787 return '', 0, []
778 788
779 789 pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
780 790 token_match = re.search(pattern, prefix, re.UNICODE)
791 assert token_match is not None # silence mypy
781 792 token_start = token_match.start()
782 793 token_prefix = token_match.group()
783 794
784 matched = []
795 matched:List[str] = []
785 796 for key in keys:
786 797 try:
787 798 if not key.startswith(prefix_str):
@@ -794,14 +805,6 b' def match_dict_keys(keys: List[str], prefix: str, delims: str):'
794 805 rem = key[len(prefix_str):]
795 806 # force repr wrapped in '
796 807 rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
797 if rem_repr.startswith('u') and prefix[0] not in 'uU':
798 # Found key is unicode, but prefix is Py2 string.
799 # Therefore attempt to interpret key as string.
800 try:
801 rem_repr = repr(rem.encode('ascii') + '"')
802 except UnicodeEncodeError:
803 continue
804
805 808 rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
806 809 if quote == '"':
807 810 # The entered prefix is quoted with ",
@@ -887,9 +890,8 b' def _safe_isinstance(obj, module, class_name):'
887 890 return (module in sys.modules and
888 891 isinstance(obj, getattr(import_module(module), class_name)))
889 892
890
891 def back_unicode_name_matches(text):
892 u"""Match unicode characters back to unicode name
893 def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:
894 """Match Unicode characters back to Unicode name
893 895
894 896 This does ``☃`` -> ``\\snowman``
895 897
@@ -898,52 +900,60 b' def back_unicode_name_matches(text):'
898 900
899 901 This will not either back-complete standard sequences like \\n, \\b ...
900 902
901 Used on Python 3 only.
903 Returns
904 =======
905
906 Return a tuple with two elements:
907
908 - The Unicode character that was matched (preceded with a backslash), or
909 empty string,
910 - a sequence (of 1), name for the match Unicode character, preceded by
911 backslash, or empty if no match.
912
902 913 """
903 914 if len(text)<2:
904 return u'', ()
915 return '', ()
905 916 maybe_slash = text[-2]
906 917 if maybe_slash != '\\':
907 return u'', ()
918 return '', ()
908 919
909 920 char = text[-1]
910 921 # no expand on quote for completion in strings.
911 922 # nor backcomplete standard ascii keys
912 if char in string.ascii_letters or char in ['"',"'"]:
913 return u'', ()
923 if char in string.ascii_letters or char in ('"',"'"):
924 return '', ()
914 925 try :
915 926 unic = unicodedata.name(char)
916 return '\\'+char,['\\'+unic]
927 return '\\'+char,('\\'+unic,)
917 928 except KeyError:
918 929 pass
919 return u'', ()
930 return '', ()
920 931
921 def back_latex_name_matches(text:str):
932 def back_latex_name_matches(text:str) -> Tuple[str, Sequence[str]] :
922 933 """Match latex characters back to unicode name
923 934
924 935 This does ``\\ℵ`` -> ``\\aleph``
925 936
926 Used on Python 3 only.
927 937 """
928 938 if len(text)<2:
929 return u'', ()
939 return '', ()
930 940 maybe_slash = text[-2]
931 941 if maybe_slash != '\\':
932 return u'', ()
942 return '', ()
933 943
934 944
935 945 char = text[-1]
936 946 # no expand on quote for completion in strings.
937 947 # nor backcomplete standard ascii keys
938 if char in string.ascii_letters or char in ['"',"'"]:
939 return u'', ()
948 if char in string.ascii_letters or char in ('"',"'"):
949 return '', ()
940 950 try :
941 951 latex = reverse_latex_symbol[char]
942 952 # '\\' replace the \ as well
943 953 return '\\'+char,[latex]
944 954 except KeyError:
945 955 pass
946 return u'', ()
956 return '', ()
947 957
948 958
949 959 def _formatparamchildren(parameter) -> str:
@@ -1002,9 +1012,19 b' def _make_signature(completion)-> str:'
1002 1012 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
1003 1013 for p in signature.defined_names()) if f])
1004 1014
1015
1016 class _CompleteResult(NamedTuple):
1017 matched_text : str
1018 matches: Sequence[str]
1019 matches_origin: Sequence[str]
1020 jedi_matches: Any
1021
1022
1005 1023 class IPCompleter(Completer):
1006 1024 """Extension of the completer class with IPython-specific features"""
1007 1025
1026 __dict_key_regexps: Optional[Dict[bool,Pattern]] = None
1027
1008 1028 @observe('greedy')
1009 1029 def _greedy_changed(self, change):
1010 1030 """update the splitter and readline delims when greedy is changed"""
@@ -1143,7 +1163,7 b' class IPCompleter(Completer):'
1143 1163 self._unicode_names = None
1144 1164
1145 1165 @property
1146 def matchers(self):
1166 def matchers(self) -> List[Any]:
1147 1167 """All active matcher routines for completion"""
1148 1168 if self.dict_keys_only:
1149 1169 return [self.dict_key_matches]
@@ -1165,7 +1185,7 b' class IPCompleter(Completer):'
1165 1185 self.dict_key_matches,
1166 1186 ]
1167 1187
1168 def all_completions(self, text) -> List[str]:
1188 def all_completions(self, text:str) -> List[str]:
1169 1189 """
1170 1190 Wrapper around the completion methods for the benefit of emacs.
1171 1191 """
@@ -1176,14 +1196,14 b' class IPCompleter(Completer):'
1176 1196
1177 1197 return self.complete(text)[1]
1178 1198
1179 def _clean_glob(self, text):
1199 def _clean_glob(self, text:str):
1180 1200 return self.glob("%s*" % text)
1181 1201
1182 def _clean_glob_win32(self,text):
1202 def _clean_glob_win32(self, text:str):
1183 1203 return [f.replace("\\","/")
1184 1204 for f in self.glob("%s*" % text)]
1185 1205
1186 def file_matches(self, text):
1206 def file_matches(self, text:str)->List[str]:
1187 1207 """Match filenames, expanding ~USER type strings.
1188 1208
1189 1209 Most of the seemingly convoluted logic in this completer is an
@@ -1265,7 +1285,7 b' class IPCompleter(Completer):'
1265 1285 # Mark directories in input list by appending '/' to their names.
1266 1286 return [x+'/' if os.path.isdir(x) else x for x in matches]
1267 1287
1268 def magic_matches(self, text):
1288 def magic_matches(self, text:str):
1269 1289 """Match magics"""
1270 1290 # Get all shell magics now rather than statically, so magics loaded at
1271 1291 # runtime show up too.
@@ -1356,7 +1376,7 b' class IPCompleter(Completer):'
1356 1376 if color.startswith(prefix) ]
1357 1377 return []
1358 1378
1359 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str):
1379 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str) -> Iterable[Any]:
1360 1380 """
1361 1381
1362 1382 Return a list of :any:`jedi.api.Completions` object from a ``text`` and
@@ -1430,7 +1450,7 b' class IPCompleter(Completer):'
1430 1450 else:
1431 1451 return []
1432 1452
1433 def python_matches(self, text):
1453 def python_matches(self, text:str)->List[str]:
1434 1454 """Match attributes or global python names"""
1435 1455 if "." in text:
1436 1456 try:
@@ -1512,7 +1532,7 b' class IPCompleter(Completer):'
1512 1532
1513 1533 return list(set(ret))
1514 1534
1515 def python_func_kw_matches(self,text):
1535 def python_func_kw_matches(self, text):
1516 1536 """Match named parameters (kwargs) of the last open function"""
1517 1537
1518 1538 if "." in text: # a parameter cannot be dotted
@@ -1582,36 +1602,39 b' class IPCompleter(Completer):'
1582 1602 # Remove used named arguments from the list, no need to show twice
1583 1603 for namedArg in set(namedArgs) - usedNamedArgs:
1584 1604 if namedArg.startswith(text):
1585 argMatches.append(u"%s=" %namedArg)
1605 argMatches.append("%s=" %namedArg)
1586 1606 except:
1587 1607 pass
1588 1608
1589 1609 return argMatches
1590 1610
1591 def dict_key_matches(self, text):
1611 @staticmethod
1612 def _get_keys(obj: Any) -> List[Any]:
1613 # Objects can define their own completions by defining an
1614 # _ipy_key_completions_() method.
1615 method = get_real_method(obj, '_ipython_key_completions_')
1616 if method is not None:
1617 return method()
1618
1619 # Special case some common in-memory dict-like types
1620 if isinstance(obj, dict) or\
1621 _safe_isinstance(obj, 'pandas', 'DataFrame'):
1622 try:
1623 return list(obj.keys())
1624 except Exception:
1625 return []
1626 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
1627 _safe_isinstance(obj, 'numpy', 'void'):
1628 return obj.dtype.names or []
1629 return []
1630
1631 def dict_key_matches(self, text:str) -> List[str]:
1592 1632 "Match string keys in a dictionary, after e.g. 'foo[' "
1593 def get_keys(obj):
1594 # Objects can define their own completions by defining an
1595 # _ipy_key_completions_() method.
1596 method = get_real_method(obj, '_ipython_key_completions_')
1597 if method is not None:
1598 return method()
1599
1600 # Special case some common in-memory dict-like types
1601 if isinstance(obj, dict) or\
1602 _safe_isinstance(obj, 'pandas', 'DataFrame'):
1603 try:
1604 return list(obj.keys())
1605 except Exception:
1606 return []
1607 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
1608 _safe_isinstance(obj, 'numpy', 'void'):
1609 return obj.dtype.names or []
1610 return []
1611 1633
1612 try:
1634
1635 if self.__dict_key_regexps is not None:
1613 1636 regexps = self.__dict_key_regexps
1614 except AttributeError:
1637 else:
1615 1638 dict_key_re_fmt = r'''(?x)
1616 1639 ( # match dict-referring expression wrt greedy setting
1617 1640 %s
@@ -1651,7 +1674,7 b' class IPCompleter(Completer):'
1651 1674 except Exception:
1652 1675 return []
1653 1676
1654 keys = get_keys(obj)
1677 keys = self._get_keys(obj)
1655 1678 if not keys:
1656 1679 return keys
1657 1680 closing_quote, token_offset, matches = match_dict_keys(keys, prefix, self.splitter.delims)
@@ -1696,16 +1719,15 b' class IPCompleter(Completer):'
1696 1719
1697 1720 return [leading + k + suf for k in matches]
1698 1721
1699 def unicode_name_matches(self, text):
1700 u"""Match Latex-like syntax for unicode characters base
1722 @staticmethod
1723 def unicode_name_matches(text:str) -> Tuple[str, List[str]] :
1724 """Match Latex-like syntax for unicode characters base
1701 1725 on the name of the character.
1702 1726
1703 1727 This does ``\\GREEK SMALL LETTER ETA`` -> ``η``
1704 1728
1705 1729 Works only on valid python 3 identifier, or on combining characters that
1706 1730 will combine to form a valid identifier.
1707
1708 Used on Python 3 only.
1709 1731 """
1710 1732 slashpos = text.rfind('\\')
1711 1733 if slashpos > -1:
@@ -1717,11 +1739,11 b' class IPCompleter(Completer):'
1717 1739 return '\\'+s,[unic]
1718 1740 except KeyError:
1719 1741 pass
1720 return u'', []
1742 return '', []
1721 1743
1722 1744
1723 def latex_matches(self, text):
1724 u"""Match Latex syntax for unicode characters.
1745 def latex_matches(self, text:str) -> Tuple[str, Sequence[str]]:
1746 """Match Latex syntax for unicode characters.
1725 1747
1726 1748 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
1727 1749 """
@@ -1738,7 +1760,7 b' class IPCompleter(Completer):'
1738 1760 matches = [k for k in latex_symbols if k.startswith(s)]
1739 1761 if matches:
1740 1762 return s, matches
1741 return u'', []
1763 return '', ()
1742 1764
1743 1765 def dispatch_custom_completer(self, text):
1744 1766 if not self.custom_completers:
@@ -1839,6 +1861,7 b' class IPCompleter(Completer):'
1839 1861 category=ProvisionalCompleterWarning, stacklevel=2)
1840 1862
1841 1863 seen = set()
1864 profiler:Optional[cProfile.Profile]
1842 1865 try:
1843 1866 if self.profile_completions:
1844 1867 import cProfile
@@ -1864,7 +1887,7 b' class IPCompleter(Completer):'
1864 1887 print("Writing profiler output to", output_path)
1865 1888 profiler.dump_stats(output_path)
1866 1889
1867 def _completions(self, full_text: str, offset: int, *, _timeout)->Iterator[Completion]:
1890 def _completions(self, full_text: str, offset: int, *, _timeout) -> Iterator[Completion]:
1868 1891 """
1869 1892 Core completion module.Same signature as :any:`completions`, with the
1870 1893 extra `timeout` parameter (in seconds).
@@ -1949,7 +1972,7 b' class IPCompleter(Completer):'
1949 1972 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
1950 1973
1951 1974
1952 def complete(self, text=None, line_buffer=None, cursor_pos=None):
1975 def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
1953 1976 """Find completions for the given text and line context.
1954 1977
1955 1978 Note that both the text and the line_buffer are optional, but at least
@@ -1973,9 +1996,9 b' class IPCompleter(Completer):'
1973 1996
1974 1997 Returns
1975 1998 -------
1999 Tuple of two items:
1976 2000 text : str
1977 2001 Text that was actually used in the completion.
1978
1979 2002 matches : list
1980 2003 A list of completion matches.
1981 2004
@@ -1995,7 +2018,7 b' class IPCompleter(Completer):'
1995 2018 return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2]
1996 2019
1997 2020 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
1998 full_text=None) -> Tuple[str, List[str], List[str], Iterable[_FakeJediCompletion]]:
2021 full_text=None) -> _CompleteResult:
1999 2022 """
2000 2023
2001 2024 Like complete but can also returns raw jedi completions as well as the
@@ -2008,8 +2031,19 b' class IPCompleter(Completer):'
2008 2031 caller) as the offset in the ``text`` or ``line_buffer``, or as the
2009 2032 ``column`` when passing multiline strings this could/should be renamed
2010 2033 but would add extra noise.
2034
2035 Return
2036 ======
2037
2038 A tuple of N elements which are (likely):
2039
2040 matched_text: ? the text that the complete matched
2041 matches: list of completions ?
2042 matches_origin: ? list same lenght as matches, and where each completion came from
2043 jedi_matches: list of Jedi matches, have it's own structure.
2011 2044 """
2012 2045
2046
2013 2047 # if the cursor position isn't given, the only sane assumption we can
2014 2048 # make is that it's at the end of the line (the common case)
2015 2049 if cursor_pos is None:
@@ -2027,17 +2061,16 b' class IPCompleter(Completer):'
2027 2061 if self.backslash_combining_completions:
2028 2062 # allow deactivation of these on windows.
2029 2063 base_text = text if not line_buffer else line_buffer[:cursor_pos]
2030 latex_text, latex_matches = self.latex_matches(base_text)
2031 if latex_matches:
2032 return latex_text, latex_matches, ['latex_matches']*len(latex_matches), ()
2033 name_text = ''
2034 name_matches = []
2035 # need to add self.fwd_unicode_match() function here when done
2036 for meth in (self.unicode_name_matches, back_latex_name_matches, back_unicode_name_matches, self.fwd_unicode_match):
2064
2065 for meth in (self.latex_matches,
2066 self.unicode_name_matches,
2067 back_latex_name_matches,
2068 back_unicode_name_matches,
2069 self.fwd_unicode_match):
2037 2070 name_text, name_matches = meth(base_text)
2038 2071 if name_text:
2039 return name_text, name_matches[:MATCHES_LIMIT], \
2040 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ()
2072 return _CompleteResult(name_text, name_matches[:MATCHES_LIMIT], \
2073 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ())
2041 2074
2042 2075
2043 2076 # If no line buffer is given, assume the input text is all there was
@@ -2052,7 +2085,7 b' class IPCompleter(Completer):'
2052 2085 matches = list(matcher(line_buffer))[:MATCHES_LIMIT]
2053 2086 if matches:
2054 2087 origins = [matcher.__qualname__] * len(matches)
2055 return text, matches, origins, ()
2088 return _CompleteResult(text, matches, origins, ())
2056 2089
2057 2090 # Start with a clean slate of completions
2058 2091 matches = []
@@ -2061,13 +2094,13 b' class IPCompleter(Completer):'
2061 2094 # different types of objects. The rlcomplete() method could then
2062 2095 # simply collapse the dict into a list for readline, but we'd have
2063 2096 # richer completion semantics in other environments.
2064 completions = ()
2097 completions:Iterable[Any] = []
2065 2098 if self.use_jedi:
2066 2099 if not full_text:
2067 2100 full_text = line_buffer
2068 2101 completions = self._jedi_matches(
2069 2102 cursor_pos, cursor_line, full_text)
2070
2103
2071 2104 if self.merge_completions:
2072 2105 matches = []
2073 2106 for matcher in self.matchers:
@@ -2105,9 +2138,38 b' class IPCompleter(Completer):'
2105 2138
2106 2139 self.matches = _matches
2107 2140
2108 return text, _matches, origins, completions
2141 return _CompleteResult(text, _matches, origins, completions)
2109 2142
2110 def fwd_unicode_match(self, text:str) -> Tuple[str, list]:
2143 def fwd_unicode_match(self, text:str) -> Tuple[str, Sequence[str]]:
2144 """
2145
2146 Forward match a string starting with a backslash with a list of
2147 potential Unicode completions.
2148
2149 Will compute list list of Unicode character names on first call and cache it.
2150
2151 Return
2152 ======
2153
2154 At tuple with:
2155 - matched text (empty if no matches)
2156 - list of potential completions, empty tuple otherwise)
2157 """
2158 # TODO: self.unicode_names is here a list we traverse each time with ~100k elements.
2159 # We could do a faster match using a Trie.
2160
2161 # Using pygtrie the follwing seem to work:
2162
2163 # s = PrefixSet()
2164
2165 # for c in range(0,0x10FFFF + 1):
2166 # try:
2167 # s.add(unicodedata.name(chr(c)))
2168 # except ValueError:
2169 # pass
2170 # [''.join(k) for k in s.iter(prefix)]
2171
2172 # But need to be timed and adds an extra dependency.
2111 2173
2112 2174 slashpos = text.rfind('\\')
2113 2175 # if text starts with slash
@@ -2126,7 +2188,7 b' class IPCompleter(Completer):'
2126 2188
2127 2189 # if text does not start with slash
2128 2190 else:
2129 return u'', ()
2191 return '', ()
2130 2192
2131 2193 @property
2132 2194 def unicode_names(self) -> List[str]:
@@ -2141,6 +2203,16 b' class IPCompleter(Completer):'
2141 2203 names.append(unicodedata.name(chr(c)))
2142 2204 except ValueError:
2143 2205 pass
2144 self._unicode_names = names
2206 self._unicode_names = _unicode_name_compute(_UNICODE_RANGES)
2145 2207
2146 2208 return self._unicode_names
2209
2210 def _unicode_name_compute(ranges:List[Tuple[int,int]]) -> List[str]:
2211 names = []
2212 for start,stop in ranges:
2213 for c in range(start, stop) :
2214 try:
2215 names.append(unicodedata.name(chr(c)))
2216 except ValueError:
2217 pass
2218 return names
@@ -33,6 +33,25 b' from nose.tools import assert_in, assert_not_in'
33 33 # Test functions
34 34 # -----------------------------------------------------------------------------
35 35
36 def test_unicode_range():
37 """
38 Test that the ranges we test for unicode names give the same number of
39 results than testing the full length.
40 """
41 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
42
43 expected_list = _unicode_name_compute([(0, 0x110000)])
44 test = _unicode_name_compute(_UNICODE_RANGES)
45 len_exp = len(expected_list)
46 len_test = len(test)
47
48 # do not inline the len() or on error pytest will try to print the 130 000 +
49 # elements.
50 assert len_exp == len_test
51
52 # fail if new unicode symbols have been added.
53 assert len_exp <= 131808
54
36 55
37 56 @contextmanager
38 57 def greedy_completion():
@@ -212,9 +231,8 b' class TestCompleter(unittest.TestCase):'
212 231 keys = random.sample(latex_symbols.keys(), 10)
213 232 for k in keys:
214 233 text, matches = ip.complete(k)
215 nt.assert_equal(len(matches), 1)
216 234 nt.assert_equal(text, k)
217 nt.assert_equal(matches[0], latex_symbols[k])
235 nt.assert_equal(matches, [latex_symbols[k]])
218 236 # Test a more complex line
219 237 text, matches = ip.complete("print(\\alpha")
220 238 nt.assert_equal(text, "\\alpha")
@@ -231,7 +249,7 b' class TestCompleter(unittest.TestCase):'
231 249 ip = get_ipython()
232 250 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
233 251 nt.assert_equal(text, "")
234 nt.assert_equal(matches, [])
252 nt.assert_equal(matches, ())
235 253
236 254 def test_back_latex_completion(self):
237 255 ip = get_ipython()
@@ -244,14 +262,14 b' class TestCompleter(unittest.TestCase):'
244 262 ip = get_ipython()
245 263
246 264 name, matches = ip.complete("\\Ⅴ")
247 nt.assert_equal(matches, ["\\ROMAN NUMERAL FIVE"])
265 nt.assert_equal(matches, ("\\ROMAN NUMERAL FIVE",))
248 266
249 267 def test_forward_unicode_completion(self):
250 268 ip = get_ipython()
251 269
252 270 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
253 nt.assert_equal(len(matches), 1)
254 nt.assert_equal(matches[0], "")
271 nt.assert_equal(matches, ["Ⅴ"] ) # This is not a V
272 nt.assert_equal(matches, ["\u2164"] ) # same as above but explicit.
255 273
256 274 @nt.nottest # now we have a completion for \jmath
257 275 @decorators.knownfailureif(
@@ -5,6 +5,7 b' include setupbase.py'
5 5 include setupegg.py
6 6 include MANIFEST.in
7 7 include pytest.ini
8 include mypy.ini
8 9 include .mailmap
9 10
10 11 recursive-exclude tools *
General Comments 0
You need to be logged in to leave comments. Login now