Repository: pandas-dev/pandas

Issue:
Comparison to string is broken for nullable int
#### Code Sample, a copy-pastable example if possible

```python
import pandas as pd
df = pd.DataFrame({'x': [1, None]})
df.x.astype(pd.Int64Dtype()) == 'a'
```
#### Problem description

The above raises TypeError on line 627 in integer.py:

```python
result[mask] = op_name == "ne"
```
Here, the result is bool. Here is the exception:

Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "C:\Python37\lib\site-packages\pandas\core\ops.py", line 1731, in wrapper
    return dispatch_to_extension_op(op, self, other)
  File "C:\Python37\lib\site-packages\pandas\core\ops.py", line 1220, in dispatch_to_extension_op
    res_values = op(new_left, new_right)
  File "C:\Python37\lib\site-packages\pandas\core\arrays\integer.py", line 564, in cmp_method
    result[mask] = True if op_name == 'ne' else False
TypeError: 'bool' object does not support item assignment

The above works  as expected for float64 and other numeric types.

#### Expected Output

0    False
1    False
Name: x, dtype: bool

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit           : None
python           : 3.7.0.final.0
python-bits      : 64
OS               : Windows
OS-release       : 10
machine          : AMD64
processor        : Intel64 Family 6 Model 158 Stepping 9, GenuineIntel
byteorder        : little
LC_ALL           : None
LANG             : None
LOCALE           : None.None
pandas           : 0.25.1
numpy            : 1.16.4
pytz             : 2019.1
dateutil         : 2.8.0
pip              : 19.2.3
setuptools       : 39.0.1
Cython           : None
pytest           : None
hypothesis       : None
sphinx           : None
blosc            : None
feather          : None
xlsxwriter       : None
lxml.etree       : 4.3.4
html5lib         : None
pymysql          : None
psycopg2         : None
jinja2           : None
IPython          : None
pandas_datareader: None
bs4              : None
bottleneck       : None
fastparquet      : None
gcsfs            : None
lxml.etree       : 4.3.4
matplotlib       : 3.1.1
numexpr          : None
odfpy            : None
openpyxl         : None
pandas_gbq       : None
pyarrow          : None
pytables         : None
s3fs             : None
scipy            : 1.3.0
sqlalchemy       : 1.3.5
tables           : None
xarray           : None
xlrd             : None
xlwt             : None
xlsxwriter       : None

</details>



Discussion:

Resolving patch:
diff --git a/pandas/conftest.py b/pandas/conftest.py
--- a/pandas/conftest.py
+++ b/pandas/conftest.py
@@ -654,6 +654,24 @@ def any_int_dtype(request):
     return request.param
 
 
+@pytest.fixture(params=ALL_EA_INT_DTYPES)
+def any_nullable_int_dtype(request):
+    """
+    Parameterized fixture for any nullable integer dtype.
+
+    * 'UInt8'
+    * 'Int8'
+    * 'UInt16'
+    * 'Int16'
+    * 'UInt32'
+    * 'Int32'
+    * 'UInt64'
+    * 'Int64'
+    """
+
+    return request.param
+
+
 @pytest.fixture(params=ALL_REAL_DTYPES)
 def any_real_dtype(request):
     """
diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py
--- a/pandas/core/arrays/integer.py
+++ b/pandas/core/arrays/integer.py
@@ -26,6 +26,7 @@
 from pandas.core import nanops, ops
 from pandas.core.algorithms import take
 from pandas.core.arrays import ExtensionArray, ExtensionOpsMixin
+from pandas.core.ops import invalid_comparison
 from pandas.core.ops.common import unpack_zerodim_and_defer
 from pandas.core.tools.numeric import to_numeric
 
@@ -646,7 +647,11 @@ def cmp_method(self, other):
             with warnings.catch_warnings():
                 warnings.filterwarnings("ignore", "elementwise", FutureWarning)
                 with np.errstate(all="ignore"):
-                    result = op(self._data, other)
+                    method = getattr(self._data, f"__{op_name}__")
+                    result = method(other)
+
+                    if result is NotImplemented:
+                        result = invalid_comparison(self._data, other, op)
 
             # nans propagate
             if mask is None:


Test patch:
diff --git a/pandas/tests/extension/test_integer.py b/pandas/tests/extension/test_integer.py
--- a/pandas/tests/extension/test_integer.py
+++ b/pandas/tests/extension/test_integer.py
@@ -168,6 +168,27 @@ def check_opname(self, s, op_name, other, exc=None):
     def _compare_other(self, s, data, op_name, other):
         self.check_opname(s, op_name, other)
 
+    def test_compare_to_string(self, any_nullable_int_dtype):
+        # GH 28930
+        s = pd.Series([1, None], dtype=any_nullable_int_dtype)
+        result = s == "a"
+        expected = pd.Series([False, False])
+
+        self.assert_series_equal(result, expected)
+
+    def test_compare_to_int(self, any_nullable_int_dtype, all_compare_operators):
+        # GH 28930
+        s1 = pd.Series([1, 2, 3], dtype=any_nullable_int_dtype)
+        s2 = pd.Series([1, 2, 3], dtype="int")
+
+        method = getattr(s1, all_compare_operators)
+        result = method(2)
+
+        method = getattr(s2, all_compare_operators)
+        expected = method(2)
+
+        self.assert_series_equal(result, expected)
+
 
 class TestInterface(base.BaseInterfaceTests):
     pass