Skip to content

Commit 70b2b19

Browse files
authored
[suggest] Rework max guesses and eliminate max args (python#7972)
Rework max guesses to be configurable and to throw out guesses past the limit instead of just hard failing. This might allow succeeding in more situations. Eliminate the maximum argument restriction since we no longer compute all of the options before looking at the number of possibilities.
1 parent 0da67dd commit 70b2b19

File tree

4 files changed

+31
-11
lines changed

4 files changed

+31
-11
lines changed

mypy/dmypy/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ def __init__(self, prog: str) -> None:
116116
help="Find callsites instead of suggesting a type")
117117
p.add_argument('--use-fixme', metavar='NAME', type=str,
118118
help="A dummy name to use instead of Any for types that can't be inferred")
119+
p.add_argument('--max-guesses', type=int,
120+
help="Set the maximum number of types to try for a function (default 64)")
119121

120122
hang_parser = p = subparsers.add_parser('hang', help="Hang for 100 seconds")
121123

@@ -370,7 +372,7 @@ def do_suggest(args: argparse.Namespace) -> None:
370372
response = request(args.status_file, 'suggest', function=args.function,
371373
json=args.json, callsites=args.callsites, no_errors=args.no_errors,
372374
no_any=args.no_any, flex_any=args.flex_any, try_text=args.try_text,
373-
use_fixme=args.use_fixme)
375+
use_fixme=args.use_fixme, max_guesses=args.max_guesses)
374376
check_output(response, verbose=False, junit_xml=None, perf_stats_file=None)
375377

376378

mypy/suggestions.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,9 @@ def __init__(self, fgmanager: FineGrainedBuildManager,
213213
no_any: bool = False,
214214
try_text: bool = False,
215215
flex_any: Optional[float] = None,
216-
use_fixme: Optional[str] = None) -> None:
216+
use_fixme: Optional[str] = None,
217+
max_guesses: Optional[int] = None
218+
) -> None:
217219
self.fgmanager = fgmanager
218220
self.manager = fgmanager.manager
219221
self.plugin = self.manager.plugin
@@ -227,7 +229,7 @@ def __init__(self, fgmanager: FineGrainedBuildManager,
227229
if no_any:
228230
self.flex_any = 1.0
229231

230-
self.max_guesses = 64
232+
self.max_guesses = max_guesses or 64
231233
self.use_fixme = use_fixme
232234

233235
def suggest(self, function: str) -> str:
@@ -362,8 +364,10 @@ def get_guesses(self, is_method: bool, base: CallableType, defaults: List[Option
362364
"""
363365
options = self.get_args(is_method, base, defaults, callsites, uses)
364366
options = [self.add_adjustments(tps) for tps in options]
365-
return [refine_callable(base, base.copy_modified(arg_types=list(x)))
366-
for x in itertools.product(*options)]
367+
368+
# Take the first `max_guesses` guesses.
369+
product = itertools.islice(itertools.product(*options), 0, self.max_guesses)
370+
return [refine_callable(base, base.copy_modified(arg_types=list(x))) for x in product]
367371

368372
def get_callsites(self, func: FuncDef) -> Tuple[List[Callsite], List[str]]:
369373
"""Find all call sites of a function."""
@@ -414,9 +418,6 @@ def get_suggestion(self, mod: str, node: FuncDef) -> PyAnnotateSignature:
414418

415419
is_method = bool(node.info) and not node.is_static
416420

417-
if len(node.arg_names) >= 10:
418-
raise SuggestionFailure("Too many arguments")
419-
420421
with strict_optional_set(graph[mod].options.strict_optional):
421422
guesses = self.get_guesses(
422423
is_method,
@@ -426,8 +427,6 @@ def get_suggestion(self, mod: str, node: FuncDef) -> PyAnnotateSignature:
426427
uses,
427428
)
428429
guesses = self.filter_options(guesses, is_method)
429-
if len(guesses) > self.max_guesses:
430-
raise SuggestionFailure("Too many possibilities!")
431430
best, _ = self.find_best(node, guesses)
432431

433432
# Now try to find the return type!

mypy/test/testfinegrained.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,13 @@ def maybe_suggest(self, step: int, server: Server, src: str, tmp_dir: str) -> Li
283283
flex_any = float(m.group(1)) if m else None
284284
m = re.match(r'--use-fixme=(\w+)', flags)
285285
use_fixme = m.group(1) if m else None
286+
m = re.match('--max-guesses=([0-9]+)', flags)
287+
max_guesses = int(m.group(1)) if m else None
286288
res = cast(Dict[str, Any],
287289
server.cmd_suggest(
288290
target.strip(), json=json, no_any=no_any, no_errors=no_errors,
289291
try_text=try_text, flex_any=flex_any, use_fixme=use_fixme,
290-
callsites=callsites))
292+
callsites=callsites, max_guesses=max_guesses))
291293
val = res['error'] if 'error' in res else res['out'] + res['err']
292294
if json:
293295
# JSON contains already escaped \ on Windows, so requires a bit of care.

test-data/unit/fine-grained-suggest.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,23 @@ bar.py:4: (arg=str)
4444
bar.py:6: (*typing.List[str])
4545
bar.py:8: (**typing.Dict[str, str])
4646

47+
[case testMaxGuesses]
48+
# suggest: foo.foo
49+
# suggest: --max-guesses=2 foo.foo
50+
[file foo.py]
51+
# The idea here is that we can only find the union type with more guesses.
52+
def foo(x, y):
53+
if not isinstance(x, int):
54+
x+'1'
55+
56+
foo(1, 2)
57+
foo('3', '4')
58+
[builtins fixtures/isinstancelist.pyi]
59+
[out]
60+
(Union[int, str], object) -> None
61+
(object, object) -> None
62+
==
63+
4764
[case testSuggestInferFunc1]
4865
# flags: --strict-optional
4966
# suggest: foo.foo

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy