@kernlang/review-python 3.1.6 → 3.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mapper.js CHANGED
@@ -4,22 +4,68 @@
4
4
  * Maps Python syntax → universal KERN concepts.
5
5
  * Phase 1: error_raise, error_handle, effect
6
6
  */
7
+ import { conceptId, conceptSpan } from '@kernlang/core';
7
8
  import Parser from 'tree-sitter';
8
9
  import Python from 'tree-sitter-python';
9
- import { conceptId, conceptSpan } from '@kernlang/core';
10
10
  const EXTRACTOR_VERSION = '1.0.0';
11
11
  // ── Network call patterns ────────────────────────────────────────────────
12
12
  const NETWORK_MODULES = new Set(['requests', 'httpx', 'aiohttp', 'urllib']);
13
13
  const NETWORK_METHODS = new Set(['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'request', 'fetch']);
14
14
  const DB_MODULES = new Set(['psycopg2', 'asyncpg', 'pymongo', 'sqlalchemy', 'django']);
15
- const DB_METHODS = new Set(['execute', 'executemany', 'fetchone', 'fetchall', 'fetchmany', 'query', 'find', 'find_one', 'insert_one', 'insert_many', 'update_one', 'delete_one']);
16
- const FS_FUNCTIONS = new Set(['open', 'read', 'write', 'readlines', 'writelines']);
15
+ const DB_METHODS = new Set([
16
+ 'execute',
17
+ 'executemany',
18
+ 'fetchone',
19
+ 'fetchall',
20
+ 'fetchmany',
21
+ 'query',
22
+ 'find',
23
+ 'find_one',
24
+ 'insert_one',
25
+ 'insert_many',
26
+ 'update_one',
27
+ 'delete_one',
28
+ ]);
29
+ const _FS_FUNCTIONS = new Set(['open', 'read', 'write', 'readlines', 'writelines']);
17
30
  const STDLIB_MODULES = new Set([
18
- 'os', 'sys', 'json', 're', 'math', 'datetime', 'time', 'logging', 'argparse',
19
- 'collections', 'itertools', 'functools', 'pathlib', 'shutil', 'subprocess',
20
- 'threading', 'multiprocessing', 'abc', 'typing', 'io', 'pickle', 'random',
21
- 'hashlib', 'hmac', 'base64', 'csv', 'sqlite3', 'zlib', 'gzip', 'tarfile', 'zipfile',
22
- 'enum', 'struct', 'tempfile', 'unittest', 'urllib', 'uuid', 'xml',
31
+ 'os',
32
+ 'sys',
33
+ 'json',
34
+ 're',
35
+ 'math',
36
+ 'datetime',
37
+ 'time',
38
+ 'logging',
39
+ 'argparse',
40
+ 'collections',
41
+ 'itertools',
42
+ 'functools',
43
+ 'pathlib',
44
+ 'shutil',
45
+ 'subprocess',
46
+ 'threading',
47
+ 'multiprocessing',
48
+ 'abc',
49
+ 'typing',
50
+ 'io',
51
+ 'pickle',
52
+ 'random',
53
+ 'hashlib',
54
+ 'hmac',
55
+ 'base64',
56
+ 'csv',
57
+ 'sqlite3',
58
+ 'zlib',
59
+ 'gzip',
60
+ 'tarfile',
61
+ 'zipfile',
62
+ 'enum',
63
+ 'struct',
64
+ 'tempfile',
65
+ 'unittest',
66
+ 'urllib',
67
+ 'uuid',
68
+ 'xml',
23
69
  ]);
24
70
  // ── Parser setup ─────────────────────────────────────────────────────────
25
71
  let parser = null;
@@ -75,7 +121,7 @@ function extractErrorRaise(root, source, filePath, nodes) {
75
121
  function extractErrorHandle(root, source, filePath, nodes) {
76
122
  // except clauses
77
123
  walkNodes(root, 'except_clause', (node) => {
78
- const block = node.children.find(c => c.type === 'block');
124
+ const block = node.children.find((c) => c.type === 'block');
79
125
  const disposition = classifyPythonDisposition(block, source);
80
126
  const errorVar = extractExceptVar(node);
81
127
  nodes.push({
@@ -161,7 +207,8 @@ function extractEffects(root, source, filePath, nodes) {
161
207
  return;
162
208
  }
163
209
  // DB: cursor.execute(), db.query(), etc.
164
- if (DB_METHODS.has(methodName) && (DB_MODULES.has(objName) || /cursor|conn|db|session|collection/i.test(objName))) {
210
+ if (DB_METHODS.has(methodName) &&
211
+ (DB_MODULES.has(objName) || /cursor|conn|db|session|collection/i.test(objName))) {
165
212
  nodes.push({
166
213
  id: conceptId(filePath, 'effect', node.startIndex),
167
214
  kind: 'effect',
@@ -209,7 +256,7 @@ function extractEntrypoints(root, source, filePath, nodes) {
209
256
  // 1. Route decorators: @app.route, @app.get, @router.post, etc.
210
257
  // tree-sitter Python wraps decorated functions in 'decorated_definition'
211
258
  walkNodes(root, 'decorated_definition', (node) => {
212
- const fnDef = node.children.find(c => c.type === 'function_definition');
259
+ const fnDef = node.children.find((c) => c.type === 'function_definition');
213
260
  if (!fnDef)
214
261
  return;
215
262
  for (const child of node.children) {
@@ -233,7 +280,7 @@ function extractEntrypoints(root, source, filePath, nodes) {
233
280
  payload: {
234
281
  kind: 'entrypoint',
235
282
  subtype: 'route',
236
- name: nameNode ? nameNode.text : (pathMatch?.[1] || 'anonymous'),
283
+ name: nameNode ? nameNode.text : pathMatch?.[1] || 'anonymous',
237
284
  httpMethod: method === 'ROUTE' ? undefined : method,
238
285
  },
239
286
  });
@@ -243,7 +290,7 @@ function extractEntrypoints(root, source, filePath, nodes) {
243
290
  // 2. if __name__ == '__main__':
244
291
  walkNodes(root, 'if_statement', (node) => {
245
292
  const condition = node.childForFieldName('condition');
246
- if (condition && condition.text.includes('__name__') && condition.text.includes('__main__')) {
293
+ if (condition?.text.includes('__name__') && condition.text.includes('__main__')) {
247
294
  nodes.push({
248
295
  id: conceptId(filePath, 'entrypoint', node.startIndex),
249
296
  kind: 'entrypoint',
@@ -289,7 +336,7 @@ function extractGuards(root, source, filePath, nodes) {
289
336
  // 2. Pydantic validation: BaseModel.model_validate()
290
337
  walkNodes(root, 'call', (node) => {
291
338
  const func = node.childForFieldName('function');
292
- if (func && func.text.includes('model_validate')) {
339
+ if (func?.text.includes('model_validate')) {
293
340
  nodes.push({
294
341
  id: conceptId(filePath, 'guard', node.startIndex),
295
342
  kind: 'guard',
@@ -306,7 +353,7 @@ function extractGuards(root, source, filePath, nodes) {
306
353
  walkNodes(root, 'if_statement', (node) => {
307
354
  const cond = node.childForFieldName('condition');
308
355
  if (cond && /\b(user|auth|request\.user)\b/.test(cond.text)) {
309
- const block = node.namedChildren.find(c => c.type === 'block');
356
+ const block = node.namedChildren.find((c) => c.type === 'block');
310
357
  if (block) {
311
358
  const firstStmt = block.namedChildren[0];
312
359
  if (firstStmt && (firstStmt.type === 'return_statement' || firstStmt.type === 'raise_statement')) {
@@ -482,14 +529,14 @@ function getContainerId(node, filePath) {
482
529
  }
483
530
  function extractRaiseType(node) {
484
531
  // raise ValueError("...") → "ValueError"
485
- const callNode = node.namedChildren.find(c => c.type === 'call');
532
+ const callNode = node.namedChildren.find((c) => c.type === 'call');
486
533
  if (callNode) {
487
534
  const func = callNode.childForFieldName('function');
488
535
  if (func)
489
536
  return func.text;
490
537
  }
491
538
  // raise ValueError → just identifier
492
- const ident = node.namedChildren.find(c => c.type === 'identifier');
539
+ const ident = node.namedChildren.find((c) => c.type === 'identifier');
493
540
  if (ident)
494
541
  return ident.text;
495
542
  return undefined;
@@ -514,7 +561,7 @@ function isInAsyncDef(node) {
514
561
  while (parent) {
515
562
  if (parent.type === 'function_definition') {
516
563
  // Check for 'async' keyword before 'def'
517
- return parent.children.some(c => c.type === 'async');
564
+ return parent.children.some((c) => c.type === 'async');
518
565
  }
519
566
  parent = parent.parent;
520
567
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernlang/review-python",
3
- "version": "3.1.6",
3
+ "version": "3.1.7",
4
4
  "description": "Python concept mapper for kern review — tree-sitter based",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,8 +8,8 @@
8
8
  "dependencies": {
9
9
  "tree-sitter": "^0.25.0",
10
10
  "tree-sitter-python": "^0.25.0",
11
- "@kernlang/core": "3.1.6",
12
- "@kernlang/review": "3.1.6"
11
+ "@kernlang/core": "3.1.7",
12
+ "@kernlang/review": "3.1.7"
13
13
  },
14
14
  "devDependencies": {
15
15
  "ts-morph": "^27.0.0",
package/src/mapper.ts CHANGED
@@ -5,13 +5,10 @@
5
5
  * Phase 1: error_raise, error_handle, effect
6
6
  */
7
7
 
8
+ import type { ConceptEdge, ConceptMap, ConceptNode, ConceptSpan, ErrorHandlePayload } from '@kernlang/core';
9
+ import { conceptId, conceptSpan } from '@kernlang/core';
8
10
  import Parser from 'tree-sitter';
9
11
  import Python from 'tree-sitter-python';
10
- import type {
11
- ConceptMap, ConceptNode, ConceptEdge, ConceptSpan,
12
- ErrorHandlePayload, EntrypointPayload, GuardPayload, StateMutationPayload, DependencyPayload,
13
- } from '@kernlang/core';
14
- import { conceptId, conceptSpan } from '@kernlang/core';
15
12
 
16
13
  const EXTRACTOR_VERSION = '1.0.0';
17
14
 
@@ -21,16 +18,62 @@ const NETWORK_MODULES = new Set(['requests', 'httpx', 'aiohttp', 'urllib']);
21
18
  const NETWORK_METHODS = new Set(['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'request', 'fetch']);
22
19
 
23
20
  const DB_MODULES = new Set(['psycopg2', 'asyncpg', 'pymongo', 'sqlalchemy', 'django']);
24
- const DB_METHODS = new Set(['execute', 'executemany', 'fetchone', 'fetchall', 'fetchmany', 'query', 'find', 'find_one', 'insert_one', 'insert_many', 'update_one', 'delete_one']);
21
+ const DB_METHODS = new Set([
22
+ 'execute',
23
+ 'executemany',
24
+ 'fetchone',
25
+ 'fetchall',
26
+ 'fetchmany',
27
+ 'query',
28
+ 'find',
29
+ 'find_one',
30
+ 'insert_one',
31
+ 'insert_many',
32
+ 'update_one',
33
+ 'delete_one',
34
+ ]);
25
35
 
26
- const FS_FUNCTIONS = new Set(['open', 'read', 'write', 'readlines', 'writelines']);
36
+ const _FS_FUNCTIONS = new Set(['open', 'read', 'write', 'readlines', 'writelines']);
27
37
 
28
38
  const STDLIB_MODULES = new Set([
29
- 'os', 'sys', 'json', 're', 'math', 'datetime', 'time', 'logging', 'argparse',
30
- 'collections', 'itertools', 'functools', 'pathlib', 'shutil', 'subprocess',
31
- 'threading', 'multiprocessing', 'abc', 'typing', 'io', 'pickle', 'random',
32
- 'hashlib', 'hmac', 'base64', 'csv', 'sqlite3', 'zlib', 'gzip', 'tarfile', 'zipfile',
33
- 'enum', 'struct', 'tempfile', 'unittest', 'urllib', 'uuid', 'xml',
39
+ 'os',
40
+ 'sys',
41
+ 'json',
42
+ 're',
43
+ 'math',
44
+ 'datetime',
45
+ 'time',
46
+ 'logging',
47
+ 'argparse',
48
+ 'collections',
49
+ 'itertools',
50
+ 'functools',
51
+ 'pathlib',
52
+ 'shutil',
53
+ 'subprocess',
54
+ 'threading',
55
+ 'multiprocessing',
56
+ 'abc',
57
+ 'typing',
58
+ 'io',
59
+ 'pickle',
60
+ 'random',
61
+ 'hashlib',
62
+ 'hmac',
63
+ 'base64',
64
+ 'csv',
65
+ 'sqlite3',
66
+ 'zlib',
67
+ 'gzip',
68
+ 'tarfile',
69
+ 'zipfile',
70
+ 'enum',
71
+ 'struct',
72
+ 'tempfile',
73
+ 'unittest',
74
+ 'urllib',
75
+ 'uuid',
76
+ 'xml',
34
77
  ]);
35
78
 
36
79
  // ── Parser setup ─────────────────────────────────────────────────────────
@@ -72,12 +115,7 @@ export function extractPythonConcepts(source: string, filePath: string): Concept
72
115
 
73
116
  // ── error_raise ──────────────────────────────────────────────────────────
74
117
 
75
- function extractErrorRaise(
76
- root: Parser.SyntaxNode,
77
- source: string,
78
- filePath: string,
79
- nodes: ConceptNode[],
80
- ): void {
118
+ function extractErrorRaise(root: Parser.SyntaxNode, source: string, filePath: string, nodes: ConceptNode[]): void {
81
119
  // raise statements
82
120
  walkNodes(root, 'raise_statement', (node) => {
83
121
  const errorType = extractRaiseType(node);
@@ -100,15 +138,10 @@ function extractErrorRaise(
100
138
 
101
139
  // ── error_handle ─────────────────────────────────────────────────────────
102
140
 
103
- function extractErrorHandle(
104
- root: Parser.SyntaxNode,
105
- source: string,
106
- filePath: string,
107
- nodes: ConceptNode[],
108
- ): void {
141
+ function extractErrorHandle(root: Parser.SyntaxNode, source: string, filePath: string, nodes: ConceptNode[]): void {
109
142
  // except clauses
110
143
  walkNodes(root, 'except_clause', (node) => {
111
- const block = node.children.find(c => c.type === 'block');
144
+ const block = node.children.find((c) => c.type === 'block');
112
145
  const disposition = classifyPythonDisposition(block, source);
113
146
  const errorVar = extractExceptVar(node);
114
147
 
@@ -180,12 +213,7 @@ function classifyPythonDisposition(
180
213
 
181
214
  // ── effect ───────────────────────────────────────────────────────────────
182
215
 
183
- function extractEffects(
184
- root: Parser.SyntaxNode,
185
- source: string,
186
- filePath: string,
187
- nodes: ConceptNode[],
188
- ): void {
216
+ function extractEffects(root: Parser.SyntaxNode, source: string, filePath: string, nodes: ConceptNode[]): void {
189
217
  walkNodes(root, 'call', (node) => {
190
218
  const funcNode = node.childForFieldName('function');
191
219
  if (!funcNode) return;
@@ -215,7 +243,10 @@ function extractEffects(
215
243
  }
216
244
 
217
245
  // DB: cursor.execute(), db.query(), etc.
218
- if (DB_METHODS.has(methodName) && (DB_MODULES.has(objName) || /cursor|conn|db|session|collection/i.test(objName))) {
246
+ if (
247
+ DB_METHODS.has(methodName) &&
248
+ (DB_MODULES.has(objName) || /cursor|conn|db|session|collection/i.test(objName))
249
+ ) {
219
250
  nodes.push({
220
251
  id: conceptId(filePath, 'effect', node.startIndex),
221
252
  kind: 'effect',
@@ -263,16 +294,11 @@ function extractEffects(
263
294
 
264
295
  // ── entrypoint ──────────────────────────────────────────────────────────
265
296
 
266
- function extractEntrypoints(
267
- root: Parser.SyntaxNode,
268
- source: string,
269
- filePath: string,
270
- nodes: ConceptNode[],
271
- ): void {
297
+ function extractEntrypoints(root: Parser.SyntaxNode, source: string, filePath: string, nodes: ConceptNode[]): void {
272
298
  // 1. Route decorators: @app.route, @app.get, @router.post, etc.
273
299
  // tree-sitter Python wraps decorated functions in 'decorated_definition'
274
300
  walkNodes(root, 'decorated_definition', (node) => {
275
- const fnDef = node.children.find(c => c.type === 'function_definition');
301
+ const fnDef = node.children.find((c) => c.type === 'function_definition');
276
302
  if (!fnDef) return;
277
303
 
278
304
  for (const child of node.children) {
@@ -297,7 +323,7 @@ function extractEntrypoints(
297
323
  payload: {
298
324
  kind: 'entrypoint',
299
325
  subtype: 'route',
300
- name: nameNode ? nameNode.text : (pathMatch?.[1] || 'anonymous'),
326
+ name: nameNode ? nameNode.text : pathMatch?.[1] || 'anonymous',
301
327
  httpMethod: method === 'ROUTE' ? undefined : method,
302
328
  },
303
329
  });
@@ -308,7 +334,7 @@ function extractEntrypoints(
308
334
  // 2. if __name__ == '__main__':
309
335
  walkNodes(root, 'if_statement', (node) => {
310
336
  const condition = node.childForFieldName('condition');
311
- if (condition && condition.text.includes('__name__') && condition.text.includes('__main__')) {
337
+ if (condition?.text.includes('__name__') && condition.text.includes('__main__')) {
312
338
  nodes.push({
313
339
  id: conceptId(filePath, 'entrypoint', node.startIndex),
314
340
  kind: 'entrypoint',
@@ -328,12 +354,7 @@ function extractEntrypoints(
328
354
 
329
355
  // ── guard ───────────────────────────────────────────────────────────────
330
356
 
331
- function extractGuards(
332
- root: Parser.SyntaxNode,
333
- source: string,
334
- filePath: string,
335
- nodes: ConceptNode[],
336
- ): void {
357
+ function extractGuards(root: Parser.SyntaxNode, source: string, filePath: string, nodes: ConceptNode[]): void {
337
358
  // 1. Auth decorators (tree-sitter: decorated_definition → decorator + function_definition)
338
359
  walkNodes(root, 'decorated_definition', (node) => {
339
360
  for (const child of node.children) {
@@ -361,7 +382,7 @@ function extractGuards(
361
382
  // 2. Pydantic validation: BaseModel.model_validate()
362
383
  walkNodes(root, 'call', (node) => {
363
384
  const func = node.childForFieldName('function');
364
- if (func && func.text.includes('model_validate')) {
385
+ if (func?.text.includes('model_validate')) {
365
386
  nodes.push({
366
387
  id: conceptId(filePath, 'guard', node.startIndex),
367
388
  kind: 'guard',
@@ -379,7 +400,7 @@ function extractGuards(
379
400
  walkNodes(root, 'if_statement', (node) => {
380
401
  const cond = node.childForFieldName('condition');
381
402
  if (cond && /\b(user|auth|request\.user)\b/.test(cond.text)) {
382
- const block = node.namedChildren.find(c => c.type === 'block');
403
+ const block = node.namedChildren.find((c) => c.type === 'block');
383
404
  if (block) {
384
405
  const firstStmt = block.namedChildren[0];
385
406
  if (firstStmt && (firstStmt.type === 'return_statement' || firstStmt.type === 'raise_statement')) {
@@ -401,12 +422,7 @@ function extractGuards(
401
422
 
402
423
  // ── state_mutation ───────────────────────────────────────────────────────
403
424
 
404
- function extractStateMutation(
405
- root: Parser.SyntaxNode,
406
- source: string,
407
- filePath: string,
408
- nodes: ConceptNode[],
409
- ): void {
425
+ function extractStateMutation(root: Parser.SyntaxNode, source: string, filePath: string, nodes: ConceptNode[]): void {
410
426
  // Track global keyword usage
411
427
  const globalVarsInFile = new Set<string>();
412
428
  walkNodes(root, 'global_statement', (node) => {
@@ -471,12 +487,7 @@ function extractStateMutation(
471
487
 
472
488
  // ── dependency ──────────────────────────────────────────────────────────
473
489
 
474
- function extractDependencyEdges(
475
- root: Parser.SyntaxNode,
476
- source: string,
477
- filePath: string,
478
- edges: ConceptEdge[],
479
- ): void {
490
+ function extractDependencyEdges(root: Parser.SyntaxNode, source: string, filePath: string, edges: ConceptEdge[]): void {
480
491
  const addDependency = (node: Parser.SyntaxNode, specifier: string): void => {
481
492
  let subtype: 'stdlib' | 'external' | 'internal' = 'external';
482
493
  if (specifier.startsWith('.')) {
@@ -529,11 +540,7 @@ function extractDependencyEdges(
529
540
 
530
541
  // ── Tree-sitter Helpers ──────────────────────────────────────────────────
531
542
 
532
- function walkNodes(
533
- root: Parser.SyntaxNode,
534
- type: string,
535
- callback: (node: Parser.SyntaxNode) => void,
536
- ): void {
543
+ function walkNodes(root: Parser.SyntaxNode, type: string, callback: (node: Parser.SyntaxNode) => void): void {
537
544
  const cursor = root.walk();
538
545
  let reachedRoot = false;
539
546
  while (true) {
@@ -543,7 +550,10 @@ function walkNodes(
543
550
  if (cursor.gotoFirstChild()) continue;
544
551
  if (cursor.gotoNextSibling()) continue;
545
552
  while (true) {
546
- if (!cursor.gotoParent()) { reachedRoot = true; break; }
553
+ if (!cursor.gotoParent()) {
554
+ reachedRoot = true;
555
+ break;
556
+ }
547
557
  if (cursor.gotoNextSibling()) break;
548
558
  }
549
559
  if (reachedRoot) break;
@@ -579,13 +589,13 @@ function getContainerId(node: Parser.SyntaxNode, filePath: string): string | und
579
589
 
580
590
  function extractRaiseType(node: Parser.SyntaxNode): string | undefined {
581
591
  // raise ValueError("...") → "ValueError"
582
- const callNode = node.namedChildren.find(c => c.type === 'call');
592
+ const callNode = node.namedChildren.find((c) => c.type === 'call');
583
593
  if (callNode) {
584
594
  const func = callNode.childForFieldName('function');
585
595
  if (func) return func.text;
586
596
  }
587
597
  // raise ValueError → just identifier
588
- const ident = node.namedChildren.find(c => c.type === 'identifier');
598
+ const ident = node.namedChildren.find((c) => c.type === 'identifier');
589
599
  if (ident) return ident.text;
590
600
  return undefined;
591
601
  }
@@ -610,7 +620,7 @@ function isInAsyncDef(node: Parser.SyntaxNode): boolean {
610
620
  while (parent) {
611
621
  if (parent.type === 'function_definition') {
612
622
  // Check for 'async' keyword before 'def'
613
- return parent.children.some(c => c.type === 'async');
623
+ return parent.children.some((c) => c.type === 'async');
614
624
  }
615
625
  parent = parent.parent;
616
626
  }
@@ -4,9 +4,9 @@
4
4
  * Same concept, two languages, same shape.
5
5
  */
6
6
 
7
- import { extractPythonConcepts } from '../src/mapper.js';
8
7
  import { extractTsConcepts } from '@kernlang/review';
9
8
  import { Project } from 'ts-morph';
9
+ import { extractPythonConcepts } from '../src/mapper.js';
10
10
 
11
11
  function tsSourceFile(source: string, filePath = 'test.ts') {
12
12
  const project = new Project({ useInMemoryFileSystem: true, compilerOptions: { strict: true } });
@@ -19,7 +19,7 @@ describe('Bilingual: entrypoint', () => {
19
19
  app.get('/users', (req, res) => { res.json([]); });
20
20
  `);
21
21
  const concepts = extractTsConcepts(sf, 'test.ts');
22
- const ep = concepts.nodes.find(n => n.kind === 'entrypoint');
22
+ const ep = concepts.nodes.find((n) => n.kind === 'entrypoint');
23
23
  expect(ep).toBeDefined();
24
24
  expect(ep!.payload.kind).toBe('entrypoint');
25
25
  if (ep!.payload.kind === 'entrypoint') {
@@ -37,7 +37,7 @@ def get_users():
37
37
  return []
38
38
  `;
39
39
  const concepts = extractPythonConcepts(source, 'test.py');
40
- const ep = concepts.nodes.find(n => n.kind === 'entrypoint');
40
+ const ep = concepts.nodes.find((n) => n.kind === 'entrypoint');
41
41
  expect(ep).toBeDefined();
42
42
  expect(ep!.payload.kind).toBe('entrypoint');
43
43
  if (ep!.payload.kind === 'entrypoint') {
@@ -55,7 +55,7 @@ describe('Bilingual: guard', () => {
55
55
  }
56
56
  `);
57
57
  const concepts = extractTsConcepts(sf, 'test.ts');
58
- const guard = concepts.nodes.find(n => n.kind === 'guard');
58
+ const guard = concepts.nodes.find((n) => n.kind === 'guard');
59
59
  expect(guard).toBeDefined();
60
60
  expect(guard!.payload.kind).toBe('guard');
61
61
  if (guard!.payload.kind === 'guard') {
@@ -72,7 +72,7 @@ def dashboard(request):
72
72
  return render(request, 'dashboard.html')
73
73
  `;
74
74
  const concepts = extractPythonConcepts(source, 'test.py');
75
- const guard = concepts.nodes.find(n => n.kind === 'guard');
75
+ const guard = concepts.nodes.find((n) => n.kind === 'guard');
76
76
  expect(guard).toBeDefined();
77
77
  expect(guard!.payload.kind).toBe('guard');
78
78
  if (guard!.payload.kind === 'guard') {
@@ -90,7 +90,7 @@ describe('Bilingual: state_mutation', () => {
90
90
  }
91
91
  `);
92
92
  const concepts = extractTsConcepts(sf, 'test.ts');
93
- const mut = concepts.nodes.find(n => n.kind === 'state_mutation');
93
+ const mut = concepts.nodes.find((n) => n.kind === 'state_mutation');
94
94
  expect(mut).toBeDefined();
95
95
  if (mut!.payload.kind === 'state_mutation') {
96
96
  expect(mut!.payload.scope).toBe('module');
@@ -106,7 +106,7 @@ class Counter:
106
106
  self.count += 1
107
107
  `;
108
108
  const concepts = extractPythonConcepts(source, 'test.py');
109
- const mut = concepts.nodes.find(n => n.kind === 'state_mutation');
109
+ const mut = concepts.nodes.find((n) => n.kind === 'state_mutation');
110
110
  expect(mut).toBeDefined();
111
111
  if (mut!.payload.kind === 'state_mutation') {
112
112
  expect(mut!.payload.scope).toBe('module');
@@ -124,9 +124,9 @@ describe('Bilingual: dependency edges', () => {
124
124
  const concepts = extractTsConcepts(sf, 'test.ts');
125
125
  expect(concepts.edges.length).toBeGreaterThanOrEqual(3);
126
126
 
127
- const external = concepts.edges.find(e => e.payload.kind === 'dependency' && e.payload.subtype === 'external');
128
- const stdlib = concepts.edges.find(e => e.payload.kind === 'dependency' && e.payload.subtype === 'stdlib');
129
- const internal = concepts.edges.find(e => e.payload.kind === 'dependency' && e.payload.subtype === 'internal');
127
+ const external = concepts.edges.find((e) => e.payload.kind === 'dependency' && e.payload.subtype === 'external');
128
+ const stdlib = concepts.edges.find((e) => e.payload.kind === 'dependency' && e.payload.subtype === 'stdlib');
129
+ const internal = concepts.edges.find((e) => e.payload.kind === 'dependency' && e.payload.subtype === 'internal');
130
130
 
131
131
  expect(external).toBeDefined();
132
132
  expect(stdlib).toBeDefined();
@@ -142,9 +142,9 @@ from .utils import helper
142
142
  const concepts = extractPythonConcepts(source, 'test.py');
143
143
  expect(concepts.edges.length).toBeGreaterThanOrEqual(3);
144
144
 
145
- const external = concepts.edges.find(e => e.payload.kind === 'dependency' && e.payload.subtype === 'external');
146
- const stdlib = concepts.edges.find(e => e.payload.kind === 'dependency' && e.payload.subtype === 'stdlib');
147
- const internal = concepts.edges.find(e => e.payload.kind === 'dependency' && e.payload.subtype === 'internal');
145
+ const external = concepts.edges.find((e) => e.payload.kind === 'dependency' && e.payload.subtype === 'external');
146
+ const stdlib = concepts.edges.find((e) => e.payload.kind === 'dependency' && e.payload.subtype === 'stdlib');
147
+ const internal = concepts.edges.find((e) => e.payload.kind === 'dependency' && e.payload.subtype === 'internal');
148
148
 
149
149
  expect(external).toBeDefined();
150
150
  expect(stdlib).toBeDefined();
@@ -4,9 +4,9 @@
4
4
  * This is the proof that KERN concepts are universal.
5
5
  */
6
6
 
7
- import { extractPythonConcepts } from '../src/mapper.js';
8
7
  import { extractTsConcepts, runConceptRules } from '@kernlang/review';
9
8
  import { Project } from 'ts-morph';
9
+ import { extractPythonConcepts } from '../src/mapper.js';
10
10
 
11
11
  function tsSourceFile(source: string, filePath = 'test.ts') {
12
12
  const project = new Project({ useInMemoryFileSystem: true, compilerOptions: { strict: true } });
@@ -18,7 +18,7 @@ describe('Bilingual: ignored-error', () => {
18
18
  const sf = tsSourceFile('try { doWork(); } catch (e) {}');
19
19
  const concepts = extractTsConcepts(sf, 'test.ts');
20
20
  const findings = runConceptRules(concepts, 'test.ts');
21
- const f = findings.find(f => f.ruleId === 'ignored-error');
21
+ const f = findings.find((f) => f.ruleId === 'ignored-error');
22
22
  expect(f).toBeDefined();
23
23
  expect(f!.severity).toBe('error');
24
24
  });
@@ -32,7 +32,7 @@ except:
32
32
  `;
33
33
  const concepts = extractPythonConcepts(source, 'test.py');
34
34
  const findings = runConceptRules(concepts, 'test.py');
35
- const f = findings.find(f => f.ruleId === 'ignored-error');
35
+ const f = findings.find((f) => f.ruleId === 'ignored-error');
36
36
  expect(f).toBeDefined();
37
37
  expect(f!.severity).toBe('error');
38
38
  });
@@ -46,7 +46,7 @@ except Exception as e:
46
46
  `;
47
47
  const concepts = extractPythonConcepts(source, 'test.py');
48
48
  const findings = runConceptRules(concepts, 'test.py');
49
- const f = findings.find(f => f.ruleId === 'ignored-error');
49
+ const f = findings.find((f) => f.ruleId === 'ignored-error');
50
50
  expect(f).toBeDefined();
51
51
  });
52
52
 
@@ -54,7 +54,7 @@ except Exception as e:
54
54
  const sf = tsSourceFile('try { doWork(); } catch (e) { throw new AppError(e); }');
55
55
  const concepts = extractTsConcepts(sf, 'test.ts');
56
56
  const findings = runConceptRules(concepts, 'test.ts');
57
- const f = findings.find(f => f.ruleId === 'ignored-error');
57
+ const f = findings.find((f) => f.ruleId === 'ignored-error');
58
58
  expect(f).toBeUndefined();
59
59
  });
60
60
 
@@ -67,7 +67,7 @@ except Exception as e:
67
67
  `;
68
68
  const concepts = extractPythonConcepts(source, 'test.py');
69
69
  const findings = runConceptRules(concepts, 'test.py');
70
- const f = findings.find(f => f.ruleId === 'ignored-error');
70
+ const f = findings.find((f) => f.ruleId === 'ignored-error');
71
71
  expect(f).toBeUndefined();
72
72
  });
73
73
  });
@@ -79,8 +79,8 @@ describe('Bilingual: concept parity', () => {
79
79
 
80
80
  const pyConcepts = extractPythonConcepts('def fail():\n raise ValueError("boom")', 'test.py');
81
81
 
82
- const tsRaise = tsConcepts.nodes.find(n => n.kind === 'error_raise');
83
- const pyRaise = pyConcepts.nodes.find(n => n.kind === 'error_raise');
82
+ const tsRaise = tsConcepts.nodes.find((n) => n.kind === 'error_raise');
83
+ const pyRaise = pyConcepts.nodes.find((n) => n.kind === 'error_raise');
84
84
 
85
85
  expect(tsRaise).toBeDefined();
86
86
  expect(pyRaise).toBeDefined();
@@ -98,8 +98,8 @@ describe('Bilingual: concept parity', () => {
98
98
 
99
99
  const pyConcepts = extractPythonConcepts('def get_data():\n requests.get("/api")', 'test.py');
100
100
 
101
- const tsEffect = tsConcepts.nodes.find(n => n.kind === 'effect');
102
- const pyEffect = pyConcepts.nodes.find(n => n.kind === 'effect');
101
+ const tsEffect = tsConcepts.nodes.find((n) => n.kind === 'effect');
102
+ const pyEffect = pyConcepts.nodes.find((n) => n.kind === 'effect');
103
103
 
104
104
  expect(tsEffect).toBeDefined();
105
105
  expect(pyEffect).toBeDefined();