@@ -517,7 +517,14 @@ class ClassValue extends Value {
517
517
/** Holds if this class is a container(). That is, does it have a __getitem__ method. */
518
518
predicate isContainer ( ) { exists ( this .lookup ( "__getitem__" ) ) }
519
519
520
- /** Holds if this class is probably a sequence. */
520
+ /**
521
+ * Holds if this class is a sequence. Mutually exclusive with `isMapping()`.
522
+ *
523
+ * Following the definition from
524
+ * https://docs.python.org/3/glossary.html#term-sequence.
525
+ * We don't look at the keys accepted by `__getitem__, but default to treating a class
526
+ * as a sequence (so might treat some mappings as sequences).
527
+ */
521
528
predicate isSequence ( ) {
522
529
/*
523
530
* To determine whether something is a sequence or a mapping is not entirely clear,
@@ -538,16 +545,26 @@ class ClassValue extends Value {
538
545
or
539
546
major_version ( ) = 3 and this .getASuperType ( ) = Value:: named ( "collections.abc.Sequence" )
540
547
or
541
- /* Does it have an index or __reversed__ method? */
542
- this .isContainer ( ) and
543
- (
544
- this .hasAttribute ( "index" ) or
545
- this .hasAttribute ( "__reversed__" )
546
- )
548
+ this .hasAttribute ( "__getitem__" ) and
549
+ this .hasAttribute ( "__len__" ) and
550
+ not this .getASuperType ( ) = ClassValue:: dict ( ) and
551
+ not this .getASuperType ( ) = Value:: named ( "collections.Mapping" ) and
552
+ not this .getASuperType ( ) = Value:: named ( "collections.abc.Mapping" )
547
553
}
548
554
549
- /** Holds if this class is a mapping. */
555
+ /**
556
+ * Holds if this class is a mapping. Mutually exclusive with `isSequence()`.
557
+ *
558
+ * Although a class will satisfy the requirement by the definition in
559
+ * https://docs.python.org/3.8/glossary.html#term-mapping, we don't look at the keys
560
+ * accepted by `__getitem__, but default to treating a class as a sequence (so might
561
+ * treat some mappings as sequences).
562
+ */
550
563
predicate isMapping ( ) {
564
+ major_version ( ) = 2 and this .getASuperType ( ) = Value:: named ( "collections.Mapping" )
565
+ or
566
+ major_version ( ) = 3 and this .getASuperType ( ) = Value:: named ( "collections.abc.Mapping" )
567
+ or
551
568
this .hasAttribute ( "__getitem__" ) and
552
569
not this .isSequence ( )
553
570
}
0 commit comments