@@ -517,7 +517,14 @@ class ClassValue extends Value {
517517 /** Holds if this class is a container(). That is, does it have a __getitem__ method. */
518518 predicate isContainer ( ) { exists ( this .lookup ( "__getitem__" ) ) }
519519
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+ */
521528 predicate isSequence ( ) {
522529 /*
523530 * To determine whether something is a sequence or a mapping is not entirely clear,
@@ -538,16 +545,26 @@ class ClassValue extends Value {
538545 or
539546 major_version ( ) = 3 and this .getASuperType ( ) = Value:: named ( "collections.abc.Sequence" )
540547 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" )
547553 }
548554
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+ */
550563 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
551568 this .hasAttribute ( "__getitem__" ) and
552569 not this .isSequence ( )
553570 }
0 commit comments