Repository: pandas-dev/pandas
Pull request: Fix Series construction with dtype=str
TST: Added test for construction Series with dtype=str 
BUG: Handles case where data is scalar 
DOC: added changes to whatsnew/v0.23.0.txt

Checklist for other PRs (remove this part if you are doing a PR for the pandas documentation sprint):

- [x] closes #19853
- [x] tests added / passed
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Problem statement:
BUG: invalid constrution of a Series with dtype=str
```python
pd.Series('', dtype=str, index=range(1000))
```

throws a `ValueError` with the following message:

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\james\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pandas\core\series.py", line 266, in __init__
    data = SingleBlockManager(data, index, fastpath=True)
  File "C:\Users\james\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pandas\core\internals.py", line 4402, in __init__
    fastpath=True)
  File "C:\Users\james\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pandas\core\internals.py", line 2957, in make_block
    return klass(values, ndim=ndim, fastpath=fastpath, placement=placement)
  File "C:\Users\james\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pandas\core\internals.py", line 2082, in __init__
    placement=placement, **kwargs)
  File "C:\Users\james\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pandas\core\internals.py", line 111, in __init__
    raise ValueError('Wrong number of dimensions')
ValueError: Wrong number of dimensions
```

Would it be possible to fix the behavior to initialize the series to `''` (or at least provide a clearer message)?

Hints from issue discussion:
``pd.Series('', dtype=object, index=range(1000))``
That's ok. String uses 'object' dtype.
this takes a different path in master. We pretty much treat str as object. So this is a construction bug.
```
In [4]: pd.Series('',  index=range(1000), dtype=str)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-3bd08f17c610> in <module>()
----> 1 pd.Series('',  index=range(1000), dtype=str)

~/pandas/pandas/core/series.py in __init__(self, data, index, dtype, name, copy, fastpath)
    237             else:
    238                 data = _sanitize_array(data, index, dtype, copy,
--> 239                                        raise_cast_failure=True)
    240 
    241                 data = SingleBlockManager(data, index, fastpath=True)

~/pandas/pandas/core/series.py in _sanitize_array(data, index, dtype, copy, raise_cast_failure)
   3260         # GH 16605
   3261         # If not empty convert the data to dtype
-> 3262         if not isna(data).all():
   3263             data = np.array(data, dtype=dtype, copy=False)
   3264 

AttributeError: 'bool' object has no attribute 'all'

```

@jamesqo there is no reason to specify a dtype here as this will be inferred to ``object`` dtype anyhow (``str`` as I said above is pretty much an alias for ``object`` dtype).

a PR to fix is welcome.
@jamesqo note that setting the string ``''`` like this doesn't have much utilitiy. pandas has a full suite of string operations that are all NaN aware.

Commit message: DOC: added changes to whatsnew/v0.23.0.txt

Patch to review:
diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt
index 4179277291478..6a6c3b2583cb1 100644
--- a/doc/source/whatsnew/v0.23.0.txt
+++ b/doc/source/whatsnew/v0.23.0.txt
@@ -714,6 +714,7 @@ Other API Changes
 - ``pd.to_datetime('today')`` now returns a datetime, consistent with ``pd.Timestamp('today')``; previously ``pd.to_datetime('today')`` returned a ``.normalized()`` datetime (:issue:`19935`)
 - :func:`Series.str.replace` now takes an optional `regex` keyword which, when set to ``False``, uses literal string replacement rather than regex replacement (:issue:`16808`)
 - :func:`DatetimeIndex.strftime` and :func:`PeriodIndex.strftime` now return an ``Index`` instead of a numpy array to be consistent with similar accessors (:issue:`20127`)
+``Series`` construction with a ``string``, ``dtype=str`` specified, and ``index`` specified will now return an ``object`` dtyped ``Series``, previously this would raise an AttributeError (:issue:`19853`)
 
 .. _whatsnew_0230.deprecations:
 
diff --git a/pandas/core/series.py b/pandas/core/series.py
index e4801242073a2..8851152406eb1 100644
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -4059,9 +4059,14 @@ def _try_cast(arr, take_fast_path):
     if issubclass(subarr.dtype.type, compat.string_types):
         # GH 16605
         # If not empty convert the data to dtype
-        if not isna(data).all():
-            data = np.array(data, dtype=dtype, copy=False)
-
-        subarr = np.array(data, dtype=object, copy=copy)
+        try:
+            all_elements_na = isna(data).all()
+        except AttributeError:
+            # GH 19853: If data is a scalar, subarr has already the result
+            pass
+        else:
+            if not all_elements_na:
+                data = np.array(data, dtype=dtype, copy=False)
+            subarr = np.array(data, dtype=object, copy=copy)
 
     return subarr
diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py
index e0bfe41645a3f..ca8fd1322fb2f 100644
--- a/pandas/tests/series/test_constructors.py
+++ b/pandas/tests/series/test_constructors.py
@@ -110,6 +110,11 @@ def test_constructor_empty(self, input_class):
             empty2 = Series(input_class(), index=lrange(10), dtype='float64')
             assert_series_equal(empty, empty2)
 
+            # GH 19853 : with empty string, index and dtype str
+            empty = Series('', dtype='str', index=range(3))
+            assert empty.all() == ''
+            assert (empty == Series('', index=range(3))).all()
+
     @pytest.mark.parametrize('input_arg', [np.nan, float('nan')])
     def test_constructor_nan(self, input_arg):
         empty = Series(dtype='float64', index=lrange(10))


Code review comments:
diff_hunk: @@ -4059,9 +4059,14 @@ def _try_cast(arr, take_fast_path):
     if issubclass(subarr.dtype.type, compat.string_types):
         # GH 16605
         # If not empty convert the data to dtype
-        if not isna(data).all():
path: pandas/core/series.py
text: @user1:
you don't need all this, just change to use ``np.any``
```
In [3]: np.any(pd.isna(''))
Out[3]: False
```

diff_hunk: @@ -714,6 +714,7 @@ Other API Changes
 - ``pd.to_datetime('today')`` now returns a datetime, consistent with ``pd.Timestamp('today')``; previously ``pd.to_datetime('today')`` returned a ``.normalized()`` datetime (:issue:`19935`)
 - :func:`Series.str.replace` now takes an optional `regex` keyword which, when set to ``False``, uses literal string replacement rather than regex replacement (:issue:`16808`)
 - :func:`DatetimeIndex.strftime` and :func:`PeriodIndex.strftime` now return an ``Index`` instead of a numpy array to be consistent with similar accessors (:issue:`20127`)
+``Series`` construction with a ``string``, ``dtype=str`` specified, and ``index`` specified will now return an ``object`` dtyped ``Series``, previously this would raise an AttributeError (:issue:`19853`)
path: doc/source/whatsnew/v0.23.0.txt
text: @user1:
referencde ``:class:`Series` ``

you can simplify this, just say construction with ``dtype=str`` previously raised in some cases.

diff_hunk: @@ -110,6 +110,11 @@ def test_constructor_empty(self, input_class):
             empty2 = Series(input_class(), index=lrange(10), dtype='float64')
             assert_series_equal(empty, empty2)
 
+            # GH 19853 : with empty string, index and dtype str
+            empty = Series('', dtype='str', index=range(3))
+            assert empty.all() == ''
path: pandas/tests/series/test_constructors.py
text: @user1:
construct the resultant series and use ``assert_series_equal``