Repository: scikit-hep/awkward

Issue:
`ak.from_numpy()` doesn't handle masked arrays
### Version of Awkward Array

main

### Description and code to reproduce

## V2
```python
>>> import awkward as ak
>>> import numpy as np
>>> np_array = np.ma.MaskedArray([[1, 2, 3], [4, 5, 6]], mask=[[False, True, False], [True, True, False]])
>>> ak_array = ak.from_numpy(np_array)
>>> ak.to_numpy(ak_array, allow_missing=False)
array([[1, 2, 3],
       [4, 5, 6]])
```

## V1
```python
>>> import awkward as ak
>>> import numpy as np
>>> np_array = np.ma.MaskedArray([[1, 2, 3], [4, 5, 6]], mask=[[False, True, False], [True, True, False]])
>>> ak_array = ak.from_numpy(np_array)
>>> ak.to_numpy(ak_array, allow_missing=False)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In [1], line 5
      3 np_array = np.ma.MaskedArray([[1, 2, 3], [4, 5, 6]], mask=[[False, True, False], [True, True, False]])
      4 ak_array = ak.from_numpy(np_array)
----> 5 ak.to_numpy(ak_array, allow_missing=False)

File /tmp/tmp.FBCxvkBC4Y/.direnv/python-3.10.4/lib/python3.10/site-packages/awkward/operations/convert.py:196, in to_numpy(array, allow_missing)
    193     return array
    195 elif isinstance(array, ak.highlevel.Array):
--> 196     return to_numpy(array.layout, allow_missing=allow_missing)
    198 elif isinstance(array, ak.highlevel.Record):
    199     out = array.layout

File /tmp/tmp.FBCxvkBC4Y/.direnv/python-3.10.4/lib/python3.10/site-packages/awkward/operations/convert.py:315, in to_numpy(array, allow_missing)
    312             return content
    314 elif isinstance(array, ak.layout.RegularArray):
--> 315     out = to_numpy(array.content, allow_missing=allow_missing)
    316     head, tail = out.shape[0], out.shape[1:]
    317     if array.size == 0:

File /tmp/tmp.FBCxvkBC4Y/.direnv/python-3.10.4/lib/python3.10/site-packages/awkward/operations/convert.py:302, in to_numpy(array, allow_missing)
    300         return numpy.ma.MaskedArray(data, mask)
    301     else:
--> 302         raise ValueError(
    303             "ak.to_numpy cannot convert 'None' values to "
    304             "np.ma.MaskedArray unless the "
    305             "'allow_missing' parameter is set to True"
    306             + ak._util.exception_suffix(__file__)
    307         )
    308 else:
    309     if allow_missing:

ValueError: ak.to_numpy cannot convert 'None' values to np.ma.MaskedArray unless the 'allow_missing' parameter is set to True

(https://github.com/scikit-hep/awkward-1.0/blob/1.10.1/src/awkward/operations/convert.py#L306)
```


Discussion:

Resolving patch:
diff --git a/src/awkward/_util.py b/src/awkward/_util.py
--- a/src/awkward/_util.py
+++ b/src/awkward/_util.py
@@ -622,7 +622,7 @@ def recurse(array, mask=None):
 
         if regulararray and len(array.shape) > 1:
             return ak.contents.RegularArray(
-                recurse(array.reshape((-1,) + array.shape[2:])),
+                recurse(array.reshape((-1,) + array.shape[2:]), mask),
                 array.shape[1],
                 array.shape[0],
             )


Test patch:
diff --git a/tests/test_1735-from-numpy-mask.py b/tests/test_1735-from-numpy-mask.py
new file mode 100644
--- /dev/null
+++ b/tests/test_1735-from-numpy-mask.py
@@ -0,0 +1,15 @@
+# BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE
+
+import numpy as np  # noqa: F401
+import pytest  # noqa: F401
+
+import awkward as ak  # noqa: F401
+
+
+def test():
+    np_array = np.ma.MaskedArray(
+        [[1, 2, 3], [4, 5, 6]], mask=[[False, True, False], [True, True, False]]
+    )
+    ak_array = ak.from_numpy(np_array)
+
+    assert ak_array.to_list() == [[1, None, 3], [None, None, 6]]