Repository: modin-project/modin

Issue:
`Series` is missing "right" operators (e.g. `__rsub__`)

### Describe the problem
<!-- Describe the problem clearly here. -->
The `Series` API is missing some operators from the `__r<op>__` family:

- __rsub__
- __rtruediv__
- __rpow__
- __rmul__
- __rmod__
- __rfloordiv__
- __rdivmod__
- __rdiv__
- __radd__
- __rxor__
- __ror__



Discussion:

Resolving patch:
diff --git a/modin/pandas/base.py b/modin/pandas/base.py
--- a/modin/pandas/base.py
+++ b/modin/pandas/base.py
@@ -3245,6 +3245,9 @@ def __abs__(self):
     def __and__(self, other):
         return self._binary_op("__and__", other, axis=0)
 
+    def __rand__(self, other):
+        return self._binary_op("__and__", other, axis=0)
+
     def __array__(self, dtype=None):
         arr = self.to_numpy(dtype)
         return arr
@@ -3287,8 +3290,8 @@ def __eq__(self, other):
     def __finalize__(self, other, method=None, **kwargs):
         return self._default_to_pandas("__finalize__", other, method=method, **kwargs)
 
-    def __ge__(self, other):
-        return self.ge(other)
+    def __ge__(self, right):
+        return self.ge(right)
 
     def __getitem__(self, key):
         if len(self) == 0:
@@ -3353,8 +3356,8 @@ def compute_offset(value):
     def __getstate__(self):
         return self._default_to_pandas("__getstate__")
 
-    def __gt__(self, other):
-        return self.gt(other)
+    def __gt__(self, right):
+        return self.gt(right)
 
     def __invert__(self):
         if not all(is_numeric_dtype(d) for d in self._get_dtypes()):
@@ -3365,8 +3368,8 @@ def __invert__(self):
             )
         return self.__constructor__(query_compiler=self._query_compiler.invert())
 
-    def __le__(self, other):
-        return self.le(other)
+    def __le__(self, right):
+        return self.le(right)
 
     def __len__(self):
         """Gets the length of the DataFrame.
@@ -3376,8 +3379,8 @@ def __len__(self):
         """
         return len(self.index)
 
-    def __lt__(self, other):
-        return self.lt(other)
+    def __lt__(self, right):
+        return self.lt(right)
 
     def __ne__(self, other):
         return self.ne(other)
@@ -3404,6 +3407,9 @@ def __nonzero__(self):
     def __or__(self, other):
         return self._binary_op("__or__", other, axis=0)
 
+    def __ror__(self, other):
+        return self._binary_op("__or__", other, axis=0)
+
     def __sizeof__(self):
         return self._default_to_pandas("__sizeof__")
 
@@ -3413,6 +3419,9 @@ def __str__(self):  # pragma: no cover
     def __xor__(self, other):
         return self._binary_op("__xor__", other, axis=0)
 
+    def __rxor__(self, other):
+        return self._binary_op("__xor__", other, axis=0)
+
     @property
     def blocks(self):
         def blocks(df):
diff --git a/modin/pandas/series.py b/modin/pandas/series.py
--- a/modin/pandas/series.py
+++ b/modin/pandas/series.py
@@ -122,6 +122,9 @@ def _prepare_inter_op(self, other):
     def __add__(self, right):
         return self.add(right)
 
+    def __radd__(self, left):
+        return self.add(left)
+
     def __and__(self, other):
         new_self, new_other = self._prepare_inter_op(other)
         return super(Series, new_self).__and__(new_other)
@@ -153,15 +156,24 @@ def __delitem__(self, key):
     def __div__(self, right):
         return self.div(right)
 
+    def __rdiv__(self, left):
+        return self.rdiv(left)
+
     def __divmod__(self, right):
         return self.divmod(right)
 
+    def __rdivmod__(self, left):
+        return self.rdivmod(left)
+
     def __float__(self):
         return float(self.squeeze())
 
     def __floordiv__(self, right):
         return self.floordiv(right)
 
+    def __rfloordiv__(self, right):
+        return self.rfloordiv(right)
+
     def _getitem(self, key):
         if isinstance(key, Series) and key.dtype == np.bool:
             # This ends up being significantly faster than looping through and getting
@@ -222,9 +234,15 @@ def __iter__(self):
     def __mod__(self, right):
         return self.mod(right)
 
+    def __rmod__(self, left):
+        return self.rmod(left)
+
     def __mul__(self, right):
         return self.mul(right)
 
+    def __rmul__(self, left):
+        return self.rmul(left)
+
     def __or__(self, other):
         new_self, new_other = self._prepare_inter_op(other)
         return super(Series, new_self).__or__(new_other)
@@ -232,6 +250,9 @@ def __or__(self, other):
     def __pow__(self, right):
         return self.pow(right)
 
