@@ -453,4 +453,205 @@ class Python314FeaturesTest {
453453 assertEquals(" get_type" , func.Name )
454454 }
455455 }
456+
457+ // ==================== PythonVersion Tests ====================
458+
459+ @Nested
460+ inner class PythonVersionTests {
461+
462+ @Test
463+ fun `should support pattern matching for Python310 and above` () {
464+ val version310 = chapi.ast.antlr.PythonVersion .Python310
465+ val version312 = chapi.ast.antlr.PythonVersion .Python312
466+ val version314 = chapi.ast.antlr.PythonVersion .Python314
467+ val version3 = chapi.ast.antlr.PythonVersion .Python3
468+ val autodetect = chapi.ast.antlr.PythonVersion .Autodetect
469+
470+ assertTrue(version310.supportsPatternMatching())
471+ assertTrue(version312.supportsPatternMatching())
472+ assertTrue(version314.supportsPatternMatching())
473+ assertTrue(version3.supportsPatternMatching())
474+ assertTrue(autodetect.supportsPatternMatching())
475+
476+ val version2 = chapi.ast.antlr.PythonVersion .Python2
477+ assertFalse(version2.supportsPatternMatching())
478+ }
479+
480+ @Test
481+ fun `should support type parameters for Python312 and above` () {
482+ val version312 = chapi.ast.antlr.PythonVersion .Python312
483+ val version314 = chapi.ast.antlr.PythonVersion .Python314
484+ val autodetect = chapi.ast.antlr.PythonVersion .Autodetect
485+
486+ assertTrue(version312.supportsTypeParameters())
487+ assertTrue(version314.supportsTypeParameters())
488+ assertTrue(autodetect.supportsTypeParameters())
489+
490+ val version310 = chapi.ast.antlr.PythonVersion .Python310
491+ val version3 = chapi.ast.antlr.PythonVersion .Python3
492+ val version2 = chapi.ast.antlr.PythonVersion .Python2
493+ assertFalse(version310.supportsTypeParameters())
494+ assertFalse(version3.supportsTypeParameters())
495+ assertFalse(version2.supportsTypeParameters())
496+ }
497+
498+ @Test
499+ fun `should support Python314 features only for Python314` () {
500+ val version314 = chapi.ast.antlr.PythonVersion .Python314
501+ val autodetect = chapi.ast.antlr.PythonVersion .Autodetect
502+
503+ assertTrue(version314.supportsPython314Features())
504+ assertTrue(autodetect.supportsPython314Features())
505+
506+ val version312 = chapi.ast.antlr.PythonVersion .Python312
507+ val version310 = chapi.ast.antlr.PythonVersion .Python310
508+ val version3 = chapi.ast.antlr.PythonVersion .Python3
509+ val version2 = chapi.ast.antlr.PythonVersion .Python2
510+ assertFalse(version312.supportsPython314Features())
511+ assertFalse(version310.supportsPython314Features())
512+ assertFalse(version3.supportsPython314Features())
513+ assertFalse(version2.supportsPython314Features())
514+ }
515+
516+ @Test
517+ fun `should return correct value for each version` () {
518+ assertEquals(0 , chapi.ast.antlr.PythonVersion .Autodetect .getValue())
519+ assertEquals(2 , chapi.ast.antlr.PythonVersion .Python2 .getValue())
520+ assertEquals(3 , chapi.ast.antlr.PythonVersion .Python3 .getValue())
521+ assertEquals(310 , chapi.ast.antlr.PythonVersion .Python310 .getValue())
522+ assertEquals(312 , chapi.ast.antlr.PythonVersion .Python312 .getValue())
523+ assertEquals(314 , chapi.ast.antlr.PythonVersion .Python314 .getValue())
524+ }
525+ }
526+
527+ // ==================== Additional Edge Cases ====================
528+
529+ @Nested
530+ inner class EdgeCaseTests {
531+
532+ @Test
533+ fun `should parse match with nested patterns` () {
534+ val code = """
535+ def process_data(data):
536+ match data:
537+ case {"items": [{"id": x, "value": y}]}:
538+ return (x, y)
539+ case {"items": []}:
540+ return None
541+ """ .trimIndent()
542+
543+ val codeFile = PythonAnalyser ().analysis(code, " test.py" )
544+ val func = codeFile.DataStructures [0 ].Functions [0 ]
545+
546+ assertEquals(" process_data" , func.Name )
547+ }
548+
549+ @Test
550+ fun `should parse match with as pattern` () {
551+ val code = """
552+ def process_point(point):
553+ match point:
554+ case (x, y) as coord:
555+ return f"Coordinate: {coord}"
556+ case _:
557+ return "Unknown"
558+ """ .trimIndent()
559+
560+ val codeFile = PythonAnalyser ().analysis(code, " test.py" )
561+ val func = codeFile.DataStructures [0 ].Functions [0 ]
562+
563+ assertEquals(" process_point" , func.Name )
564+ }
565+
566+ @Test
567+ fun `should parse complex type parameter with multiple bounds` () {
568+ val code = """
569+ class Processor[T: (int, str), U: float]:
570+ def process(self, item: T, value: U) -> T:
571+ return item
572+ """ .trimIndent()
573+
574+ val codeFile = PythonAnalyser ().analysis(code, " test.py" )
575+ val cls = codeFile.DataStructures [0 ]
576+
577+ assertEquals(" Processor" , cls.NodeName )
578+ }
579+
580+ @Test
581+ fun `should parse type alias with type parameters` () {
582+ val code = """
583+ type Result[T, E] = tuple[T, E] | None
584+
585+ def create_result[T, E](value: T, error: E) -> Result[T, E]:
586+ return (value, error)
587+ """ .trimIndent()
588+
589+ val codeFile = PythonAnalyser ().analysis(code, " test.py" )
590+ assertNotNull(codeFile)
591+ }
592+
593+ @Test
594+ fun `should parse match with star pattern` () {
595+ val code = """
596+ def process_list(items):
597+ match items:
598+ case [first, *rest]:
599+ return (first, rest)
600+ case []:
601+ return None
602+ """ .trimIndent()
603+
604+ val codeFile = PythonAnalyser ().analysis(code, " test.py" )
605+ val func = codeFile.DataStructures [0 ].Functions [0 ]
606+
607+ assertEquals(" process_list" , func.Name )
608+ }
609+
610+ @Test
611+ fun `should parse match with double star pattern` () {
612+ val code = """
613+ def process_dict(data):
614+ match data:
615+ case {"name": name, **rest}:
616+ return (name, rest)
617+ case _:
618+ return None
619+ """ .trimIndent()
620+
621+ val codeFile = PythonAnalyser ().analysis(code, " test.py" )
622+ val func = codeFile.DataStructures [0 ].Functions [0 ]
623+
624+ assertEquals(" process_dict" , func.Name )
625+ }
626+
627+ @Test
628+ fun `should parse t-string with nested f-string like syntax` () {
629+ val code = """
630+ def format_message(user, count):
631+ return t"User {user.name} has {count} items"
632+ """ .trimIndent()
633+
634+ val codeFile = PythonAnalyser ().analysis(code, " test.py" )
635+ val func = codeFile.DataStructures [0 ].Functions [0 ]
636+
637+ assertEquals(" format_message" , func.Name )
638+ }
639+
640+ @Test
641+ fun `should parse except with except star` () {
642+ val code = """
643+ def handle_errors():
644+ try:
645+ risky_operation()
646+ except* ValueError:
647+ handle_value_error()
648+ except* TypeError, KeyError:
649+ handle_type_or_key_error()
650+ """ .trimIndent()
651+
652+ // Note: except* is not yet in our grammar, but we should handle gracefully
653+ val codeFile = PythonAnalyser ().analysis(code, " test.py" )
654+ assertNotNull(codeFile)
655+ }
656+ }
456657}
0 commit comments