(
self,
import_id: str,
source_id: str,
imported_id: str,
module_public: bool,
module_hidden: bool,
context: Node,
add_unknown_imported_symbol: bool = True,
)
| 3109 | ) |
| 3110 | |
| 3111 | def report_missing_module_attribute( |
| 3112 | self, |
| 3113 | import_id: str, |
| 3114 | source_id: str, |
| 3115 | imported_id: str, |
| 3116 | module_public: bool, |
| 3117 | module_hidden: bool, |
| 3118 | context: Node, |
| 3119 | add_unknown_imported_symbol: bool = True, |
| 3120 | ) -> None: |
| 3121 | # Missing attribute. |
| 3122 | if self.is_incomplete_namespace(import_id): |
| 3123 | # We don't know whether the name will be there, since the namespace |
| 3124 | # is incomplete. Defer the current target. |
| 3125 | self.mark_incomplete( |
| 3126 | imported_id, context, module_public=module_public, module_hidden=module_hidden |
| 3127 | ) |
| 3128 | return |
| 3129 | message = f'Module "{import_id}" has no attribute "{source_id}"' |
| 3130 | # Suggest alternatives, if any match is found. |
| 3131 | module = self.modules.get(import_id) |
| 3132 | if module: |
| 3133 | if source_id in module.names.keys() and not module.names[source_id].module_public: |
| 3134 | message = ( |
| 3135 | f'Module "{import_id}" does not explicitly export attribute "{source_id}"' |
| 3136 | ) |
| 3137 | elif not ( |
| 3138 | self.options.ignore_errors |
| 3139 | or self.cur_mod_node.path in self.errors.ignored_files |
| 3140 | or self.errors.prefer_simple_messages() |
| 3141 | ): |
| 3142 | alternatives = set(module.names.keys()).difference({source_id}) |
| 3143 | matches = best_matches(source_id, alternatives, n=3) |
| 3144 | if matches: |
| 3145 | suggestion = f"; maybe {pretty_seq(matches, 'or')}?" |
| 3146 | message += f"{suggestion}" |
| 3147 | self.fail(message, context, code=codes.ATTR_DEFINED) |
| 3148 | if add_unknown_imported_symbol: |
| 3149 | self.add_unknown_imported_symbol( |
| 3150 | imported_id, |
| 3151 | context, |
| 3152 | target_name=None, |
| 3153 | module_public=module_public, |
| 3154 | module_hidden=not module_public, |
| 3155 | ) |
| 3156 | |
| 3157 | if import_id == "typing": |
| 3158 | # The user probably has a missing definition in a test fixture. Let's verify. |
| 3159 | fullname = f"builtins.{source_id.lower()}" |
| 3160 | if ( |
| 3161 | self.lookup_fully_qualified_or_none(fullname) is None |
| 3162 | and fullname in SUGGESTED_TEST_FIXTURES |
| 3163 | ): |
| 3164 | # Yes. Generate a helpful note. |
| 3165 | self.msg.add_fixture_note(fullname, context) |
| 3166 | else: |
| 3167 | typing_extensions = self.modules.get("typing_extensions") |
| 3168 | if typing_extensions and source_id in typing_extensions.names: |
no test coverage detected