Repository: pyccel/pyccel
Pull request #1905 (2024-05-28 20:02:13)

Add python support for DICT get method
Add Python support for the dict get method. Fixes https://github.com/pyccel/pyccel/issues/1885

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 shoaib-moeen 
pyccel-bot[bot]: @shoaib-moeen It looks like the Pyccel maintainers trust you :rocket:. I will now start running tests when you request them.

EmilyBourne: @shoaib-moeen I can't work out how to reopen this PR and point it at `devel`
EmilyBourne: /bot checklist
EmilyBourne: /bot run macosx
EmilyBourne: /bot run docs spelling pylint pyccel_lint
pyccel-bot[bot]: Hey @pyccel/pyccel-dev ! @shoaib-moeen has just created this great new pull request! Check it out and let me know what you think!

EmilyBourne: /bot run pr_tests
EmilyBourne: /bot run pr_tests

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00a80981..d02d5fc3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,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()`.
+-   #1885 : Add Python support for dict method `get()`.
 -   #1944 : Add the appropriate C language equivalent for declaring a Python `dict` container using the STC library.
 -   #1936 : Add missing C output for inline decorator example in documentation
 -   #1937 : Optimise `pyccel.ast.basic.PyccelAstNode.substitute` method.
diff --git a/docs/builtin-functions.md b/docs/builtin-functions.md
index ff4650bb..726ff39a 100644
--- a/docs/builtin-functions.md
+++ b/docs/builtin-functions.md
@@ -101,7 +101,7 @@ Python contains a limited number of builtin functions defined [here](https://doc
 |----------|-----------|
 | `clear` | No |
 | `copy` | No |
-| `get` | No |
+| `get` | Python-only |
 | `items` | No |
 | `keys` | No |
 | `pop` | Python-only |
diff --git a/pyccel/ast/builtin_methods/dict_methods.py b/pyccel/ast/builtin_methods/dict_methods.py
index de542f67..6e867013 100644
--- a/pyccel/ast/builtin_methods/dict_methods.py
+++ b/pyccel/ast/builtin_methods/dict_methods.py
@@ -4,7 +4,7 @@
 # go to https://github.com/pyccel/pyccel/blob/devel/LICENSE for full license details.      #
 #------------------------------------------------------------------------------------------#
 """
-The dict container has a number of built-in methods that are 
+The dict container has a number of built-in methods that are
 always available.
 
 This module contains objects which describe these methods within Pyccel's AST.
@@ -13,7 +13,8 @@ This module contains objects which describe these methods within Pyccel's AST.
 from pyccel.ast.datatypes import InhomogeneousTupleType
 from pyccel.ast.internals import PyccelFunction
 
-__all__ = ('DictMethod',
+__all__ = ('DictGet',
+           'DictMethod',
            'DictPop',
            'DictPopitem',
            )
@@ -30,7 +31,7 @@ class DictMethod(PyccelFunction):
     ----------
     dict_obj : TypedAstNode
         The object which the method is called from.
-    
+
     *args : TypedAstNode
         The arguments passed to dict methods.
     """
@@ -122,3 +123,57 @@ class DictPopitem(DictMethod):
         dict_type = dict_obj.class_type
         self._class_type = InhomogeneousTupleType(dict_type.key_type, dict_type.value_type)
         super().__init__(dict_obj)
+
+#==============================================================================
+class DictGet(DictMethod):
+    """
+    Represents a call to the .get() method.
+
+    The get() method returns the value for the specified key. If the key is not
+    found, it returns the default value.
+
+    Parameters
+    ----------
+    dict_obj : TypedAstNode
+        The object from which the method is called.
+
+    k : TypedAstNode
+        The key which is used to select the value from the dictionary.
+
+    d : TypedAstNode, optional
+        The value that should be returned if the key is not present in the
+        dictionary.
+    """
+    __slots__ = ('_class_type',)
+    _shape = None
+    name = 'get'
+
+    def __init__(self, dict_obj, k, d = None):
+        dict_type = dict_obj.class_type
+        self._class_type = dict_type.value_type
+        if k.class_type != dict_type.key_type:
+            raise TypeError(f"Key passed to get method has type {k.class_type}. Expected {dict_type.key_type}")
+        if d and d.class_type != dict_type.value_type:
+            raise TypeError(f"Default value passed to get 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 0c4c43fc..40dc7c87 100644
--- a/pyccel/ast/class_defs.py
+++ b/pyccel/ast/class_defs.py
@@ -10,7 +10,7 @@ from pyccel.ast.builtin_methods.set_methods  import SetAdd, SetClear, SetCopy, S
 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
+from pyccel.ast.builtin_methods.dict_methods  import DictPop, DictPopitem, DictGet
 
 from .builtins   import PythonImag, PythonReal, PythonConjugate
 from .core       import ClassDef, PyccelFunctionDef
@@ -170,6 +170,7 @@ SetClass = ClassDef('set',
 
 DictClass = ClassDef('dict',
         methods=[
+            PyccelFunctionDef('get', func_class = DictGet),
             PyccelFunctionDef('pop', func_class = DictPop),
             PyccelFunctionDef('popitem', func_class = DictPopitem),
         ])
diff --git a/pyccel/codegen/printing/pycode.py b/pyccel/codegen/printing/pycode.py
index 5ca764e6..2dcc14e0 100644
--- a/pyccel/codegen/printing/pycode.py
+++ b/pyccel/codegen/printing/pycode.py
@@ -837,6 +837,15 @@ class PythonCodePrinter(CodePrinter):
         else:
             return f"{dict_obj}.pop({key})\n"
 
+    def _print_DictGet(self, expr):
+        dict_obj = self._print(expr.dict_obj)
+        key = self._print(expr.key)
+        if expr.default_value:
+            val = self._print(expr.default_value)
+            return f"{dict_obj}.get({key}, {val})\n"
+        else:
+            return f"{dict_obj}.get({key})\n"
+
     def _print_Slice(self, expr):
         start = self._print(expr.start) if expr.start else ''
         stop  = self._print(expr.stop)  if expr.stop  else ''


Test patch:
diff --git a/tests/epyccel/test_epyccel_dicts.py b/tests/epyccel/test_epyccel_dicts.py
index c27e6a1c..8245c225 100644
--- a/tests/epyccel/test_epyccel_dicts.py
+++ b/tests/epyccel/test_epyccel_dicts.py
@@ -141,3 +141,43 @@ def test_pop_item_key(language):
     python_result = pop_item()
     assert isinstance(python_result, type(pyccel_result))
     assert python_result == pyccel_result
+
+def test_get_element(language) :
+    def get_element():
+        a = {1:1.0, 2:2.0}
+        return a.get(1)
+    epyc_element = epyccel(get_element, language = language)
+    pyccel_result = epyc_element()
+    python_result = get_element()
+    assert isinstance(python_result, type(pyccel_result))
+    assert python_result == pyccel_result
+
+def test_get_default_element(language) :
+    def get_default_element():
+        a = {1:True, 2:False}
+        return a.get(3, True)
+    epyc_default_element = epyccel(get_default_element, language = language)
+    pyccel_result = epyc_default_element()
+    python_result = get_default_element()
+    assert isinstance(python_result, type(pyccel_result))
+    assert python_result == pyccel_result
+
+def test_get_str_keys(language) :
+    def get_str_keys():
+        a = {'a':1, 'b':2}
+        return a.get('a')
+    epyc_str_keys = epyccel(get_str_keys, language = language)
+    pyccel_result = epyc_str_keys()
+    python_result = get_str_keys()
+    assert isinstance(python_result, type(pyccel_result))
+    assert python_result == pyccel_result
+
+def test_get_default_str_keys(language) :
+    def get_default_str_keys():
+        a = {'a':1, 'b':2}
+        return a.get('c', 4)
+    epyc_str_keys = epyccel(get_default_str_keys, language = language)
+    pyccel_result = epyc_str_keys()
+    python_result = get_default_str_keys()
+    assert isinstance(python_result, type(pyccel_result))
+    assert python_result == pyccel_result