In Python 2, PyMapping_Check will return 0 for list objects. In Python
3, it returns 1. Obviously, this makes it rather difficult to
differentiate between mappings and other sized iterables. In addition,
it differs from the behavior of the ``collections.Mapping`` ABC --
isinstance([], collections.Mapping) returns False.
Since most of the PyMapping_* functions don't seem to work properly on
lists, I believe this behavior is erroneous.
The behavior can be seen from a C extension, or if you're lazy, using
ctypes:
Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41)
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.CDLL('libpython2.6.so').PyMapping_Check(ctypes.py_object([]))
0
Python 3.0.1+ (r301:69556, Apr 15 2009, 15:59:22)
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.CDLL('libpython3.0.so').PyMapping_Check(ctypes.py_object([]))
1

I understand this is indeed unintuitive. The reason list objects support
the C "mapping protocol" in 3.x is that it is how slicing of lists (and
tuples, for that matter) is implemented. Perhaps the documentation
should carry a warning about this.
Unfortunately, right now there is no easy way in C to check that an
object implements a given ABC.

Personally, I think PyMapping_Check and PySequence_Check should be
deprecated and removed. Like their Python counterparts,
operator.isMappingType() and operation.isSequenceType(), they are
unreliable at best in the face of not LBYL and abcs.

> Why did the list implementation get changed in Py3.x? Is it now
> necessary for any subscripting type to put the same method in both the
> sequence methods and mapping methods? Was this change necessary?
I think it's a case of foolish consistency. In py3k there are no opcodes
dedicated to slicing anymore, instead the slice object is passed to the
mapping's getitem method.

> Why did the list implementation get changed in Py3.x?
Because we decided to get rid of the sq_slice and sq_ass_slice slots
in PySequenceMethods, and that in turn was because we got rid of the
slice-related opcodes and the separate __getslice__ and __setslice__
magic methods.
> Is it now necessary for any subscripting type to put the same method
> in both the sequence methods and mapping methods?
Yes, if the type wants to support slicing. The reason is that while
we changed many things, we didn't want to change the signature of the
methods that we kept, and the sq_item/sq_ass_item signatures have
arguments that make it impossible to pass the info necessary for a
slice.
> Was this change necessary?
The changes are briefly mentioned in PEP 3100 without any motivation.
However I think the rationale was the observation that the old
sq_slice / sq_ass_slice took only integers (really ssize_t), meaning
that it was impossible to implement a sequence type taking a slice
with arguments outside the range supported by ssize_t (e.g. a custom
range type supporting huge longs). Or with non-integral arguments.
This problem never existed for non-slice __get__ since one could
always implement mp_subscript / mp_ass_subscript.
Was it necessary? I'm not sure -- but that's water under the bridge.
Was it a good change? From the POV of Python code, yes. The old
approach caused some odd problems for classes implementing
__getslice__ / __setslice__ (since those were invoked after the
arguments had been pushed through the sq_slice / sq_ass_slice API).
> Personally, I think PyMapping_Check and PySequence_Check should be
> deprecated and removed. Like their Python counterparts,
> operator.isMappingType() and operation.isSequenceType(), they are
> unreliable at best in the face of not LBYL and abcs.
Right, calling PyMapping_Check() was never particularly reliable, and
extension modules depending on it probably always had subtle bugs.
Perhaps it would be nice if we provided a C API to at least some of
the ABC package.

> Right, calling PyMapping_Check() was never particularly reliable, and
> extension modules depending on it probably always had subtle bugs.
> Perhaps it would be nice if we provided a C API to at least some of
> the ABC package.
In the meantime, would it be reasonable to add the moral equivalent of `hasattr(type(op), 'items')` to PyMapping_Check()?

> In the meantime, would it be reasonable to add the moral equivalent
> of `hasattr(type(op), 'items')` to PyMapping_Check()?
That all depends on what it is used for. Which is hard to say without someone following more of the links that Raymond posted.

Modules/posixmodule.c: uses PyMapping_Size(), PyMapping_Keys() and PyMapping_Values() to parse an environment mapping (for execve() and friends).
PyFrame_New(): validates the "locals" argument in pydebug mode (only used for module-level code).
Note that functions in frameobject.c have "assert(PyDict_Check(dict))" where "dict" is that same locals argument (copied into f_locals)...
PC/_subprocess.c: uses PyMapping_Size(), PyMapping_Keys() and PyMapping_Values() to parse an environment mapping (for CreateProcessW()).
Python/btninmodule.c: validates the "locals" argument to eval().
There are also a couple of places where the PyMapping API (such PyMapping_Keys()) is used (e.g. _json), but without calling PyMapping_Check first.

The question is, if PyMapping_Check() returns True, and a list is passed, will the code segfault or raise an exception? A segfault would be unacceptable; an exception would be acceptable assuming that the code would have raised an exception anyway if PyMapping_Check() had returned False.

It looks like PyMapping_Check() already checks for the presence of a fairly arbitrary special operation (mp_subscript). It sounds fine to replace that with a check for the presence of a keys() or items() method (I'm not sure which one is more representative). I wish the check can be done fast -- but I fear that it'll involve a dict lookup. So be it.

Rather than introduce "fixes" that break code and hurt performance, I think it would be better to deprecate PyMapping_Check() and wait for a fast, clean C version of the ABCs (that is supposed to be our one obvious way to do it).
FWIW, the spreadsheet example has been around for years and I know of more than one private company that has made heavy use of code modeled on that example (not for spreadsheets, but as a hook for eval). So, I don't think the new "keys" check should be backported.

> Rather than introduce "fixes" that break code and hurt performance, I
> think it would be better to deprecate PyMapping_Check() and wait for a
> fast, clean C version of the ABCs (that is supposed to be our one
> obvious way to do it).
Do you also advocate deprecating PySequence_Check()? Both are useful
APIs to know what you're dealing with.
As for the "clean C version of the ABCs", I'm afraid we could wait quite
a bit, since that's a lot more work and nobody seems really interested
in the matter.
> FWIW, the spreadsheet example has been around for years and I know of
> more than one private company that has made heavy use of code modeled
> on that example (not for spreadsheets, but as a hook for eval). So, I
> don't think the new "keys" check should be backported.
Well, I'm not proposing to backport it, but to fix things in 3.2.

> Do you also advocate deprecating PySequence_Check()?
Perhaps just document PyMapping_Check() as being less informative than before (now it has false positives for sequences).
> As for the "clean C version of the ABCs",
> I'm afraid we could wait quite a bit
That may be true. I hope not. The ABCs were meant to solve exactly this problem. At this point, I would rather ignore the problem for 3.2 than to implement a less clean alternative.

> > As for the "clean C version of the ABCs",
> > I'm afraid we could wait quite a bit
>
> That may be true. I hope not. The ABCs were meant to solve exactly
> this problem. At this point, I would rather ignore the problem for
> 3.2 than to implement a less clean alternative.
Also, please note that checking for ABCs would not solve the
test_builtin failure, since the class there doesn't implement the
Mapping ABC (and doesn't claim to either).

Proposed wording doesn't looks much informative to me. Maybe just say that PyMapping_Check() also returns 1 for sequences that support slicing? And recommend `PyMapping_Check() && !PySequence_Check()` for true mapping test?