Repository: pyccel/pyccel (python)

Issue:
remove: method removes the first occurrence of the element with the specified value.
### Feature Description
Add support for remove() method for lists in the Python translation.

### Test Code
This code should work when this issue is fixed:
```python

lis = [1, 2, 3]
lis.remove(2)
print(lis)
>> [1, 3]
```

### Proposed Solution
See description in https://github.com/pyccel/pyccel/issues/1529

Pull request description:
List support: `remove` method
Add support for the `remove()` method of Python lists to the semantic stage and Python codegen. This fixes #1693.

**Commit Summary**

- Add a `ListRemove` subclass of `ListMethod`, representing the `remove()` method. The constructor checks for compatibility between the type of the `remove()` argument and the type of the list, and raises an error if they are not homogeneous.
- Add success and error tests for the `remove()` method involving many scenarios where the function could be used.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 02a52a8e..6b6f043f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
 -   #1739 : Add Python support for set method `clear()`.
 -   #1739 : Add abstract class `SetMethod` to handle calls to various set methods.
 -   #1740 : Add Python support for set method `copy()`.
+-   #1693 : Add Python support for list method `remove()`.    
 
 ### Fixed
 
diff --git a/pyccel/ast/builtin_methods/list_methods.py b/pyccel/ast/builtin_methods/list_methods.py
index b8e3c575..26f99ffa 100644
--- a/pyccel/ast/builtin_methods/list_methods.py
+++ b/pyccel/ast/builtin_methods/list_methods.py
@@ -19,6 +19,7 @@ __all__ = ('ListAppend',
            'ListInsert',
            'ListMethod',
            'ListPop',
+           'ListRemove',
            )
 
 #==============================================================================
@@ -229,3 +230,43 @@ class ListExtend(ListMethod):
 
     def __init__(self, list_obj, iterable) -> None:
         super().__init__(list_obj, iterable)
+
+#==============================================================================
+class ListRemove(ListMethod) :
+    """
+    Represents a call to the .remove() method.
+    
+    Represents a call to the .remove() method which removes the first
+    occurrence of a given element from the list.
+    Note that the .remove() method doesn't return any value.
+
+    >>> a = [[1, 2], [3, 4]]
+    >>> a.remove([1, 2])
+    >>> print(a)
+    [[3, 4]]
+
+    Parameters
+    ----------
+    list_obj : TypedAstNode
+        The list object which the method is called from.
+
+    removed_obj : TypedAstNode
+        The object to be removed from the list.
+    """
+    __slots__ = ()
+    _shape = None
+    _order = None
+    _rank = 0
+    _class_type = VoidType()
+    name = 'remove'
+
+    def __init__(self, list_obj, removed_obj) -> None:
+        expected_type = list_obj.class_type.element_type
+        is_homogeneous = (
+            removed_obj.class_type == expected_type and
+            list_obj.rank - 1 == removed_obj.rank
+        )
+        if not is_homogeneous:
+            raise TypeError(f"Can't remove an element of type {removed_obj.class_type} from {list_obj.class_type}")
+        super().__init__(list_obj, removed_obj)
+
diff --git a/pyccel/ast/class_defs.py b/pyccel/ast/class_defs.py
index f1c08745..4bca0c7a 100644
--- a/pyccel/ast/class_defs.py
+++ b/pyccel/ast/class_defs.py
@@ -7,7 +7,8 @@ This module contains all types which define a python class which is automaticall
 """
 
 from pyccel.ast.builtin_methods.set_methods  import SetAdd, SetClear, SetCopy, SetPop
-from pyccel.ast.builtin_methods.list_methods import ListAppend, ListInsert, ListPop, ListClear, ListExtend
+from pyccel.ast.builtin_methods.list_methods import (ListAppend, ListInsert, ListPop,
+                                                     ListClear, ListExtend, ListRemove)
 
 
 from .builtins   import PythonImag, PythonReal, PythonConjugate
@@ -145,6 +146,7 @@ ListClass = ClassDef('list',
             PyccelFunctionDef('extend', func_class = ListExtend),
             PyccelFunctionDef('insert', func_class = ListInsert),
             PyccelFunctionDef('pop', func_class = ListPop),
+            PyccelFunctionDef('remove', func_class = ListRemove),
         ])
 
 #=======================================================================================


Test patch:
diff --git a/tests/epyccel/test_epyccel_lists.py b/tests/epyccel/test_epyccel_lists.py
index 9664d6fd..6bc2f68b 100644
--- a/tests/epyccel/test_epyccel_lists.py
+++ b/tests/epyccel/test_epyccel_lists.py
@@ -528,6 +528,38 @@ def test_extend_user_defined_objects(language):
     for python_elem, accelerated_elem in zip(python_list, accelerated_list):
         assert python_elem.x == accelerated_elem.x
 
+def test_remove_basic(language):
+    def f():
+        lst = [1, 2, 3, 4]
+        lst.remove(3)
+        return lst
+    epyc_f = epyccel(f, language=language)
+    assert f() == epyc_f()
+
+def test_remove_float(language):
+    def f():
+        lst = [1.4, 2.3, 3.2, 4.4]
+        lst.remove(3.2)
+        return lst
+    epyc_f = epyccel(f, language=language)
+    assert f() == epyc_f()
+
+def test_remove_complex(language):
+    def f():
+        lst = [1j, 3j, 8j]
+        lst.remove(3j)
+        return lst
+    epyc_f = epyccel(f, language=language)
+    assert f() == epyc_f()
+
+def test_remove_list_from_list(language):
+    def f():
+        lst = [[True, False, True], [False, True]]
+        lst.remove([False, True])
+        return lst
+    epyc_f = epyccel(f, language=language)
+    assert f() == epyc_f()
+
 def test_extend_list_class_attribute(language):
     import modules.list_class_attr as mod