Skip to content

Commit

Permalink
Added support for several "type" metadata fields that are accessible …
Browse files Browse the repository at this point in the history
…on classes that derive from type. This includes `__subclasses__`, `__module__`, etc.
  • Loading branch information
msfterictraut committed Jun 25, 2020
1 parent 44f32e8 commit 0c3a917
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 3 deletions.
13 changes: 12 additions & 1 deletion server/src/analyzer/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,10 +331,21 @@ export class Binder extends ParseTreeWalker {
// Note that __class__, __dict__ and __doc__ are skipped here
// because the builtins.pyi type stub declares these in the
// 'object' class.
this._addBuiltInSymbolToCurrentScope('__base__', node, 'Any');
this._addBuiltInSymbolToCurrentScope('__bases__', node, 'Any');
this._addBuiltInSymbolToCurrentScope('__basicsize__', node, 'int');
this._addBuiltInSymbolToCurrentScope('__dict__', node, 'Dict[str, Any]');
this._addBuiltInSymbolToCurrentScope('__dictoffset__', node, 'int');
this._addBuiltInSymbolToCurrentScope('__flags__', node, 'int');
this._addBuiltInSymbolToCurrentScope('__itemsize__', node, 'int');
this._addBuiltInSymbolToCurrentScope('__module__', node, 'str');
this._addBuiltInSymbolToCurrentScope('__mro__', node, 'Any');
this._addBuiltInSymbolToCurrentScope('__name__', node, 'str');
if (this._fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V33) {
if (this._fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V30) {
this._addBuiltInSymbolToCurrentScope('__qualname__', node, 'str');
this._addBuiltInSymbolToCurrentScope('__text_signature__', node, 'str');
}
this._addBuiltInSymbolToCurrentScope('__subclasses__', node, 'Any');

// Analyze the suite.
this.walk(node.suite);
Expand Down
2 changes: 1 addition & 1 deletion server/src/analyzer/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const enum DeclarationType {
Alias,
}

export type IntrinsicType = 'Any' | 'str' | 'Iterable[str]' | 'class' | 'Dict[str, Any]';
export type IntrinsicType = 'Any' | 'str' | 'int' | 'Iterable[str]' | 'class' | 'Dict[str, Any]';

export interface DeclarationBase {
// Category of this symbol (function, variable, etc.).
Expand Down
8 changes: 7 additions & 1 deletion server/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10891,12 +10891,18 @@ export function createTypeEvaluator(importLookup: ImportLookup, printTypeFlags:
const classTypeInfo = getTypeOfClass(classNode);
return classTypeInfo ? classTypeInfo.classType : undefined;
}

const strType = getBuiltInObject(declaration.node, 'str');
if (strType.category === TypeCategory.Object) {
const intType = getBuiltInObject(declaration.node, 'int');
if (intType.category === TypeCategory.Object && strType.category === TypeCategory.Object) {
if (declaration.intrinsicType === 'str') {
return strType;
}

if (declaration.intrinsicType === 'int') {
return intType;
}

if (declaration.intrinsicType === 'Iterable[str]') {
const iterableType = getBuiltInType(declaration.node, 'Iterable');
if (iterableType.category === TypeCategory.Class) {
Expand Down
6 changes: 6 additions & 0 deletions server/src/tests/checker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,12 @@ test('Classes2', () => {
validateResults(analysisResults, 2);
});

test('Classes3', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['classes3.py']);

validateResults(analysisResults, 1);
});

test('Mro1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['mro1.py']);

Expand Down
24 changes: 24 additions & 0 deletions server/src/tests/samples/classes3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This sample tests that various class variables (as defined in
# the type metaclass) are accessible without a type error.


class TestClass:
pass


base = TestClass.__base__
basic_size = TestClass.__basicsize__
dict = TestClass.__dict__
dict_offset = TestClass.__dictoffset__
flags = TestClass.__flags__
item_size = TestClass.__itemsize__
module = TestClass.__module__
mro = TestClass.__mro__
name = TestClass.__name__
qualname = TestClass.__qualname__
text_signature = TestClass.__text_signature__
subclasses = TestClass.__subclasses__


# This should generate an error
dummy = TestClass.__dummy__

0 comments on commit 0c3a917

Please sign in to comment.