Repository: pyccel/pyccel
Pull request #2033 (2024-11-02 11:14:40)

#1888: add setdefault classes for dict
#1888 : Add setdefault classes for dict in python

pyccel-bot[bot]: Hello! Welcome to Pyccel! Thank you very much for your contribution :heart:.

I am the GitHub bot. I will help guide you through the different stages necessary to validate a review in Pyccel. If you haven't yet seen our developer docs make sure you check them out [here](https://github.com/pyccel/pyccel/tree/devel/developer_docs). Amongst other things they describe the [review process](https://github.com/pyccel/pyccel/blob/devel/developer_docs/review_process.md) that we have just started. You can also get in touch with our other developers on our [Pyccel Discord Server](https://discord.gg/2Q6hwjfFVb).

To begin with I will give you a short checklist to make sure your pull request is complete. Please tick items off when you have completed them or determined that they are not necessary for this pull request. If you want me to run any specific tests to check out corner cases that you can't easily check on your computer, you can request this using the command `/bot run X`. Use the command `/bot show tests` to see a full list of the tests I can run. Once you have finished preparing your pull request and are ready to request reviews just take your PR out of draft, or let me know with the command `/bot mark as ready`. I will then run the full suite of tests to check that everything is as neat as you think before asking other contributors for reviews. Tests will not run automatically before this point to avoid wasting resources. You can get a full list of commands that I understand using `/bot commands`.

Please begin by requesting your checklist using the command `/bot checklist`.

pyccel-bot[bot]: @yguclu, @EmilyBourne, @bauom, please can you check if I can trust this user. If you are happy, let me know with `/bot trust user`
EmilyBourne: /bot trust user @learning-dev 
pyccel-bot[bot]: @learning-dev It looks like the Pyccel maintainers trust you :rocket:. I will now start running tests when you request them.

pyccel-bot[bot]: Thank you for your request. Unfortunately I am not allowed to run tests for you as you have never contributed to this repository and haven't been approved on this Pull Request.

pyccel-bot[bot]: I can't seem to find your checklist to confirm that you have completed all necessary tasks. Please request one using `/bot checklist`.

pyccel-bot[bot]: I can't seem to find your checklist to confirm that you have completed all necessary tasks. Please request one using `/bot checklist`.

pyccel-bot[bot]: I can't seem to find your checklist to confirm that you have completed all necessary tasks. Please request one using `/bot checklist`.

pyccel-bot[bot]: I can't seem to find your checklist to confirm that you have completed all necessary tasks. Please request one using `/bot checklist`.

learning-dev: /bot checklist
pyccel-bot[bot]: Unfortunately your PR is not passing the tests so it is not quite ready for review yet. Let me know when it is fixed with `/bot mark as ready`.

EmilyBourne: /bot run pr_tests
pyccel-bot[bot]: Hey @pyccel/pyccel-dev ! @learning-dev has just created this great new pull request! Check it out and let me know what you think!

EmilyBourne: @learning-dev I had to add an empty commit to your branch to trigger the codacy tests as there is a bug in their software which means that they sometimes fail to pick up new commits. You will have to pull or overwrite this commit
pyccel-bot[bot]: @learning-dev, @EmilyBourne has a few questions/comments about your code. Can you go through and see if you agree with them. If not go ahead and explain why. Once you've adressed all the comments let me know with `/bot mark as ready` and we will see if we can get approval.

learning-dev: @EmilyBourne  All the changes are done including the documentation.md update and add the description to the PR. 
It is ready to review. 
learning-dev: /bot mark as ready
learning-dev: /bot ready
pyccel-bot[bot]: This bot reacts to all comments which begin with `/bot`. This phrase can be followed by any of these commands:
- `show tests` : Lists the tests which can be triggered
- `run X` : Triggers the test X (acceptable values for X can be seen using `show tests`). Multiple tests can be specified separated by spaces.
- `try V X` : Triggers the test X (acceptable values for X can be seen using `show tests`) using Python version V. Multiple tests can be specified separated by spaces, but all will use the same Python version.
- `mark as ready` : Runs the PR tests. If they pass then it adds the appropriate review flag and requests reviews. This command should be used when the PR is first ready for review, or when a review has been answered.
- `commands` : Shows this list detailing all the commands which are understood.
- `trust user X` : Tells the bot that a new user X is trusted to run workflows (prevents misuse of GitHub actions for mining etc). This command can only be used by a trusted reviewer.

Beware: if you have never contributed to this repository and you are not a member of the Pyccel organisation, the bot will ignore all requests to run tests until permitted by a trusted reviewer.

learning-dev: /bot mark as ready
pyccel-bot[bot]: @EmilyBourne, @learning-dev has been working hard and thinks that they have now replied to or fixed all your comments. Could you take another look at the PR and see if you can approve now?


Patch:
diff --git a/AUTHORS b/AUTHORS
index 8a6955af..8ff6e447 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -35,4 +35,5 @@ Contributors
 * Shoaib Moeen
 * Kush Choudhary
 * Emmmanuel Ferdman
+* Mohammed Ayaz Shaikh
 * Amir Movassaghi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d6551c2..6de01ea7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file.
 -   #1895 : Add Python support for dict initialisation with `dict()`.
 -   #1886 : Add Python support for dict method `pop()`.
 -   #1887 : Add Python support for dict method `popitem()`.
+-   #1888 : Add Python support for dict method `setdefault()`.
 -   #1885 : Add Python support for dict method `get()`.
 -   #1844 : Add line numbers and code to errors from built-in function calls.
 -   #1655 : Add the appropriate C language equivalent for declaring a Python `list` container using the STC library.
