From ba0b92eecb474790d84c424f15861797dd25e507 Mon Sep 17 00:00:00 2001
From: Aviram Hassan <aviramyhassan@gmail.com>
Date: Thu, 13 Jan 2022 19:08:18 +0200
Subject: [PATCH] Fix conversion of non-mapping sequences into `PyMapping` (i.e
 `PyStr` can be cast to `PyMapping`) due to `PyMapping_Check` returning true
 for sequences.

---
 CHANGELOG.md         |  1 +
 src/types/mapping.rs | 16 +++++++++++++---
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index eba57ef4bb7..4f807e84099 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -62,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Fix undefined behavior in `PySlice::indices`. [#2061](https://github.com/PyO3/pyo3/pull/2061)
 - Use the Rust function path for `wrap_pymodule!` of a `#[pymodule]` with a `#[pyo3(name = "..")]` attribute, not the Python name. [#2081](https://github.com/PyO3/pyo3/pull/2081)
 - Fix panic in `#[pyfunction]` generated code when a required argument following an `Option` was not provided.  [#2093](https://github.com/PyO3/pyo3/pull/2093)
+- Fix conversion of non-mapping sequences into `PyMapping` (i.e `PyStr` can be cast to `PyMapping`) due to `PyMapping_Check` returning true for sequences. [#2098](https://github.com/PyO3/pyo3/pull/2098)
 
 ## [0.15.1] - 2021-11-19
 
diff --git a/src/types/mapping.rs b/src/types/mapping.rs
index 0ff430a004c..cf71ac8c1af 100644
--- a/src/types/mapping.rs
+++ b/src/types/mapping.rs
@@ -101,7 +101,9 @@ impl<'v> PyTryFrom<'v> for PyMapping {
     fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyMapping, PyDowncastError<'v>> {
         let value = value.into();
         unsafe {
-            if ffi::PyMapping_Check(value.as_ptr()) != 0 {
+            if ffi::PyMapping_Check(value.as_ptr()) != 0
+                && ffi::PySequence_Check(value.as_ptr()) == 0
+            {
                 Ok(<PyMapping as PyTryFrom>::try_from_unchecked(value))
             } else {
                 Err(PyDowncastError::new(value, "Mapping"))
@@ -142,8 +144,8 @@ mod tests {
     use std::collections::HashMap;
 
     use crate::{
-        exceptions::PyKeyError,
-        types::{PyDict, PyTuple},
+        exceptions::{PyKeyError, PyTypeError},
+        types::{PyDict, PyString, PyTuple},
         Python,
     };
 
@@ -299,4 +301,12 @@ mod tests {
             assert_eq!(mapping_ref.get_refcnt(), 2);
         })
     }
+
+    #[test]
+    fn test_not_mapping() {
+        Python::with_gil(|py| {
+            let str = PyString::new(py, "hello, world");
+            str.downcast::<PyMapping>().unwrap_err();
+        });
+    }
 }