@@ -409,32 +409,43 @@ def has_no_attr(
409
409
if not self .are_type_names_disabled ():
410
410
failed = False
411
411
if isinstance (original_type , Instance ) and original_type .type .names :
412
- alternatives = set (original_type .type .names .keys ())
413
-
414
- if module_symbol_table is not None :
415
- alternatives |= {key for key in module_symbol_table .keys ()}
416
-
417
- # in some situations, the member is in the alternatives set
418
- # but since we're in this function, we shouldn't suggest it
419
- if member in alternatives :
420
- alternatives .remove (member )
421
-
422
- matches = [m for m in COMMON_MISTAKES .get (member , []) if m in alternatives ]
423
- matches .extend (best_matches (member , alternatives )[:3 ])
424
- if member == "__aiter__" and matches == ["__iter__" ]:
425
- matches = [] # Avoid misleading suggestion
426
- if matches :
412
+ if (
413
+ module_symbol_table is not None
414
+ and member in module_symbol_table
415
+ and not module_symbol_table [member ].module_public
416
+ ):
427
417
self .fail (
428
- '{} has no attribute "{}"; maybe {}?{}' .format (
429
- format_type (original_type ),
430
- member ,
431
- pretty_seq (matches , "or" ),
432
- extra ,
433
- ),
418
+ f"{ format_type (original_type , module_names = True )} does not "
419
+ f'explicitly export attribute "{ member } "' ,
434
420
context ,
435
421
code = codes .ATTR_DEFINED ,
436
422
)
437
423
failed = True
424
+ else :
425
+ alternatives = set (original_type .type .names .keys ())
426
+ if module_symbol_table is not None :
427
+ alternatives |= {
428
+ k for k , v in module_symbol_table .items () if v .module_public
429
+ }
430
+ # Rare but possible, see e.g. testNewAnalyzerCyclicDefinitionCrossModule
431
+ alternatives .discard (member )
432
+
433
+ matches = [m for m in COMMON_MISTAKES .get (member , []) if m in alternatives ]
434
+ matches .extend (best_matches (member , alternatives )[:3 ])
435
+ if member == "__aiter__" and matches == ["__iter__" ]:
436
+ matches = [] # Avoid misleading suggestion
437
+ if matches :
438
+ self .fail (
439
+ '{} has no attribute "{}"; maybe {}?{}' .format (
440
+ format_type (original_type ),
441
+ member ,
442
+ pretty_seq (matches , "or" ),
443
+ extra ,
444
+ ),
445
+ context ,
446
+ code = codes .ATTR_DEFINED ,
447
+ )
448
+ failed = True
438
449
if not failed :
439
450
self .fail (
440
451
'{} has no attribute "{}"{}' .format (
0 commit comments