tywrap 0.5.1 → 0.6.1

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.
Files changed (178) hide show
  1. package/dist/cli.js +8 -6
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config/index.d.ts.map +1 -1
  4. package/dist/config/index.js +236 -147
  5. package/dist/config/index.js.map +1 -1
  6. package/dist/core/annotation-parser.d.ts.map +1 -1
  7. package/dist/core/annotation-parser.js +112 -97
  8. package/dist/core/annotation-parser.js.map +1 -1
  9. package/dist/core/discovery.d.ts +17 -0
  10. package/dist/core/discovery.d.ts.map +1 -1
  11. package/dist/core/discovery.js +85 -49
  12. package/dist/core/discovery.js.map +1 -1
  13. package/dist/core/emit-call.d.ts +58 -0
  14. package/dist/core/emit-call.d.ts.map +1 -0
  15. package/dist/core/emit-call.js +103 -0
  16. package/dist/core/emit-call.js.map +1 -0
  17. package/dist/core/generator.d.ts +2 -0
  18. package/dist/core/generator.d.ts.map +1 -1
  19. package/dist/core/generator.js +54 -211
  20. package/dist/core/generator.js.map +1 -1
  21. package/dist/core/mapper.d.ts.map +1 -1
  22. package/dist/core/mapper.js +118 -104
  23. package/dist/core/mapper.js.map +1 -1
  24. package/dist/core/validation.d.ts +23 -0
  25. package/dist/core/validation.d.ts.map +1 -1
  26. package/dist/core/validation.js +52 -48
  27. package/dist/core/validation.js.map +1 -1
  28. package/dist/dev.d.ts.map +1 -1
  29. package/dist/dev.js +175 -133
  30. package/dist/dev.js.map +1 -1
  31. package/dist/index.d.ts +6 -25
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +9 -31
  34. package/dist/index.js.map +1 -1
  35. package/dist/runtime/bounded-context.d.ts +2 -2
  36. package/dist/runtime/bounded-context.js +2 -2
  37. package/dist/runtime/{safe-codec.d.ts → bridge-codec.d.ts} +26 -11
  38. package/dist/runtime/bridge-codec.d.ts.map +1 -0
  39. package/dist/runtime/{safe-codec.js → bridge-codec.js} +69 -72
  40. package/dist/runtime/bridge-codec.js.map +1 -0
  41. package/dist/runtime/{http-io.d.ts → http-transport.d.ts} +6 -6
  42. package/dist/runtime/http-transport.d.ts.map +1 -0
  43. package/dist/runtime/{http-io.js → http-transport.js} +3 -3
  44. package/dist/runtime/http-transport.js.map +1 -0
  45. package/dist/runtime/http.d.ts +3 -3
  46. package/dist/runtime/http.d.ts.map +1 -1
  47. package/dist/runtime/http.js +4 -4
  48. package/dist/runtime/http.js.map +1 -1
  49. package/dist/runtime/index.d.ts +9 -1
  50. package/dist/runtime/index.d.ts.map +1 -1
  51. package/dist/runtime/index.js +9 -1
  52. package/dist/runtime/index.js.map +1 -1
  53. package/dist/runtime/node.d.ts +6 -6
  54. package/dist/runtime/node.d.ts.map +1 -1
  55. package/dist/runtime/node.js +7 -7
  56. package/dist/runtime/node.js.map +1 -1
  57. package/dist/runtime/pooled-transport.d.ts +11 -11
  58. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  59. package/dist/runtime/pooled-transport.js +10 -10
  60. package/dist/runtime/pooled-transport.js.map +1 -1
  61. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
  62. package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
  63. package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
  64. package/dist/runtime/{pyodide-io.d.ts → pyodide-transport.d.ts} +8 -8
  65. package/dist/runtime/pyodide-transport.d.ts.map +1 -0
  66. package/dist/runtime/{pyodide-io.js → pyodide-transport.js} +5 -5
  67. package/dist/runtime/pyodide-transport.js.map +1 -0
  68. package/dist/runtime/pyodide.d.ts +3 -3
  69. package/dist/runtime/pyodide.d.ts.map +1 -1
  70. package/dist/runtime/pyodide.js +5 -5
  71. package/dist/runtime/pyodide.js.map +1 -1
  72. package/dist/runtime/rpc-client.d.ts +23 -5
  73. package/dist/runtime/rpc-client.d.ts.map +1 -1
  74. package/dist/runtime/rpc-client.js +36 -31
  75. package/dist/runtime/rpc-client.js.map +1 -1
  76. package/dist/runtime/{process-io.d.ts → subprocess-transport.d.ts} +25 -9
  77. package/dist/runtime/subprocess-transport.d.ts.map +1 -0
  78. package/dist/runtime/{process-io.js → subprocess-transport.js} +60 -41
  79. package/dist/runtime/subprocess-transport.js.map +1 -0
  80. package/dist/runtime/{worker-pool.d.ts → transport-pool.d.ts} +15 -15
  81. package/dist/runtime/transport-pool.d.ts.map +1 -0
  82. package/dist/runtime/{worker-pool.js → transport-pool.js} +6 -6
  83. package/dist/runtime/transport-pool.js.map +1 -0
  84. package/dist/runtime/transport.d.ts +14 -8
  85. package/dist/runtime/transport.d.ts.map +1 -1
  86. package/dist/runtime/transport.js +10 -4
  87. package/dist/runtime/transport.js.map +1 -1
  88. package/dist/types/index.d.ts +10 -1
  89. package/dist/types/index.d.ts.map +1 -1
  90. package/dist/tywrap.d.ts +0 -5
  91. package/dist/tywrap.d.ts.map +1 -1
  92. package/dist/tywrap.js +0 -20
  93. package/dist/tywrap.js.map +1 -1
  94. package/dist/utils/cache.d.ts +13 -2
  95. package/dist/utils/cache.d.ts.map +1 -1
  96. package/dist/utils/cache.js +52 -60
  97. package/dist/utils/cache.js.map +1 -1
  98. package/dist/utils/codec.d.ts +1 -1
  99. package/dist/utils/codec.d.ts.map +1 -1
  100. package/dist/utils/codec.js +181 -182
  101. package/dist/utils/codec.js.map +1 -1
  102. package/dist/utils/python.d.ts.map +1 -1
  103. package/dist/utils/python.js +28 -17
  104. package/dist/utils/python.js.map +1 -1
  105. package/dist/utils/runtime.d.ts.map +1 -1
  106. package/dist/utils/runtime.js +22 -16
  107. package/dist/utils/runtime.js.map +1 -1
  108. package/dist/version.d.ts +12 -0
  109. package/dist/version.d.ts.map +1 -0
  110. package/dist/version.js +12 -0
  111. package/dist/version.js.map +1 -0
  112. package/package.json +3 -2
  113. package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
  114. package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
  115. package/runtime/python_bridge.py +54 -6
  116. package/runtime/safe_codec.py +8 -8
  117. package/runtime/tywrap_bridge_core.py +159 -23
  118. package/src/cli.ts +8 -12
  119. package/src/config/index.ts +271 -165
  120. package/src/core/annotation-parser.ts +135 -123
  121. package/src/core/discovery.ts +102 -54
  122. package/src/core/emit-call.ts +171 -0
  123. package/src/core/generator.ts +67 -263
  124. package/src/core/mapper.ts +125 -110
  125. package/src/core/validation.ts +84 -48
  126. package/src/dev.ts +237 -153
  127. package/src/index.ts +11 -68
  128. package/src/runtime/bounded-context.ts +2 -2
  129. package/src/runtime/{safe-codec.ts → bridge-codec.ts} +83 -95
  130. package/src/runtime/{http-io.ts → http-transport.ts} +5 -5
  131. package/src/runtime/http.ts +5 -5
  132. package/src/runtime/index.ts +23 -1
  133. package/src/runtime/node.ts +15 -15
  134. package/src/runtime/pooled-transport.ts +15 -15
  135. package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
  136. package/src/runtime/{pyodide-io.ts → pyodide-transport.ts} +7 -7
  137. package/src/runtime/pyodide.ts +6 -6
  138. package/src/runtime/rpc-client.ts +51 -40
  139. package/src/runtime/{process-io.ts → subprocess-transport.ts} +77 -47
  140. package/src/runtime/{worker-pool.ts → transport-pool.ts} +26 -26
  141. package/src/runtime/transport.ts +15 -8
  142. package/src/types/index.ts +10 -1
  143. package/src/tywrap.ts +0 -24
  144. package/src/utils/cache.ts +61 -63
  145. package/src/utils/codec.ts +217 -196
  146. package/src/utils/python.ts +33 -20
  147. package/src/utils/runtime.ts +24 -13
  148. package/src/version.ts +12 -0
  149. package/dist/runtime/http-io.d.ts.map +0 -1
  150. package/dist/runtime/http-io.js.map +0 -1
  151. package/dist/runtime/optimized-node.d.ts +0 -23
  152. package/dist/runtime/optimized-node.d.ts.map +0 -1
  153. package/dist/runtime/optimized-node.js +0 -23
  154. package/dist/runtime/optimized-node.js.map +0 -1
  155. package/dist/runtime/process-io.d.ts.map +0 -1
  156. package/dist/runtime/process-io.js.map +0 -1
  157. package/dist/runtime/protocol.d.ts +0 -3
  158. package/dist/runtime/protocol.d.ts.map +0 -1
  159. package/dist/runtime/protocol.js +0 -3
  160. package/dist/runtime/protocol.js.map +0 -1
  161. package/dist/runtime/pyodide-io.d.ts.map +0 -1
  162. package/dist/runtime/pyodide-io.js.map +0 -1
  163. package/dist/runtime/safe-codec.d.ts.map +0 -1
  164. package/dist/runtime/safe-codec.js.map +0 -1
  165. package/dist/runtime/worker-pool.d.ts.map +0 -1
  166. package/dist/runtime/worker-pool.js.map +0 -1
  167. package/dist/utils/bundle-optimizer.d.ts +0 -182
  168. package/dist/utils/bundle-optimizer.d.ts.map +0 -1
  169. package/dist/utils/bundle-optimizer.js +0 -680
  170. package/dist/utils/bundle-optimizer.js.map +0 -1
  171. package/dist/utils/memory-profiler.d.ts +0 -123
  172. package/dist/utils/memory-profiler.d.ts.map +0 -1
  173. package/dist/utils/memory-profiler.js +0 -426
  174. package/dist/utils/memory-profiler.js.map +0 -1
  175. package/src/runtime/optimized-node.ts +0 -25
  176. package/src/runtime/protocol.ts +0 -2
  177. package/src/utils/bundle-optimizer.ts +0 -876
  178. package/src/utils/memory-profiler.ts +0 -582
