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']); }); });