Repository: numpy/numpy

Issue:
loadtxt(comments=None) considers the string 'None' as a comment symbol
Numpy 1.8.2
`numpy.loadtxt(comments=None)` considers the string `'None'` as a comment symbol.

Expected behaviour:
`comments=None` should indicate that there are no comments symbols. Certainly not cast `None` to a string, what is this, Javascript?

Masked array view fails if structured dtype has datetime component
A view as `numpy.ma.MaskedArray` fails if the array has a structured dtype, including at least one part that is `datetime64`, as follows:

```
$ python3.3
Python 3.3.2+ (default, Feb 28 2014, 00:52:16) 
[GCC 4.8.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> import numpy, numpy.ma, numpy.version
>>> numpy.version.version
'1.8.0'
>>> A = numpy.empty(shape=(5,), dtype=[("A", "<f4"), ("B", "datetime64[ms]")])
>>> A.view(numpy.ma.MaskedArray)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/gerrit/venv/python-3.3/lib/python3.3/site-packages/numpy/ma/core.py", line 2800, in __array_finalize__
    self._fill_value = _check_fill_value(None, self.dtype)
  File "/home/gerrit/venv/python-3.3/lib/python3.3/site-packages/numpy/ma/core.py", line 402, in _check_fill_value
    dtype=ndtype,)
ValueError: Error parsing datetime string "?" at position 0
```



Discussion:

A simpler `numpy.ma.MaskedArray([], shape=(0,), dtype=[("A", "<f4"), ("B", "datetime64[ms]")])` fails with the same `ValueError`.

Just tested with with the latest git repository.  Problem still exists.


Resolving patch:
diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py
--- a/numpy/lib/npyio.py
+++ b/numpy/lib/npyio.py
@@ -717,7 +717,8 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None,
 
     """
     # Type conversions for Py3 convenience
-    comments = asbytes(comments)
+    if comments is not None:
+        comments = asbytes(comments)
     user_converters = converters
     if delimiter is not None:
         delimiter = asbytes(delimiter)
@@ -790,7 +791,10 @@ def pack_items(items, packing):
 
     def split_line(line):
         """Chop off comments, strip, and split at delimiter."""
-        line = asbytes(line).split(comments)[0].strip(asbytes('\r\n'))
+        if comments is None:
+            line = asbytes(line).strip(asbytes('\r\n'))
+        else:
+            line = asbytes(line).split(comments)[0].strip(asbytes('\r\n'))
         if line:
             return line.split(delimiter)
         else:
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -145,10 +145,15 @@ class MaskError(MAError):
                   'S' : 'N/A',
                   'u' : 999999,
                   'V' : '???',
-                  'U' : 'N/A',
-                  'M8[D]' : np.datetime64('NaT', 'D'),
-                  'M8[us]' : np.datetime64('NaT', 'us')
+                  'U' : 'N/A'
                   }
+
+# Add datetime64 and timedelta64 types
+for v in ["Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns", "ps",
+          "fs", "as"]:
+    default_filler["M8[" + v + "]"] = np.datetime64("NaT", v)
+    default_filler["m8[" + v + "]"] = np.timedelta64("NaT", v)
+
 max_filler = ntypes._minvals
 max_filler.update([(k, -np.inf) for k in [np.float32, np.float64]])
 min_filler = ntypes._maxvals
@@ -194,7 +199,7 @@ def default_fill_value(obj):
     999999
     >>> np.ma.default_fill_value(np.array([1.1, 2., np.pi]))
     1e+20
-    >>>  np.ma.default_fill_value(np.dtype(complex))
+    >>> np.ma.default_fill_value(np.dtype(complex))
     (1e+20+0j)
 
     """
@@ -203,7 +208,7 @@ def default_fill_value(obj):
     elif isinstance(obj, np.dtype):
         if obj.subdtype:
             defval = default_filler.get(obj.subdtype[0].kind, '?')
-        elif obj.kind == 'M':
+        elif obj.kind in 'Mm':
             defval = default_filler.get(obj.str[1:], '?')
         else:
             defval = default_filler.get(obj.kind, '?')


Test patch:
diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py
--- a/numpy/lib/tests/test_io.py
+++ b/numpy/lib/tests/test_io.py
@@ -775,6 +775,14 @@ def test_bad_line(self):
         # Check for exception and that exception contains line number
         assert_raises_regex(ValueError, "3", np.loadtxt, c)
 
+    def test_none_as_string(self):
+        # gh-5155, None should work as string when format demands it
+        c = TextIO()
+        c.write('100,foo,200\n300,None,400')
+        c.seek(0)
+        dt = np.dtype([('x', int), ('a', 'S10'), ('y', int)])
+        data = np.loadtxt(c, delimiter=',', dtype=dt, comments=None)
+
 
 class Testfromregex(TestCase):
     # np.fromregex expects files opened in binary mode.
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -1489,6 +1489,21 @@ def test_fillvalue_exotic_dtype(self):
         control = np.array((0, 0, 0), dtype="int, float, float").astype(ndtype)
         assert_equal(_check_fill_value(0, ndtype), control)
 
+    def test_fillvalue_datetime_timedelta(self):
+        # Test default fillvalue for datetime64 and timedelta64 types.
+        # See issue #4476, this would return '?' which would cause errors
+        # elsewhere
+
+        for timecode in ("as", "fs", "ps", "ns", "us", "ms", "s", "m",
+                         "h", "D", "W", "M", "Y"):
+            control = numpy.datetime64("NaT", timecode)
+            test = default_fill_value(numpy.dtype("<M8[" + timecode + "]"))
+            assert_equal(test, control)
+
+            control = numpy.timedelta64("NaT", timecode)
+            test = default_fill_value(numpy.dtype("<m8[" + timecode + "]"))
+            assert_equal(test, control)
+
     def test_extremum_fill_value(self):
         # Tests extremum fill values for flexible type.
         a = array([(1, (2, 3)), (4, (5, 6))],