@@ -503,119 +503,134 @@ export class TypeMapper {
503
503
  readonly: false,
504
504
  });
505
505
 
506
- if (this.hasPreset('stdlib')) {
507
- const stdlibModule = moduleName ?? '';
508
- const allowModule =
509
- !moduleName ||
510
- stdlibModule === 'datetime' ||
511
- stdlibModule === 'decimal' ||
512
- stdlibModule === 'uuid' ||
513
- stdlibModule === 'pathlib';
514
- if (allowModule) {
515
- if (name === 'datetime' || name === 'date' || name === 'time') {
516
- return { kind: 'primitive', name: 'string' };
517
- }
518
- if (name === 'timedelta') {
519
- return { kind: 'primitive', name: 'number' };
520
- }
521
- if (name === 'Decimal' || name === 'UUID') {
522
- return { kind: 'primitive', name: 'string' };
523
- }
524
- if (
525
- name === 'Path' ||
526
- name === 'PurePath' ||
527
- name === 'PosixPath' ||
528
- name === 'WindowsPath'
529
- ) {
530
- return { kind: 'primitive', name: 'string' };
531
- }
532
- }
533
- }
534
-
535
- if (this.hasPreset('pandas')) {
536
- const allowModule = !moduleName || moduleName.startsWith('pandas');
537
- if (allowModule) {
538
- if (name === 'DataFrame') {
539
- const recordsArray: TSArrayType = { kind: 'array', elementType: recordObject };
540
- return { kind: 'union', types: [recordObject, recordsArray] };
541
- }
542
- if (name === 'Series') {
543
- const valuesArray: TSArrayType = {
544
- kind: 'array',
545
- elementType: { kind: 'primitive', name: 'unknown' },
546
- };
547
- return { kind: 'union', types: [valuesArray, recordObject] };
548
- }
549
- }
550
- }
551
-
552
- if (this.hasPreset('pydantic')) {
553
- const allowModule = !moduleName || moduleName.startsWith('pydantic');
554
- if (allowModule && name === 'BaseModel') {
555
- return recordObject;
556
- }
557
- }
506
+ const baseProps = [prop('shape', numberArray), prop('dtype', stringType, true)];
507
+ const buildSparse = (format: 'csr' | 'csc' | 'coo'): TSObjectType => ({
508
+ kind: 'object',
509
+ properties: [
510
+ prop('format', { kind: 'literal', value: format }),
511
+ ...baseProps,
512
+ prop('data', unknownArray),
513
+ ...(format === 'coo'
514
+ ? [prop('row', numberArray), prop('col', numberArray)]
515
+ : [prop('indices', numberArray), prop('indptr', numberArray)]),
516
+ ],
517
+ });
558
518
 
