public async Task HandleAnnotatedExpressionAsync(ExpressionWithAnnotation expr, IMember value, CancellationToken cancellationToken = default) { if (expr?.Annotation == null) { return; } var variableType = await Eval.GetTypeFromAnnotationAsync(expr.Annotation, cancellationToken); // If value is null, then this is a pure declaration like // x: List[str] // without a value. If value is provided, then this is // x: List[str] = [...] // Check value type for compatibility IMember instance = null; if (value != null) { var valueType = value.GetPythonType(); if (!variableType.IsUnknown() && !valueType.Equals(variableType)) { // TODO: warn incompatible value type. // TODO: verify values. Value may be list() while variable type is List[str]. // Leave it as variable type. } else { instance = value; } } instance = instance ?? variableType?.CreateInstance(variableType.Name, Eval.GetLoc(expr.Expression), ArgumentSet.Empty) ?? Eval.UnknownType; if (expr.Expression is NameExpression ne) { Eval.DeclareVariable(ne.Name, instance, expr.Expression); return; } if (expr.Expression is MemberExpression m) { // self.x : int = 42 var self = Eval.LookupNameInScopes("self", out var scope); var argType = self?.GetPythonType(); if (argType is PythonClassType cls && scope != null) { cls.AddMember(m.Name, instance, true); } } }
public override async Task EvaluateAsync(CancellationToken cancellationToken = default) { if (SymbolTable.ReplacedByStubs.Contains(Target)) { return; } cancellationToken.ThrowIfCancellationRequested(); // Process annotations. var annotationType = await Eval.GetTypeFromAnnotationAsync(FunctionDefinition.ReturnAnnotation, cancellationToken); if (!annotationType.IsUnknown()) { // Annotations are typically types while actually functions return // instances unless specifically annotated to a type such as Type[T]. var instance = annotationType.CreateInstance(annotationType.Name, Eval.GetLoc(FunctionDefinition), ArgumentSet.Empty); _overload.SetReturnValue(instance, true); } else { // Check if function is a generator var suite = FunctionDefinition.Body as SuiteStatement; var yieldExpr = suite?.Statements.OfType <ExpressionStatement>().Select(s => s.Expression as YieldExpression).ExcludeDefault().FirstOrDefault(); if (yieldExpr != null) { // Function return is an iterator var yieldValue = await Eval.GetValueFromExpressionAsync(yieldExpr.Expression, cancellationToken) ?? Eval.UnknownType; var returnValue = new PythonGenerator(Eval.Interpreter, yieldValue); _overload.SetReturnValue(returnValue, true); } } using (Eval.OpenScope(FunctionDefinition, out _)) { await DeclareParametersAsync(cancellationToken); if (annotationType.IsUnknown() || Module.ModuleType == ModuleType.User) { // Return type from the annotation is sufficient for libraries // and stubs, no need to walk the body. if (FunctionDefinition.Body != null && Module.ModuleType != ModuleType.Specialized) { await FunctionDefinition.Body.WalkAsync(this, cancellationToken); } } } }
public async Task EvaluateClassAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); // Open class scope chain using (Eval.OpenScope(_classDef, out var outerScope)) { var instance = Eval.GetInScope(_classDef.Name, outerScope); if (!(instance?.GetPythonType() is PythonClassType classInfo)) { if (instance != null) { // TODO: warning that variable is already declared of a different type. } // May be odd case like class inside a class. return; } // Evaluate inner classes, if any await EvaluateInnerClassesAsync(_classDef, cancellationToken); _class = classInfo; // Set bases to the class. var bases = new List <IPythonType>(); foreach (var a in _classDef.Bases.Where(a => string.IsNullOrEmpty(a.Name))) { // We cheat slightly and treat base classes as annotations. var b = await Eval.GetTypeFromAnnotationAsync(a.Expression, cancellationToken); if (b != null) { bases.Add(b.GetPythonType()); } } _class.SetBases(Interpreter, bases); // Declare __class__ variable in the scope. Eval.DeclareVariable("__class__", _class, _classDef); await ProcessClassBody(cancellationToken); } }
private async Task DeclareParametersAsync(CancellationToken cancellationToken = default) { // For class method no need to add extra parameters, but first parameter type should be the class. // For static and unbound methods do not add or set anything. // For regular bound methods add first parameter and set it to the class. var parameters = new List <ParameterInfo>(); var skip = 0; if (_self != null && _function.HasClassFirstArgument()) { var p0 = FunctionDefinition.Parameters.FirstOrDefault(); if (p0 != null && !string.IsNullOrEmpty(p0.Name)) { // Actual parameter type will be determined when method is invoked. // The reason is that if method might be called on a derived class. // Declare self or cls in this scope. Eval.DeclareVariable(p0.Name, _self, p0.NameExpression); // Set parameter info. var pi = new ParameterInfo(Ast, p0, _self); pi.SetType(_self); parameters.Add(pi); skip++; } } // Declare parameters in scope for (var i = skip; i < FunctionDefinition.Parameters.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); var p = FunctionDefinition.Parameters[i]; if (!string.IsNullOrEmpty(p.Name)) { // If parameter has default value, look for the annotation locally first // since outer type may be getting redefined. Consider 's = None; def f(s: s = 123): ... IPythonType paramType = null; if (p.DefaultValue != null) { paramType = await Eval.GetTypeFromAnnotationAsync(p.Annotation, cancellationToken, LookupOptions.Local | LookupOptions.Builtins); if (paramType == null) { var defaultValue = await Eval.GetValueFromExpressionAsync(p.DefaultValue, cancellationToken); if (!defaultValue.IsUnknown()) { paramType = defaultValue.GetPythonType(); } } } // If all else fails, look up globally. paramType = paramType ?? await Eval.GetTypeFromAnnotationAsync(p.Annotation, cancellationToken); var pi = new ParameterInfo(Ast, p, paramType); await DeclareParameterAsync(p, i, pi, cancellationToken); parameters.Add(pi); } } _overload.SetParameters(parameters); }