Implement EntityCollection.extend() method for Issue #27
Added extend() method to EntityCollection that accepts any iterable of Entity objects and adds them all to the collection. The method: - Accepts lists, tuples, generators, or any iterable - Validates all items are Entity objects - Sets the grid association for each added entity - Properly handles errors and empty iterables closes #27
This commit is contained in:
parent
923350137d
commit
1e7f5e9e7e
|
@ -0,0 +1,20 @@
|
||||||
|
import mcrfpy
|
||||||
|
|
||||||
|
# Create grid
|
||||||
|
t = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||||
|
g = mcrfpy.Grid(5, 5, t, (0, 0), (100, 100))
|
||||||
|
|
||||||
|
# Create some entities
|
||||||
|
entities = [mcrfpy.Entity((i, i), t, i, g) for i in range(3)]
|
||||||
|
|
||||||
|
# Test extend
|
||||||
|
print(f"Initial entities: {len(g.entities)}")
|
||||||
|
g.entities.extend(entities)
|
||||||
|
print(f"After extend: {len(g.entities)}")
|
||||||
|
|
||||||
|
# Test with tuple
|
||||||
|
more = (mcrfpy.Entity((3, 3), t, 3, g), mcrfpy.Entity((4, 4), t, 4, g))
|
||||||
|
g.entities.extend(more)
|
||||||
|
print(f"After second extend: {len(g.entities)}")
|
||||||
|
|
||||||
|
print("✓ EntityCollection.extend() works!")
|
|
@ -637,9 +637,47 @@ PyObject* UIEntityCollection::remove(PyUIEntityCollectionObject* self, PyObject*
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject* UIEntityCollection::extend(PyUIEntityCollectionObject* self, PyObject* o)
|
||||||
|
{
|
||||||
|
// Accept any iterable of Entity objects
|
||||||
|
PyObject* iterator = PyObject_GetIter(o);
|
||||||
|
if (iterator == NULL) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "UIEntityCollection.extend requires an iterable");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* item;
|
||||||
|
while ((item = PyIter_Next(iterator)) != NULL) {
|
||||||
|
// Check if item is an Entity
|
||||||
|
if (!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"))) {
|
||||||
|
Py_DECREF(item);
|
||||||
|
Py_DECREF(iterator);
|
||||||
|
PyErr_SetString(PyExc_TypeError, "All items in iterable must be Entity objects");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the entity to the collection
|
||||||
|
PyUIEntityObject* entity = (PyUIEntityObject*)item;
|
||||||
|
self->data->push_back(entity->data);
|
||||||
|
entity->data->grid = self->grid;
|
||||||
|
|
||||||
|
Py_DECREF(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(iterator);
|
||||||
|
|
||||||
|
// Check if iteration ended due to an error
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
PyMethodDef UIEntityCollection::methods[] = {
|
PyMethodDef UIEntityCollection::methods[] = {
|
||||||
{"append", (PyCFunction)UIEntityCollection::append, METH_O},
|
{"append", (PyCFunction)UIEntityCollection::append, METH_O},
|
||||||
//{"extend", (PyCFunction)UIEntityCollection::extend, METH_O}, // TODO
|
{"extend", (PyCFunction)UIEntityCollection::extend, METH_O},
|
||||||
{"remove", (PyCFunction)UIEntityCollection::remove, METH_O},
|
{"remove", (PyCFunction)UIEntityCollection::remove, METH_O},
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,6 +77,7 @@ class UIEntityCollection {
|
||||||
public:
|
public:
|
||||||
static PySequenceMethods sqmethods;
|
static PySequenceMethods sqmethods;
|
||||||
static PyObject* append(PyUIEntityCollectionObject* self, PyObject* o);
|
static PyObject* append(PyUIEntityCollectionObject* self, PyObject* o);
|
||||||
|
static PyObject* extend(PyUIEntityCollectionObject* self, PyObject* o);
|
||||||
static PyObject* remove(PyUIEntityCollectionObject* self, PyObject* o);
|
static PyObject* remove(PyUIEntityCollectionObject* self, PyObject* o);
|
||||||
static PyMethodDef methods[];
|
static PyMethodDef methods[];
|
||||||
static PyObject* repr(PyUIEntityCollectionObject* self);
|
static PyObject* repr(PyUIEntityCollectionObject* self);
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test for Issue #27: EntityCollection.extend() method
|
||||||
|
|
||||||
|
Verifies that EntityCollection can extend with multiple entities at once.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_entity_extend(timer_name):
|
||||||
|
"""Test that EntityCollection.extend() method works correctly"""
|
||||||
|
import mcrfpy
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print("Issue #27 test: EntityCollection.extend() method")
|
||||||
|
|
||||||
|
# Create test scene and grid
|
||||||
|
mcrfpy.createScene("test")
|
||||||
|
ui = mcrfpy.sceneUI("test")
|
||||||
|
|
||||||
|
# Create grid with texture
|
||||||
|
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||||
|
grid = mcrfpy.Grid(10, 10, texture, (10, 10), (400, 400))
|
||||||
|
ui.append(grid)
|
||||||
|
|
||||||
|
# Add some initial entities
|
||||||
|
entity1 = mcrfpy.Entity((1, 1), texture, 1, grid)
|
||||||
|
entity2 = mcrfpy.Entity((2, 2), texture, 2, grid)
|
||||||
|
grid.entities.append(entity1)
|
||||||
|
grid.entities.append(entity2)
|
||||||
|
|
||||||
|
print(f"✓ Initial entities: {len(grid.entities)}")
|
||||||
|
|
||||||
|
# Test 1: Extend with a list of entities
|
||||||
|
new_entities = [
|
||||||
|
mcrfpy.Entity((3, 3), texture, 3, grid),
|
||||||
|
mcrfpy.Entity((4, 4), texture, 4, grid),
|
||||||
|
mcrfpy.Entity((5, 5), texture, 5, grid)
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
grid.entities.extend(new_entities)
|
||||||
|
assert len(grid.entities) == 5, f"Expected 5 entities, got {len(grid.entities)}"
|
||||||
|
print(f"✓ Extended with list: now {len(grid.entities)} entities")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Failed to extend with list: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Test 2: Extend with a tuple
|
||||||
|
more_entities = (
|
||||||
|
mcrfpy.Entity((6, 6), texture, 6, grid),
|
||||||
|
mcrfpy.Entity((7, 7), texture, 7, grid)
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
grid.entities.extend(more_entities)
|
||||||
|
assert len(grid.entities) == 7, f"Expected 7 entities, got {len(grid.entities)}"
|
||||||
|
print(f"✓ Extended with tuple: now {len(grid.entities)} entities")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Failed to extend with tuple: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Test 3: Extend with generator expression
|
||||||
|
try:
|
||||||
|
grid.entities.extend(mcrfpy.Entity((8, i), texture, 8+i, grid) for i in range(3))
|
||||||
|
assert len(grid.entities) == 10, f"Expected 10 entities, got {len(grid.entities)}"
|
||||||
|
print(f"✓ Extended with generator: now {len(grid.entities)} entities")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Failed to extend with generator: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Test 4: Verify all entities have correct grid association
|
||||||
|
for i, entity in enumerate(grid.entities):
|
||||||
|
# Just checking that we can iterate and access them
|
||||||
|
assert entity.sprite_number >= 1, f"Entity {i} has invalid sprite number"
|
||||||
|
print("✓ All entities accessible and valid")
|
||||||
|
|
||||||
|
# Test 5: Invalid input - non-iterable
|
||||||
|
try:
|
||||||
|
grid.entities.extend(42)
|
||||||
|
print("✗ Should have raised TypeError for non-iterable")
|
||||||
|
except TypeError as e:
|
||||||
|
print(f"✓ Correctly rejected non-iterable: {e}")
|
||||||
|
|
||||||
|
# Test 6: Invalid input - iterable with non-Entity
|
||||||
|
try:
|
||||||
|
grid.entities.extend([entity1, "not an entity", entity2])
|
||||||
|
print("✗ Should have raised TypeError for non-Entity in iterable")
|
||||||
|
except TypeError as e:
|
||||||
|
print(f"✓ Correctly rejected non-Entity in iterable: {e}")
|
||||||
|
|
||||||
|
# Test 7: Empty iterable (should work)
|
||||||
|
initial_count = len(grid.entities)
|
||||||
|
try:
|
||||||
|
grid.entities.extend([])
|
||||||
|
assert len(grid.entities) == initial_count, "Empty extend changed count"
|
||||||
|
print("✓ Empty extend works correctly")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Empty extend failed: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
print(f"\n✅ Issue #27 test PASSED - EntityCollection.extend() works correctly")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Execute the test after a short delay
|
||||||
|
import mcrfpy
|
||||||
|
mcrfpy.setTimer("test", test_entity_extend, 100)
|
Loading…
Reference in New Issue