559
- if (this.hasPreset('scipy')) {
560
- const allowModule = !moduleName || moduleName.startsWith('scipy');
561
- if (allowModule) {
562
- const baseProps = [prop('shape', numberArray), prop('dtype', stringType, true)];
563
- const buildSparse = (format: 'csr' | 'csc' | 'coo'): TSObjectType => ({
564
- kind: 'object',
565
- properties: [
566
- prop('format', { kind: 'literal', value: format }),
567
- ...baseProps,
568
- prop('data', unknownArray),
569
- ...(format === 'coo'
570
- ? [prop('row', numberArray), prop('col', numberArray)]
571
- : [prop('indices', numberArray), prop('indptr', numberArray)]),
572
- ],
573
- });
574
- if (name === 'csr_matrix') {
575
- return buildSparse('csr');
576
- }
577
- if (name === 'csc_matrix') {
578
- return buildSparse('csc');
579
- }
580
- if (name === 'coo_matrix') {
581
- return buildSparse('coo');
582
- }
583
- if (name === 'spmatrix') {
584
- return {
585
- kind: 'union',
586
- types: [buildSparse('csr'), buildSparse('csc'), buildSparse('coo')],
587
- };
588
- }
589
- }
590
- }
519
+ // Each entry pairs a preset with the module(s) it owns and a name->type resolver.
520
+ // Entries are evaluated in order; the first enabled preset whose module is allowed
521
+ // and whose resolver returns a type wins. This preserves the prior cascading-`if`
522
+ // precedence while keeping the per-preset rules table-driven.
523
+ const presetResolvers: ReadonlyArray<{
524
+ preset: TypePreset;
525
+ allowModule: (moduleName: string | undefined) => boolean;
526
+ resolve: (name: string) => TypescriptType | undefined;
527
+ }> = [
528
+ {
529
+ preset: 'stdlib',
530
+ allowModule: m =>
531
+ !m || m === 'datetime' || m === 'decimal' || m === 'uuid' || m === 'pathlib',
532
+ resolve: n => {
533
+ if (n === 'datetime' || n === 'date' || n === 'time') {
534
+ return { kind: 'primitive', name: 'string' };
535
+ }
536
+ if (n === 'timedelta') {
537
+ return { kind: 'primitive', name: 'number' };
538
+ }
539
+ if (n === 'Decimal' || n === 'UUID') {
540
+ return { kind: 'primitive', name: 'string' };
541
+ }
542
+ if (n === 'Path' || n === 'PurePath' || n === 'PosixPath' || n === 'WindowsPath') {
543
+ return { kind: 'primitive', name: 'string' };
544
+ }
545
+ return undefined;
546
+ },
547
+ },
548
+ {
549
+ preset: 'pandas',
550
+ allowModule: m => !m || m.startsWith('pandas'),
551
+ resolve: n => {
552
+ if (n === 'DataFrame') {
553
+ const recordsArray: TSArrayType = { kind: 'array', elementType: recordObject };
554
+ return { kind: 'union', types: [recordObject, recordsArray] };
555
+ }
556
+ if (n === 'Series') {
557
+ const valuesArray: TSArrayType = {
558
+ kind: 'array',
559
+ elementType: { kind: 'primitive', name: 'unknown' },
560
+ };
561
+ return { kind: 'union', types: [valuesArray, recordObject] };
562
+ }
563
+ return undefined;
564
+ },
565
+ },
566
+ {
567
+ preset: 'pydantic',
568
+ allowModule: m => !m || m.startsWith('pydantic'),
569
+ resolve: n => (n === 'BaseModel' ? recordObject : undefined),
570
+ },
571
+ {
572
+ preset: 'scipy',
573
+ allowModule: m => !m || m.startsWith('scipy'),
574
+ resolve: n => {
575
+ if (n === 'csr_matrix') {
576
+ return buildSparse('csr');
577
+ }
578
+ if (n === 'csc_matrix') {
579
+ return buildSparse('csc');
580
+ }
581
+ if (n === 'coo_matrix') {
582
+ return buildSparse('coo');
583
+ }
584
+ if (n === 'spmatrix') {
585
+ return {
586
+ kind: 'union',
587
+ types: [buildSparse('csr'), buildSparse('csc'), buildSparse('coo')],
588
+ };
589
+ }
590
+ return undefined;
591
+ },
592
+ },
593
+ {
594
+ preset: 'torch',
595
+ allowModule: m => !m || m.startsWith('torch'),
596
+ resolve: n =>
597
+ n === 'Tensor'
598
+ ? {
599
+ kind: 'object',
600
+ properties: [
601
+ prop('data', unknownType),
602
+ prop('shape', numberArray),
603
+ prop('dtype', stringType, true),
604
+ prop('device', stringType, true),
605
+ ],
606
+ }
607
+ : undefined,
608
+ },
609
+ {
610
+ preset: 'sklearn',
611
+ allowModule: m => !m || m.startsWith('sklearn'),
612
+ resolve: n =>
613
+ n === 'BaseEstimator'
614
+ ? {
615
+ kind: 'object',
616
+ properties: [
617
+ prop('className', stringType),
618
+ prop('module', stringType),
619
+ prop('version', stringType, true),
620
+ prop('params', recordObject),
621
+ ],
622
+ }
623
+ : undefined,
624
+ },
625
+ ];
591
626
 
