This commit is contained in:
Maxim Kurnikov
2020-03-15 00:58:11 +03:00
parent 0b1507c81e
commit 1419b144d9
20 changed files with 513 additions and 321 deletions

View File

@@ -230,102 +230,3 @@ def add_symbol_table_node(api: SemanticAnalyzer,
return True
return False
class CreateNewManagerClassFrom_AsManager(helpers.DynamicClassPluginCallback):
def create_new_dynamic_class(self) -> None:
pass
def create_manager_class_from_as_manager_method(ctx: DynamicClassDefContext) -> None:
semanal_api = sem_helpers.get_semanal_api(ctx)
try:
queryset_info = resolve_callee_info_or_exception(ctx)
django_manager_info = resolve_django_manager_info_or_exception(ctx)
except sem_helpers.IncompleteDefnError:
if not semanal_api.final_iteration:
semanal_api.defer()
return
else:
raise
generic_param: MypyType = AnyType(TypeOfAny.explicit)
generic_param_name = 'Any'
if (semanal_api.scope.classes
and semanal_api.scope.classes[-1].has_base(fullnames.MODEL_CLASS_FULLNAME)):
info = semanal_api.scope.classes[-1] # type: TypeInfo
generic_param = Instance(info, [])
generic_param_name = info.name
new_manager_class_name = queryset_info.name + '_AsManager_' + generic_param_name
new_manager_info = helpers.new_typeinfo(new_manager_class_name,
bases=[Instance(django_manager_info, [generic_param])],
module_name=semanal_api.cur_mod_id)
new_manager_info.set_line(ctx.call)
record_new_manager_info_fullname_into_metadata(ctx,
new_manager_info.fullname,
django_manager_info,
queryset_info,
django_manager_info)
class_def_context = ClassDefContext(cls=new_manager_info.defn,
reason=ctx.call, api=semanal_api)
self_type = Instance(new_manager_info, [AnyType(TypeOfAny.explicit)])
try:
for name, method_node in iter_all_custom_queryset_methods(queryset_info):
sem_helpers.copy_method_or_incomplete_defn_exception(class_def_context,
self_type,
new_method_name=name,
method_node=method_node)
except sem_helpers.IncompleteDefnError:
if not semanal_api.final_iteration:
semanal_api.defer()
return
else:
raise
new_manager_sym = SymbolTableNode(GDEF, new_manager_info, plugin_generated=True)
# context=None - forcibly replace old node
added = add_symbol_table_node(semanal_api, new_manager_class_name, new_manager_sym,
context=None,
symbol_table=semanal_api.globals)
if added:
# replace all references to the old manager Var everywhere
for _, module in semanal_api.modules.items():
if module.fullname != semanal_api.cur_mod_id:
for sym_name, sym in module.names.items():
if sym.fullname == new_manager_info.fullname:
module.names[sym_name] = new_manager_sym.copy()
# we need another iteration to process methods
if (not added
and not semanal_api.final_iteration):
semanal_api.defer()
def instantiate_anonymous_queryset_from_as_manager(ctx: MethodContext) -> MypyType:
api = chk_helpers.get_typechecker_api(ctx)
django_manager_info = helpers.lookup_fully_qualified_typeinfo(api, fullnames.MANAGER_CLASS_FULLNAME)
assert django_manager_info is not None
assert isinstance(ctx.type, CallableType)
assert isinstance(ctx.type.ret_type, Instance)
queryset_info = ctx.type.ret_type.type
gen_name = django_manager_info.name + 'From' + queryset_info.name
gen_fullname = 'django.db.models.manager' + '.' + gen_name
metadata = get_generated_managers_metadata(django_manager_info)
if gen_fullname not in metadata:
raise ValueError(f'{gen_fullname!r} is not present in generated managers list')
module_name, _, class_name = metadata[gen_fullname].rpartition('.')
current_module = helpers.get_current_module(api)
assert module_name == current_module.fullname
generated_manager_info = current_module.names[class_name].node
assert isinstance(generated_manager_info, TypeInfo)
return Instance(generated_manager_info, [])