diff --git a/docs/builtin-functions.md b/docs/builtin-functions.md
index dcffd58c..16e23cc2 100644
--- a/docs/builtin-functions.md
+++ b/docs/builtin-functions.md
@@ -128,7 +128,7 @@ Python contains a limited number of builtin functions defined [here](https://doc
 | `pop` | Python-only |
 | `popitem` | Python-only |
 | `reversed` | No |
-| `setdefault` | No |
+| `setdefault` | Python-only |
 | `update` | No |
 | `values` | No |
 
diff --git a/pyccel/ast/builtin_methods/dict_methods.py b/pyccel/ast/builtin_methods/dict_methods.py
index 6e867013..878c3a9e 100644
--- a/pyccel/ast/builtin_methods/dict_methods.py
+++ b/pyccel/ast/builtin_methods/dict_methods.py
@@ -17,6 +17,7 @@ __all__ = ('DictGet',
            'DictMethod',
            'DictPop',
            'DictPopitem',
+           'DictSetDefault',
            )
 
 #==============================================================================
@@ -176,4 +177,62 @@ class DictGet(DictMethod):
         """
         return self._args[1]
 
+#==============================================================================
+
+class DictSetDefault(DictMethod):
+    """
+    Represents a call to the .setdefault() method.
+
+    The setdefault() method set an element in the dict. The element is set
+    via a key and a value. If the value is not passed then default value is set
+    as the value.
+
+    If returns the value and if it is not present in the dictionary then the default
+    value is returned.
+
+    Parameters
+    ----------
+    dict_obj : TypedAstNode
+        The object from which the method is called.
+
+    k : TypedAstNode
+        The key which is used to set the value from the dictionary.
+
+    d : TypedAstNode, optional
+        The value that should be returned. if the value is not present in the
+        dictionary then default value is returned.
+    """
+    __slots__ = ('_class_type', '_shape')
+    name = 'setdefault'
+
+    def __init__(self, dict_obj, k, d = None):
+        dict_type = dict_obj.class_type
+        self._class_type = dict_type.value_type
+
+        self._shape = (None) * self._class_type.rank if self._class_type.rank else None
 
+        if k.class_type != dict_type.key_type:
+            raise TypeError(f"Key passed to setdefault method has type {k.class_type}. Expected {dict_type.key_type}")
+        if d is None:
+            raise TypeError("None cannot be used as the default argument for the setdefault method.")
+        if d and d.class_type != dict_type.value_type:
+            raise TypeError(f"Default value passed to setdefault method has type {d.class_type}. Expected {dict_type.value_type}")
+        super().__init__(dict_obj, k, d)
+
+    @property
+    def key(self):
+        """
+        The key that is used to select the element from the dict.
+
+        The key that is used to select the element from the dict.
+        """
+        return self._args[0]
+
+    @property
+    def default_value(self):
+        """
+        The value that should be returned if the key is not present in the dictionary.
+
+        The value that should be returned if the key is not present in the dictionary.
+        """
+        return self._args[1]
diff --git a/pyccel/ast/class_defs.py b/pyccel/ast/class_defs.py
index 4f627697..aafb30a7 100644
--- a/pyccel/ast/class_defs.py
+++ b/pyccel/ast/class_defs.py
@@ -11,7 +11,8 @@ from pyccel.ast.builtin_methods.set_methods  import (SetAdd, SetClear, SetCopy,
 from pyccel.ast.builtin_methods.list_methods import (ListAppend, ListInsert, ListPop,
                                                      ListClear, ListExtend, ListRemove,
                                                      ListCopy, ListSort)
-from pyccel.ast.builtin_methods.dict_methods  import DictPop, DictPopitem, DictGet
+from pyccel.ast.builtin_methods.dict_methods  import( DictPop, DictPopitem, DictGet,
+                                                     DictSetDefault)
 
 from .builtins   import PythonImag, PythonReal, PythonConjugate
 from .core       import ClassDef, PyccelFunctionDef
@@ -177,6 +178,7 @@ DictClass = ClassDef('dict',
             PyccelFunctionDef('get', func_class = DictGet),
             PyccelFunctionDef('pop', func_class = DictPop),
             PyccelFunctionDef('popitem', func_class = DictPopitem),
+            PyccelFunctionDef('setdefault', func_class = DictSetDefault),
         ])
 
 #=======================================================================================


Test patch:
diff --git a/tests/epyccel/test_epyccel_dicts.py b/tests/epyccel/test_epyccel_dicts.py
index d389392a..74a87de8 100644
--- a/tests/epyccel/test_epyccel_dicts.py
+++ b/tests/epyccel/test_epyccel_dicts.py
@@ -192,6 +192,19 @@ def test_dict_contains(language):
     assert isinstance(python_result, type(pyccel_result))
     assert python_result == pyccel_result
 
+
+def test_set_default(python_only_language):
+    def set_default():
+        a = {1: 1.0, 2:2.0}
+        b = a.setdefault(1, 3.0)
+        c = a.setdefault(3, 4.0)
+        return a, b, c
+    epyc_str_keys = epyccel(set_default, language = python_only_language)
+    pyccel_result = epyc_str_keys()
+    python_result = set_default()
+    assert isinstance(python_result, type(pyccel_result))
+    assert python_result == pyccel_result
+
 def test_dict_ptr(python_only_language):
     def dict_ptr():
         a = {1:1.0, 2:2.0, 3:3.0}