+    def __rpow__(self, left):
+        return self.rpow(left)
+
     def __repr__(self):
         num_rows = pandas.get_option("max_rows") or 60
         num_cols = pandas.get_option("max_columns") or 20
@@ -269,9 +290,15 @@ def __setitem__(self, key, value):
     def __sub__(self, right):
         return self.sub(right)
 
+    def __rsub__(self, left):
+        return self.rsub(left)
+
     def __truediv__(self, right):
         return self.truediv(right)
 
+    def __rtruediv__(self, left):
+        return self.rtruediv(left)
+
     __iadd__ = __add__
     __imul__ = __add__
     __ipow__ = __pow__


Test patch:
diff --git a/modin/pandas/test/test_series.py b/modin/pandas/test/test_series.py
--- a/modin/pandas/test/test_series.py
+++ b/modin/pandas/test/test_series.py
@@ -44,23 +44,45 @@
 matplotlib.use("Agg")
 
 
+def get_rop(op):
+    if op.startswith("__") and op.endswith("__"):
+        return "__r" + op[2:]
+    else:
+        return None
+
+
 def inter_df_math_helper(modin_series, pandas_series, op):
+    inter_df_math_helper_one_side(modin_series, pandas_series, op)
+    rop = get_rop(op)
+    if rop:
+        inter_df_math_helper_one_side(modin_series, pandas_series, rop)
+
+
+def inter_df_math_helper_one_side(modin_series, pandas_series, op):
+    try:
+        pandas_attr = getattr(pandas_series, op)
+    except Exception as e:
+        with pytest.raises(type(e)):
+            _ = getattr(modin_series, op)
+        return
+    modin_attr = getattr(modin_series, op)
+
     try:
-        pandas_result = getattr(pandas_series, op)(4)
+        pandas_result = pandas_attr(4)
     except Exception as e:
         with pytest.raises(type(e)):
-            repr(getattr(modin_series, op)(4))  # repr to force materialization
+            repr(modin_attr(4))  # repr to force materialization
     else:
-        modin_result = getattr(modin_series, op)(4)
+        modin_result = modin_attr(4)
         df_equals(modin_result, pandas_result)
 
     try:
-        pandas_result = getattr(pandas_series, op)(4.0)
+        pandas_result = pandas_attr(4.0)
     except Exception as e:
         with pytest.raises(type(e)):
-            repr(getattr(modin_series, op)(4.0))  # repr to force materialization
+            repr(modin_attr(4.0))  # repr to force materialization
     else:
-        modin_result = getattr(modin_series, op)(4.0)
+        modin_result = modin_attr(4.0)
         df_equals(modin_result, pandas_result)
 
     # These operations don't support non-scalar `other` or have a strange behavior in
@@ -81,37 +103,33 @@ def inter_df_math_helper(modin_series, pandas_series, op):
         return
 
     try:
-        pandas_result = getattr(pandas_series, op)(pandas_series)
+        pandas_result = pandas_attr(pandas_series)
     except Exception as e:
         with pytest.raises(type(e)):
-            repr(
-                getattr(modin_series, op)(modin_series)
-            )  # repr to force materialization
+            repr(modin_attr(modin_series))  # repr to force materialization
     else:
-        modin_result = getattr(modin_series, op)(modin_series)
+        modin_result = modin_attr(modin_series)
         df_equals(modin_result, pandas_result)
 
     list_test = random_state.randint(RAND_LOW, RAND_HIGH, size=(modin_series.shape[0]))
     try:
-        pandas_result = getattr(pandas_series, op)(list_test)
+        pandas_result = pandas_attr(list_test)
     except Exception as e:
         with pytest.raises(type(e)):
-            repr(getattr(modin_series, op)(list_test))  # repr to force materialization
+            repr(modin_attr(list_test))  # repr to force materialization
     else:
-        modin_result = getattr(modin_series, op)(list_test)
+        modin_result = modin_attr(list_test)
         df_equals(modin_result, pandas_result)
 
     series_test_modin = pd.Series(list_test, index=modin_series.index)
     series_test_pandas = pandas.Series(list_test, index=pandas_series.index)
     try:
-        pandas_result = getattr(pandas_series, op)(series_test_pandas)
+        pandas_result = pandas_attr(series_test_pandas)
     except Exception as e:
         with pytest.raises(type(e)):
-            repr(
-                getattr(modin_series, op)(series_test_modin)
-            )  # repr to force materialization
+            repr(modin_attr(series_test_modin))  # repr to force materialization
     else:
-        modin_result = getattr(modin_series, op)(series_test_modin)
+        modin_result = modin_attr(series_test_modin)
         df_equals(modin_result, pandas_result)
 
     # Level test