Skip to content

Commit fda3f0b

Browse files
authored
[DICT] Add support for pop() method (pyccel#1891)
Add support for the `dict` method `pop()` and the initialisation of a dictionary via a call to `{}`. Syntactic, semantic and Python printing support is added. The `DictType` datatype is expanded to match the expected description from the docs. The class `PythonDict` is added to manage the initialisation. The abstract class `DictMethod` and the subclass `DictPop` are defined to handle the `pop()` method. Fixes pyccel#1886. _Blocked by #1897_
1 parent 1d78e0b commit fda3f0b

File tree

8 files changed

+169
-2
lines changed

8 files changed

+169
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ All notable changes to this project will be documented in this file.
2525
- #1877 : Add C Support for set method `pop()`.
2626
- #1895 : Add Python support for dict initialisation with `{}`.
2727
- #1895 : Add Python support for dict initialisation with `dict()`.
28+
- #1886 : Add Python support for dict method `pop()`.
2829
- \[INTERNALS\] Added `container_rank` property to `ast.datatypes.PyccelType` objects.
2930
- \[DEVELOPER\] Added an improved traceback to the developer-mode errors for errors in function calls.
3031

docs/builtin-functions.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,20 @@ Python contains a limited number of builtin functions defined [here](https://doc
7373
| `vars` | No |
7474
| **`zip`** | as a loop iterable |
7575
| \_\_`import`\_\_ | No
76+
77+
## Dictionary methods
78+
79+
| Method | Supported |
80+
|----------|-----------|
81+
| `clear` | No |
82+
| `copy` | No |
83+
| `get` | No |
84+
| `items` | No |
85+
| `keys` | No |
86+
| `pop` | Python-only |
87+
| `popitem` | No |
88+
| `reversed` | No |
89+
| `setdefault` | No |
90+
| `update` | No |
91+
| `values` | No |
92+
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# coding: utf-8
2+
#------------------------------------------------------------------------------------------#
3+
# This file is part of Pyccel which is released under MIT License. See the LICENSE file or #
4+
# go to https://github.com/pyccel/pyccel/blob/devel/LICENSE for full license details. #
5+
#------------------------------------------------------------------------------------------#
6+
"""
7+
The dict container has a number of built-in methods that are
8+
always available.
9+
10+
This module contains objects which describe these methods within Pyccel's AST.
11+
"""
12+
13+
from pyccel.ast.internals import PyccelFunction
14+
15+
__all__ = ('DictMethod',
16+
'DictPop',
17+
)
18+
19+
#==============================================================================
20+
class DictMethod(PyccelFunction):
21+
"""
22+
Abstract class for dict method calls.
23+
24+
A subclass of this base class represents calls to a specific dict
25+
method.
26+
27+
Parameters
28+
----------
29+
dict_obj : TypedAstNode
30+
The object which the method is called from.
31+
32+
*args : TypedAstNode
33+
The arguments passed to dict methods.
34+
"""
35+
__slots__ = ("_dict_obj",)
36+
_attribute_nodes = PyccelFunction._attribute_nodes + ("_dict_obj",)
37+
38+
def __init__(self, dict_obj, *args):
39+
self._dict_obj = dict_obj
40+
super().__init__(*args)
41+
42+
@property
43+
def dict_obj(self):
44+
"""
45+
Get the object representing the dict.
46+
47+
Get the object representing the dict.
48+
"""
49+
return self._dict_obj
50+
51+
#==============================================================================
52+
class DictPop(DictMethod):
53+
"""
54+
Represents a call to the .pop() method.
55+
56+
The pop() method pops an element from the dict. The element is selected
57+
via a key. If the key is not present in the dictionary then the default
58+
value is returned.
59+
60+
Parameters
61+
----------
62+
dict_obj : TypedAstNode
63+
The object from which the method is called.
64+
65+
k : TypedAstNode
66+
The key which is used to select the value from the dictionary.
67+
68+
d : TypedAstNode, optional
69+
The value that should be returned if the key is not present in the
70+
dictionary.
71+
"""
72+
__slots__ = ('_class_type',)
73+
_shape = None
74+
name = 'pop'
75+
76+
def __init__(self, dict_obj, k, d = None):
77+
dict_type = dict_obj.class_type
78+
self._class_type = dict_type.value_type
79+
if k.class_type != dict_type.key_type:
80+
raise TypeError(f"Key passed to pop method has type {k.class_type}. Expected {dict_type.key_type}")
81+
if d and d.class_type != dict_type.value_type:
82+
raise TypeError(f"Default value passed to pop method has type {d.class_type}. Expected {dict_type.value_type}")
83+
super().__init__(dict_obj, k, d)
84+
85+
@property
86+
def key(self):
87+
"""
88+
The key that is used to select the element from the dict.
89+
90+
The key that is used to select the element from the dict.
91+
"""
92+
return self._args[0]
93+
94+
@property
95+
def default_value(self):
96+
"""
97+
The value that should be returned if the key is not present in the dictionary.
98+
99+
The value that should be returned if the key is not present in the dictionary.
100+
"""
101+
return self._args[1]

pyccel/ast/builtin_methods/list_methods.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class ListMethod(PyccelFunction):
4141
The arguments passed to list methods.
4242
"""
4343
__slots__ = ("_list_obj",)
44-
_attribute_nodes = ("_list_obj",)
44+
_attribute_nodes = PyccelFunction._attribute_nodes + ("_list_obj",)
4545
name = None
4646
def __init__(self, list_obj, *args):
4747
self._list_obj = list_obj

pyccel/ast/builtin_methods/set_methods.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class SetMethod(PyccelFunction):
4040
The arguments passed to the function call.
4141
"""
4242
__slots__ = ('_set_variable',)
43-
_attribute_nodes = ('_set_variable',)
43+
_attribute_nodes = PyccelFunction._attribute_nodes + ('_set_variable',)
4444
def __init__(self, set_variable, *args):
4545
self._set_variable = set_variable
4646
super().__init__(*args)

pyccel/ast/class_defs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pyccel.ast.builtin_methods.list_methods import (ListAppend, ListInsert, ListPop,
1111
ListClear, ListExtend, ListRemove,
1212
ListCopy, ListSort)
13+
from pyccel.ast.builtin_methods.dict_methods import DictPop
1314

1415
from .builtins import PythonImag, PythonReal, PythonConjugate
1516
from .core import ClassDef, PyccelFunctionDef
@@ -169,6 +170,7 @@
169170

170171
DictClass = ClassDef('dict',
171172
methods=[
173+
PyccelFunctionDef('pop', func_class = DictPop),
172174
])
173175

174176
#=======================================================================================

pyccel/codegen/printing/pycode.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,22 @@ def _print_ListMethod(self, expr):
847847

848848
return f"{list_obj}.{method_name}({method_args})\n"
849849

850+
def _print_DictMethod(self, expr):
851+
method_name = expr.name
852+
dict_obj = self._print(expr.dict_obj)
853+
method_args = ', '.join(self._print(a) for a in expr.args)
854+
855+
return f"{dict_obj}.{method_name}({method_args})\n"
856+
857+
def _print_DictPop(self, expr):
858+
dict_obj = self._print(expr.dict_obj)
859+
key = self._print(expr.key)
860+
if expr.default_value:
861+
val = self._print(expr.default_value)
862+
return f"{dict_obj}.pop({key}, {val})\n"
863+
else:
864+
return f"{dict_obj}.pop({key})\n"
865+
850866
def _print_Slice(self, expr):
851867
start = self._print(expr.start) if expr.start else ''
852868
stop = self._print(expr.stop) if expr.stop else ''

tests/epyccel/test_epyccel_dicts.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,33 @@ def kwarg_init():
6868
python_result = kwarg_init()
6969
assert isinstance(python_result, type(pyccel_result))
7070
assert python_result == pyccel_result
71+
72+
def test_pop_element(language) :
73+
def pop_element():
74+
a = {1:1.0, 2:2.0}
75+
return a.pop(1)
76+
epyc_element = epyccel(pop_element, language = language)
77+
pyccel_result = epyc_element()
78+
python_result = pop_element()
79+
assert isinstance(python_result, type(pyccel_result))
80+
assert python_result == pyccel_result
81+
82+
def test_pop_default_element(language) :
83+
def pop_default_element():
84+
a = {1:True, 2:False}
85+
return a.pop(3, True)
86+
epyc_default_element = epyccel(pop_default_element, language = language)
87+
pyccel_result = epyc_default_element()
88+
python_result = pop_default_element()
89+
assert isinstance(python_result, type(pyccel_result))
90+
assert python_result == pyccel_result
91+
92+
def test_pop_str_keys(language) :
93+
def pop_str_keys():
94+
a = {'a':1, 'b':2}
95+
return a.pop('a')
96+
epyc_str_keys = epyccel(pop_str_keys, language = language)
97+
pyccel_result = epyc_str_keys()
98+
python_result = pop_str_keys()
99+
assert isinstance(python_result, type(pyccel_result))
100+
assert python_result == pyccel_result

0 commit comments

Comments
 (0)