Repository: cupy/cupy
Pull request: Implement `__format__` in `ndarray`
Closes #4532

Problem statement:
Unsupported f-string format
`f"{element}:.2f"` isn't a valid format string

* Code to reproduce
```python
import numpy as np
import cupy as cp

arr = [1.12345]
np_arr = np.asarray(arr)
cp_arr = cp.asarray(arr)

print(f"With 2 precisions: {np_arr[0]:.2f})                                # With 2 precisions: 1.12

print(f"With 2 precisions: {cp_arr[0]:.2f}) 

Traceback (most recent call last):
  File "er.py", line 10, in <module>
    print(f"With 2 precisions: {cp_arr[0]:.2f}")
TypeError: unsupported format string passed to cupy.core.core.ndarray.__format__
```

* Error messages, stack traces, or logs
Conditions (you can just paste the output of `python -c 'import cupy; cupy.show_config()'`)
```console
CuPy Version          : 6.0.0
CUDA Root             : /usr/local/cuda
CUDA Build Version    : 10000
CUDA Driver Version   : 11010
CUDA Runtime Version  : 10000
cuDNN Build Version   : 7301
cuDNN Version         : 7605
NCCL Build Version    : 1000
NCCL Runtime Version  : (unknown)
```

Hints from issue discussion:

Commit message: Update tests/cupy_tests/core_tests/test_ndarray.py

Co-authored-by: Toshiki Kataoka <tos.lunar@gmail.com>

Patch to review:
diff --git a/cupy/core/core.pyx b/cupy/core/core.pyx
index c6366057bd1..b983cfe8a64 100644
--- a/cupy/core/core.pyx
+++ b/cupy/core/core.pyx
@@ -1508,6 +1508,9 @@ cdef class ndarray:
     def __str__(self):
         return str(self.get())
 
+    def __format__(self, format_spec):
+        return self.get().__format__(format_spec)
+
     # -------------------------------------------------------------------------
     # Methods outside of the ndarray main documentation
     # -------------------------------------------------------------------------
diff --git a/tests/cupy_tests/core_tests/test_ndarray.py b/tests/cupy_tests/core_tests/test_ndarray.py
index ae1a117675a..44586e24e1a 100644
--- a/tests/cupy_tests/core_tests/test_ndarray.py
+++ b/tests/cupy_tests/core_tests/test_ndarray.py
@@ -570,6 +570,11 @@ def test_bytes_tobytes_scalar_array(self, xp, dtype):
         x = xp.array(3, dtype)
         return bytes(x)
 
+    @testing.numpy_cupy_equal()
+    def test_format(self, xp):
+        x = xp.array(1.12345)
+        return format(x, '.2f')
+
 
 @testing.gpu
 class TestNdarrayImplicitConversion(unittest.TestCase):


Code review comments:
diff_hunk: @@ -1508,6 +1508,9 @@ cdef class ndarray:
     def __str__(self):
         return str(self.get())
 
+    def __format__(self, format_spec):
+        return self.get().__format__(format_spec)
path: cupy/core/core.pyx
text: @user1:
For consistency around the lines, the builtin `format` looks better here, too.
```suggestion
        return format(self.get(), format_spec)
```

Do you have other pros/cons in mind?  e.g. performance

@author:
I think that since this implies device synchronization, there is not much to care about. Thanks!