mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-11 03:38:07 +00:00
125 lines
4.8 KiB
TypeScript
125 lines
4.8 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { emitPythonScopeCaptures } from '../../../../src/core/ingestion/languages/python/captures.js';
|
|
import { pythonProvider } from '../../../../src/core/ingestion/languages/python.js';
|
|
import { extractParsedFile } from '../../../../src/core/ingestion/scope-extractor-bridge.js';
|
|
import { getPythonParser } from '../../../../src/core/ingestion/languages/python/query.js';
|
|
import { pythonFunctionDefinitionLabel } from '../../../../src/core/ingestion/languages/python/simple-hooks.js';
|
|
|
|
function bindingScopes(source: string, name: string) {
|
|
const parsed = extractParsedFile(pythonProvider, source, 'globals.py');
|
|
expect(parsed).toBeDefined();
|
|
return parsed!.scopes.filter((scope) => scope.bindings.has(name)).map((scope) => scope.kind);
|
|
}
|
|
|
|
describe('Python global declaration ownership', () => {
|
|
it('keeps a class-body nonlocal function in its enclosing function namespace', () => {
|
|
const source = `def outer():
|
|
target = None
|
|
class Installer:
|
|
nonlocal target
|
|
def target(value): return value
|
|
`;
|
|
const parsed = extractParsedFile(pythonProvider, source, 'globals.py')!;
|
|
const outer = parsed.scopes.find(
|
|
(scope) => scope.kind === 'Function' && scope.range.startLine === 1,
|
|
)!;
|
|
const installer = parsed.scopes.find((scope) => scope.kind === 'Class')!;
|
|
expect(installer.bindings.has('target')).toBe(false);
|
|
expect(outer.bindings.get('target')?.some((binding) => binding.def.type === 'Function')).toBe(
|
|
true,
|
|
);
|
|
expect(
|
|
emitPythonScopeCaptures(source, 'globals.py').some(
|
|
(match) => match['@type-binding.self'] !== undefined,
|
|
),
|
|
).toBe(false);
|
|
const tree = getPythonParser().parse(source);
|
|
const target = tree.rootNode.descendantsOfType('function_definition')[1]!;
|
|
expect(pythonFunctionDefinitionLabel(target, 'Function')).toBe('Function');
|
|
});
|
|
|
|
it.each(['def target(): pass', 'class target: pass'])(
|
|
'binds an explicitly global declaration at module scope: %s',
|
|
(declaration) => {
|
|
expect(
|
|
bindingScopes(`def boot():\n global target\n ${declaration}\n`, 'target'),
|
|
).toEqual(['Module']);
|
|
},
|
|
);
|
|
|
|
it.each(['def target(): pass', 'class target: pass'])(
|
|
'does not promote a function-local declaration because a nested class declares it global: %s',
|
|
(declaration) => {
|
|
const source = `def boot():
|
|
class Inner:
|
|
global target
|
|
${declaration}
|
|
`;
|
|
expect(bindingScopes(source, 'target')).toEqual(['Function']);
|
|
},
|
|
);
|
|
|
|
it.each(['def target(): pass', 'class target: pass'])(
|
|
'does not promote a declaration because a deeper function declares it global: %s',
|
|
(declaration) => {
|
|
const source = `def boot():
|
|
def deeper():
|
|
global target
|
|
${declaration}
|
|
`;
|
|
expect(bindingScopes(source, 'target')).toEqual(['Function']);
|
|
},
|
|
);
|
|
|
|
it.each(['def target(self): pass', 'class target: pass'])(
|
|
'does not inherit global declarations across a nested class boundary: %s',
|
|
(declaration) => {
|
|
const source = `def boot():
|
|
global target
|
|
class Inner:
|
|
${declaration}
|
|
`;
|
|
expect(bindingScopes(source, 'target')).toEqual(['Class']);
|
|
},
|
|
);
|
|
|
|
it('does not inherit a global declaration across a nested function boundary', () => {
|
|
const source = `def boot():
|
|
global target
|
|
def deeper():
|
|
def target(): pass
|
|
`;
|
|
expect(bindingScopes(source, 'target')).toEqual(['Function']);
|
|
});
|
|
|
|
it('applies class-owned globals to class and function declarations through control flow', () => {
|
|
const source = `class Installer:
|
|
global target, Target
|
|
if True:
|
|
def target(value): return value
|
|
class Target: pass
|
|
`;
|
|
expect(bindingScopes(source, 'target')).toEqual(['Module']);
|
|
expect(bindingScopes(source, 'Target')).toEqual(['Module']);
|
|
const matches = emitPythonScopeCaptures(source, 'globals.py');
|
|
const target = matches.find((match) => match['@declaration.name']?.text === 'target')!;
|
|
expect(target['@declaration.function']).toBeDefined();
|
|
expect(target['@declaration.method']).toBeUndefined();
|
|
expect(target['@declaration.parameter-count']?.text).toBe('1');
|
|
expect(target['@declaration.required-parameter-count']?.text).toBe('1');
|
|
expect(matches.some((match) => match['@type-binding.self'] !== undefined)).toBe(false);
|
|
|
|
const tree = getPythonParser().parse(source);
|
|
const functionNode = tree.rootNode.descendantsOfType('function_definition')[0]!;
|
|
expect(pythonFunctionDefinitionLabel(functionNode, 'Function')).toBe('Function');
|
|
});
|
|
|
|
it('does not treat comments or strings as global declarations', () => {
|
|
const source = `def boot():
|
|
# global target
|
|
description = "global target"
|
|
def target(): pass
|
|
`;
|
|
expect(bindingScopes(source, 'target')).toEqual(['Function']);
|
|
});
|
|
});
|