592
- if (this.hasPreset('torch')) {
593
- const allowModule = !moduleName || moduleName.startsWith('torch');
594
- if (allowModule && name === 'Tensor') {
595
- return {
596
- kind: 'object',
597
- properties: [
598
- prop('data', unknownType),
599
- prop('shape', numberArray),
600
- prop('dtype', stringType, true),
601
- prop('device', stringType, true),
602
- ],
603
- };
627
+ for (const entry of presetResolvers) {
628
+ if (!this.hasPreset(entry.preset) || !entry.allowModule(moduleName)) {
629
+ continue;
604
630
  }
605
- }
606
-
607
- if (this.hasPreset('sklearn')) {
608
- const allowModule = !moduleName || moduleName.startsWith('sklearn');
609
- if (allowModule && name === 'BaseEstimator') {
610
- return {
611
- kind: 'object',
612
- properties: [
613
- prop('className', stringType),
614
- prop('module', stringType),
615
- prop('version', stringType, true),
616
- prop('params', recordObject),
617
- ],
618
- };
631
+ const resolved = entry.resolve(name);
632
+ if (resolved !== undefined) {
633
+ return resolved;
619
634
  }
620
635
  }
621
636
 
@@ -195,60 +195,96 @@ export class ValidationEngine {
195
195
  errors: AnalysisError[],
196
196
  warnings: AnalysisWarning[]
197
197
  ): void {
198
- // Check return type (skip __init__ methods as they conventionally don't need return type annotations)
199
- if (this.isEmptyType(func.returnType) && func.name !== '__init__') {
200
- if (this.config.allowMissingTypeHints) {
201
- warnings.push({
202
- type: 'missing-type',
203
- message: `Function '${func.name}' is missing return type annotation`,
204
- });
205
- } else if (this.config.strictTypeChecking) {
206
- // In strict mode without allowMissingTypeHints, we warn first then error
207
- warnings.push({
208
- type: 'missing-type',
209
- message: `Function '${func.name}' is missing return type annotation`,
210
- });
211
- errors.push({
212
- type: 'type',
213
- message: `Function '${func.name}' requires return type annotation`,
214
- });
215
- }
216
- }
198
+ this.checkReturnTypeHint(func, errors, warnings);
217
199
 
218
200
  // Check parameter types (skip 'self' and 'cls' parameters)
219
201
  for (const param of func.parameters) {
220
- if (this.isEmptyType(param.type) && param.name !== 'self' && param.name !== 'cls') {
221
- if (this.config.allowMissingTypeHints) {
222
- warnings.push({
223
- type: 'missing-type',
224
- message: `Parameter '${param.name}' in function '${func.name}' is missing type annotation`,
225
- });
226
- } else if (this.config.strictTypeChecking) {
227
- // In strict mode without allowMissingTypeHints, we warn first then error
228
- warnings.push({
229
- type: 'missing-type',
230
- message: `Parameter '${param.name}' in function '${func.name}' is missing type annotation`,
231
- });
232
- errors.push({
233
- type: 'type',
234
- message: `Parameter '${param.name}' in function '${func.name}' requires type annotation`,
235
- });
236
- }
237
- }
202
+ this.checkParameterTypeHint(func, param, errors, warnings);
203
+ this.checkParameterTypeStructure(param, errors);
204
+ }
238
205
 
239
- // Validate type structure only in strict mode (skip 'self' and 'cls' parameters)
240
- if (
241
- this.config.strictTypeChecking &&
242
- !this.config.allowMissingTypeHints &&
243
- param.name !== 'self' &&
244
- param.name !== 'cls'
245
- ) {
246
- const typeErrors = this.validateTypeAnnotation(param.type, `parameter '${param.name}'`);
247
- errors.push(...typeErrors);
248
- }
206
+ this.checkReturnTypeStructure(func, errors);
207
+ }
208
+
209
+ /**
210
+ * Emit a missing-type warning, and—in strict mode—an accompanying error.
211
+ * In strict mode without allowMissingTypeHints, we warn first then error.
212
+ */
213
+ private reportMissingType(
214
+ warningMessage: string,
215
+ errorMessage: string,
216
+ warnings: AnalysisWarning[],
217
+ errors: AnalysisError[]
218
+ ): void {
219
+ if (this.config.allowMissingTypeHints) {
220
+ warnings.push({ type: 'missing-type', message: warningMessage });
221
+ } else if (this.config.strictTypeChecking) {
222
+ warnings.push({ type: 'missing-type', message: warningMessage });
223
+ errors.push({ type: 'type', message: errorMessage });
249
224
  }
225
+ }
250
226
 
251
- // Validate return type structure only in strict mode
227
+ /**
228
+ * Check the function return type annotation (skip __init__ methods as they
229
+ * conventionally don't need return type annotations).
230
+ */
231
+ private checkReturnTypeHint(
232
+ func: PythonFunction,
233
+ errors: AnalysisError[],
234
+ warnings: AnalysisWarning[]
235
+ ): void {
236
+ if (this.isEmptyType(func.returnType) && func.name !== '__init__') {
237
+ this.reportMissingType(
238
+ `Function '${func.name}' is missing return type annotation`,
239
+ `Function '${func.name}' requires return type annotation`,
240
+ warnings,
241
+ errors
242
+ );
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Check a single parameter's type annotation (skip 'self' and 'cls').
248
+ */
249
+ private checkParameterTypeHint(
250
+ func: PythonFunction,
251
+ param: PythonFunction['parameters'][number],
252
+ errors: AnalysisError[],
253
+ warnings: AnalysisWarning[]
254
+ ): void {
255
+ if (this.isEmptyType(param.type) && param.name !== 'self' && param.name !== 'cls') {
256
+ this.reportMissingType(
257
+ `Parameter '${param.name}' in function '${func.name}' is missing type annotation`,
258
+ `Parameter '${param.name}' in function '${func.name}' requires type annotation`,
259
+ warnings,
260
+ errors
261
+ );
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Validate a single parameter's type structure only in strict mode
267
+ * (skip 'self' and 'cls' parameters).
268
+ */
269
+ private checkParameterTypeStructure(
270
+ param: PythonFunction['parameters'][number],
271
+ errors: AnalysisError[]
272
+ ): void {
273
+ if (
274
+ this.config.strictTypeChecking &&
275
+ !this.config.allowMissingTypeHints &&
276
+ param.name !== 'self' &&
277
+ param.name !== 'cls'
278
+ ) {
279
+ const typeErrors = this.validateTypeAnnotation(param.type, `parameter '${param.name}'`);
280
+ errors.push(...typeErrors);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Validate the return type structure only in strict mode.
286
+ */
287
+ private checkReturnTypeStructure(func: PythonFunction, errors: AnalysisError[]): void {
252
288
  if (this.config.strictTypeChecking && !this.config.allowMissingTypeHints) {
253
289
  const returnTypeErrors = this.validateTypeAnnotation(func.returnType, 'return type');
254
290
  errors.push(...returnTypeErrors);