2023-05-23 19:06:25 +02:00
|
|
|
{%- block pxd_header %}
|
|
|
|
{% endblock -%}
|
|
|
|
{%- block pyx_header %}
|
|
|
|
{% endblock -%}
|
|
|
|
|
|
|
|
{# We can't do const in Python #}
|
2023-06-02 11:05:45 +02:00
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_operator_index_const") }}
|
2023-05-23 19:06:25 +02:00
|
|
|
|
|
|
|
@cython.final
|
|
|
|
cdef class Dictionary:
|
|
|
|
{% block cdef_attributes %}
|
2023-06-02 11:05:45 +02:00
|
|
|
cdef pandemonium_dictionary _gd_data
|
2023-05-23 19:06:25 +02:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
cdef inline Dictionary new()
|
|
|
|
|
|
|
|
@staticmethod
|
2023-06-02 11:05:45 +02:00
|
|
|
cdef inline Dictionary from_ptr(const pandemonium_dictionary *_ptr)
|
2023-05-23 19:06:25 +02:00
|
|
|
|
|
|
|
cdef inline operator_update(self, Dictionary items)
|
|
|
|
cdef inline bint operator_equal(self, Dictionary other)
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
{% block python_defs %}
|
|
|
|
def __init__(self, iterable=None):
|
2023-06-02 11:05:45 +02:00
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_new") }}
|
2023-05-23 19:06:25 +02:00
|
|
|
if not iterable:
|
2023-06-02 11:05:45 +02:00
|
|
|
gdapi10.pandemonium_dictionary_new(&self._gd_data)
|
2023-05-23 19:06:25 +02:00
|
|
|
elif isinstance(iterable, Dictionary):
|
2023-06-02 11:05:45 +02:00
|
|
|
self._gd_data = gdapi12.pandemonium_dictionary_duplicate(&(<Dictionary>iterable)._gd_data, False)
|
2023-05-23 19:06:25 +02:00
|
|
|
# TODO: handle Pool*Array
|
|
|
|
elif isinstance(iterable, dict):
|
2023-06-02 11:05:45 +02:00
|
|
|
gdapi10.pandemonium_dictionary_new(&self._gd_data)
|
2023-05-23 19:06:25 +02:00
|
|
|
for k, v in iterable.items():
|
|
|
|
self[k] = v
|
|
|
|
else:
|
2023-06-02 11:05:45 +02:00
|
|
|
gdapi10.pandemonium_dictionary_new(&self._gd_data)
|
2023-05-23 19:06:25 +02:00
|
|
|
try:
|
|
|
|
for k, v in iterable:
|
|
|
|
self[k] = v
|
|
|
|
except ValueError as exc:
|
|
|
|
raise ValueError("dictionary update sequence element has length 1; 2 is required")
|
|
|
|
|
|
|
|
def __dealloc__(self):
|
2023-06-02 11:05:45 +02:00
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_destroy") }}
|
2023-05-23 19:06:25 +02:00
|
|
|
# /!\ if `__init__` is skipped, `_gd_data` must be initialized by
|
|
|
|
# hand otherwise we will get a segfault here
|
2023-06-02 11:05:45 +02:00
|
|
|
gdapi10.pandemonium_dictionary_destroy(&self._gd_data)
|
2023-05-23 19:06:25 +02:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
cdef inline Dictionary new():
|
|
|
|
# Call to __new__ bypasses __init__ constructor
|
|
|
|
cdef Dictionary ret = Dictionary.__new__(Dictionary)
|
2023-06-02 11:05:45 +02:00
|
|
|
gdapi10.pandemonium_dictionary_new(&ret._gd_data)
|
2023-05-23 19:06:25 +02:00
|
|
|
return ret
|
|
|
|
|
|
|
|
@staticmethod
|
2023-06-02 11:05:45 +02:00
|
|
|
cdef inline Dictionary from_ptr(const pandemonium_dictionary *_ptr):
|
2023-05-23 19:06:25 +02:00
|
|
|
# Call to __new__ bypasses __init__ constructor
|
|
|
|
cdef Dictionary ret = Dictionary.__new__(Dictionary)
|
2023-06-02 11:05:45 +02:00
|
|
|
# `pandemonium_dictionary` is a cheap structure pointing on a refcounted hashmap
|
|
|
|
# of variants. Unlike it name could let think, `pandemonium_dictionary_new_copy`
|
2023-05-23 19:06:25 +02:00
|
|
|
# only increment the refcount of the underlying structure.
|
2023-06-02 11:05:45 +02:00
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_new_copy") }}
|
|
|
|
gdapi10.pandemonium_dictionary_new_copy(&ret._gd_data, _ptr)
|
2023-05-23 19:06:25 +02:00
|
|
|
return ret
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
repr_dict = {}
|
|
|
|
for k, v in self.items():
|
|
|
|
if isinstance(k, GDString):
|
|
|
|
k = str(k)
|
|
|
|
if isinstance(v, GDString):
|
|
|
|
v = str(v)
|
|
|
|
repr_dict[k] = v
|
|
|
|
return f"<Dictionary({repr_dict})>"
|
|
|
|
|
|
|
|
def __getitem__(self, object key):
|
2023-06-02 11:05:45 +02:00
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_operator_index") }}
|
|
|
|
cdef pandemonium_variant var_key
|
|
|
|
if not pyobj_to_pandemonium_variant(key, &var_key):
|
2023-06-02 11:31:19 +02:00
|
|
|
raise TypeError(f"Cannot convert `{key!r}` to Pandemonium Variant")
|
2023-06-02 11:05:45 +02:00
|
|
|
cdef pandemonium_variant *p_var_ret = gdapi10.pandemonium_dictionary_operator_index(&self._gd_data, &var_key)
|
|
|
|
gdapi10.pandemonium_variant_destroy(&var_key)
|
2023-05-23 19:06:25 +02:00
|
|
|
if p_var_ret == NULL:
|
|
|
|
raise KeyError(key)
|
|
|
|
else:
|
2023-06-02 11:05:45 +02:00
|
|
|
return pandemonium_variant_to_pyobj(p_var_ret)
|
2023-05-23 19:06:25 +02:00
|
|
|
|
|
|
|
{{ render_method("set", py_name="__setitem__") | indent }}
|
|
|
|
|
|
|
|
def __delitem__(self, object key):
|
2023-06-02 11:05:45 +02:00
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_erase_with_return") }}
|
|
|
|
cdef pandemonium_variant var_key
|
|
|
|
if not pyobj_to_pandemonium_variant(key, &var_key):
|
2023-06-02 11:31:19 +02:00
|
|
|
raise TypeError(f"Cannot convert `{key!r}` to Pandemonium Variant")
|
2023-06-02 11:05:45 +02:00
|
|
|
cdef pandemonium_bool ret = gdapi11.pandemonium_dictionary_erase_with_return(&self._gd_data, &var_key)
|
|
|
|
gdapi10.pandemonium_variant_destroy(&var_key)
|
2023-05-23 19:06:25 +02:00
|
|
|
if not ret:
|
|
|
|
raise KeyError(key)
|
|
|
|
|
|
|
|
def __iter__(self):
|
2023-06-02 11:05:45 +02:00
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_next") }}
|
|
|
|
cdef pandemonium_variant *p_key = NULL
|
2023-05-23 19:06:25 +02:00
|
|
|
# TODO: mid iteration mutation should throw exception ?
|
|
|
|
while True:
|
2023-06-02 11:05:45 +02:00
|
|
|
p_key = gdapi10.pandemonium_dictionary_next(&self._gd_data, p_key)
|
2023-05-23 19:06:25 +02:00
|
|
|
if p_key == NULL:
|
|
|
|
return
|
2023-06-02 11:05:45 +02:00
|
|
|
yield pandemonium_variant_to_pyobj(p_key)
|
2023-05-23 19:06:25 +02:00
|
|
|
|
|
|
|
def __copy__(self):
|
|
|
|
return self.duplicate(False)
|
|
|
|
|
|
|
|
def __deepcopy__(self):
|
|
|
|
return self.duplicate(True)
|
|
|
|
|
|
|
|
def get(self, object key, object default=None):
|
2023-06-02 11:05:45 +02:00
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_get") }}
|
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_get_with_default") }}
|
|
|
|
cdef pandemonium_variant var_key
|
|
|
|
pyobj_to_pandemonium_variant(key, &var_key)
|
|
|
|
cdef pandemonium_variant var_ret
|
|
|
|
cdef pandemonium_variant var_default
|
2023-05-23 19:06:25 +02:00
|
|
|
if default is not None:
|
2023-06-02 11:05:45 +02:00
|
|
|
pyobj_to_pandemonium_variant(default, &var_default)
|
|
|
|
var_ret = gdapi11.pandemonium_dictionary_get_with_default(&self._gd_data, &var_key, &var_default)
|
|
|
|
gdapi10.pandemonium_variant_destroy(&var_default)
|
2023-05-23 19:06:25 +02:00
|
|
|
else:
|
2023-06-02 11:05:45 +02:00
|
|
|
var_ret = gdapi10.pandemonium_dictionary_get(&self._gd_data, &var_key)
|
|
|
|
gdapi10.pandemonium_variant_destroy(&var_key)
|
|
|
|
cdef object ret = pandemonium_variant_to_pyobj(&var_ret)
|
|
|
|
gdapi10.pandemonium_variant_destroy(&var_ret)
|
2023-05-23 19:06:25 +02:00
|
|
|
return ret
|
|
|
|
|
|
|
|
cdef inline operator_update(self, Dictionary items):
|
2023-06-02 11:05:45 +02:00
|
|
|
cdef pandemonium_variant *p_value
|
|
|
|
cdef pandemonium_variant *p_key = NULL
|
2023-05-23 19:06:25 +02:00
|
|
|
while True:
|
2023-06-02 11:05:45 +02:00
|
|
|
p_key = gdapi10.pandemonium_dictionary_next(&items._gd_data, p_key)
|
2023-05-23 19:06:25 +02:00
|
|
|
if p_key == NULL:
|
|
|
|
break
|
2023-06-02 11:05:45 +02:00
|
|
|
p_value = gdapi10.pandemonium_dictionary_operator_index(&items._gd_data, p_key)
|
|
|
|
gdapi10.pandemonium_dictionary_set(&self._gd_data, p_key, p_value)
|
2023-05-23 19:06:25 +02:00
|
|
|
return self
|
|
|
|
|
|
|
|
def update(self, other):
|
|
|
|
cdef object k
|
|
|
|
cdef object v
|
|
|
|
if isinstance(other, Dictionary):
|
|
|
|
Dictionary.operator_update(self, other)
|
|
|
|
elif isinstance(other, dict):
|
|
|
|
for k, v in other.items():
|
|
|
|
self[k] = v
|
|
|
|
else:
|
2023-06-02 11:13:10 +02:00
|
|
|
raise TypeError("other must be pandemonium.Dictionary or dict")
|
2023-05-23 19:06:25 +02:00
|
|
|
|
|
|
|
def items(self):
|
2023-06-02 11:05:45 +02:00
|
|
|
cdef pandemonium_variant *p_key = NULL
|
|
|
|
cdef pandemonium_variant *p_value
|
2023-05-23 19:06:25 +02:00
|
|
|
# TODO: mid iteration mutation should throw exception ?
|
|
|
|
while True:
|
2023-06-02 11:05:45 +02:00
|
|
|
p_key = gdapi10.pandemonium_dictionary_next(&self._gd_data, p_key)
|
2023-05-23 19:06:25 +02:00
|
|
|
if p_key == NULL:
|
|
|
|
return
|
2023-06-02 11:05:45 +02:00
|
|
|
p_value = gdapi10.pandemonium_dictionary_operator_index(&self._gd_data, p_key)
|
|
|
|
yield pandemonium_variant_to_pyobj(p_key), pandemonium_variant_to_pyobj(p_value)
|
2023-05-23 19:06:25 +02:00
|
|
|
|
|
|
|
cdef inline bint operator_equal(self, Dictionary other):
|
|
|
|
if other is None:
|
|
|
|
return False
|
2023-06-02 11:05:45 +02:00
|
|
|
cdef pandemonium_int size = self.size()
|
2023-05-23 19:06:25 +02:00
|
|
|
if size != other.size():
|
|
|
|
return False
|
|
|
|
# TODO: gdnative should provide a function to do that
|
|
|
|
return dict(self) == dict(other)
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
2023-06-02 11:13:10 +02:00
|
|
|
{# see https://github.com/pandemoniumengine/pandemonium/issues/27615 #}
|
2023-06-02 11:05:45 +02:00
|
|
|
{{ force_mark_rendered("pandemonium_dictionary_operator_equal") }}
|
2023-05-23 19:06:25 +02:00
|
|
|
try:
|
|
|
|
return Dictionary.operator_equal(self, <Dictionary?>other)
|
|
|
|
except TypeError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def __ne__(self, other):
|
|
|
|
try:
|
|
|
|
return not Dictionary.operator_equal(self, <Dictionary?>other)
|
|
|
|
except TypeError:
|
|
|
|
return True
|
|
|
|
|
|
|
|
{{ render_method("size", py_name="__len__") | indent }}
|
|
|
|
{{ render_method("hash", py_name="__hash__") | indent }}
|
|
|
|
{{ render_method("has", py_name="__contains__") | indent }}
|
|
|
|
|
|
|
|
{{ render_method("duplicate") | indent }}
|
|
|
|
{{ render_method("size") | indent }}
|
|
|
|
{{ render_method("empty") | indent }}
|
|
|
|
{{ render_method("clear") | indent }}
|
|
|
|
{{ render_method("has") | indent }}
|
|
|
|
{{ render_method("has_all") | indent }}
|
|
|
|
{{ render_method("erase") | indent }}
|
|
|
|
{{ render_method("hash") | indent }}
|
|
|
|
{{ render_method("keys") | indent }}
|
|
|
|
{{ render_method("values") | indent }}
|
|
|
|
{{ render_method("to_json") | indent }}
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
{%- block python_consts %}
|
|
|
|
{% endblock %}
|