aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt41
-rw-r--r--entityx/python/PythonSystem.cc295
-rw-r--r--entityx/python/PythonSystem.h262
-rw-r--r--entityx/python/PythonSystem_test.cc277
-rw-r--r--entityx/python/README.md165
-rw-r--r--entityx/python/entityx/__init__.py111
-rw-r--r--entityx/python/entityx/tests/__init__.py0
-rw-r--r--entityx/python/entityx/tests/assign_test.py18
-rw-r--r--entityx/python/entityx/tests/constructor_test.py10
-rw-r--r--entityx/python/entityx/tests/create_entities_from_python_test.py33
-rw-r--r--entityx/python/entityx/tests/deep_subclass_test.py26
-rw-r--r--entityx/python/entityx/tests/event_emit_test.py13
-rw-r--r--entityx/python/entityx/tests/event_test.py11
-rw-r--r--entityx/python/entityx/tests/update_test.py8
-rw-r--r--entityx/python/setup.py12
15 files changed, 0 insertions, 1282 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f49c75f..af6d17c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,7 +13,6 @@ set(ENTITYX_RUN_BENCHMARKS false CACHE BOOL "Run benchmarks (in conjunction with
set(ENTITYX_MAX_COMPONENTS 64 CACHE STRING "Set the maximum number of components.")
set(ENTITYX_USE_CPP11_STDLIB true CACHE BOOL "For Clang, specify whether to use libc++ (-stdlib=libc++).")
set(ENTITYX_BUILD_SHARED false CACHE BOOL "Build shared libraries?")
-set(ENTITYX_BUILD_PYTHON false CACHE BOOL "Build entityx::python?")
include(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake)
include(CheckCXXSourceCompiles)
@@ -91,13 +90,6 @@ require(HAS_CXX11_LONG_LONG "C++11 lambdas")
message("-- Checking misc features")
require(HAVE_STDINT_H "stdint.h")
-if (ENTITYX_BUILD_PYTHON)
- message("-- Building with Python support (-DENTITYX_BUILD_PYTHON=0 to disable)")
- find_package(Boost 1.48.0 COMPONENTS python)
-else (ENTITYX_BUILD_PYTHON)
- message("-- Python support disabled")
-endif (ENTITYX_BUILD_PYTHON)
-
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
@@ -119,35 +111,6 @@ if (ENTITYX_BUILD_SHARED)
list(APPEND install_libs entityx_shared)
endif (ENTITYX_BUILD_SHARED)
-if (ENTITYX_BUILD_PYTHON AND Boost_PYTHON_LIBRARY)
- message("-- Found boost::python, building entityx/python")
- find_package(PythonLibs REQUIRED)
- include_directories(${PYTHON_INCLUDE_DIRS})
- set(ENTITYX_HAVE_BOOST_PYTHON 1)
- set(python_sources entityx/python/PythonSystem.cc)
- add_library(entityx_python STATIC ${python_sources})
- list(APPEND install_libs entityx_python)
- install(
- FILES ${CMAKE_CURRENT_SOURCE_DIR}/entityx/python/entityx/__init__.py
- DESTINATION share/entityx/python/
- RENAME entityx.py
- )
- message("-- Installing entityx Python package to ${CMAKE_INSTALL_PREFIX}/share/entityx/python")
- set(ENTITYX_INSTALLED_PYTHON_PACKAGE_DIR ${CMAKE_INSTALL_PREFIX}/share/entityx/python/)
- if (ENTITYX_BUILD_SHARED)
- add_library(entityx_python_shared SHARED ${python_sources})
- target_link_libraries(
- entityx_python_shared
- entityx_shared
- ${Boost_PYTHON_LIBRARY}
- ${PYTHON_LIBRARIES}
- )
- set_target_properties(entityx_python_shared PROPERTIES OUTPUT_NAME entityx_python)
- list(APPEND install_libs entityx_python_shared)
- endif (ENTITYX_BUILD_SHARED)
- set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS})
-endif (ENTITYX_BUILD_PYTHON AND Boost_PYTHON_LIBRARY)
-
if (ENTITYX_BUILD_TESTING)
#find_package(Boost 1.48.0 REQUIRED COMPONENTS timer system)
add_subdirectory(gtest-1.6.0)
@@ -158,10 +121,6 @@ if (ENTITYX_BUILD_TESTING)
create_test(system_test entityx/System_test.cc)
create_test(tags_component_test entityx/tags/TagsComponent_test.cc)
create_test(dependencies_test entityx/deps/Dependencies_test.cc)
- if (Boost_PYTHON_LIBRARY)
- add_definitions(-DENTITYX_PYTHON_TEST_DATA=\"${CMAKE_CURRENT_SOURCE_DIR}/entityx/python\")
- create_test(python_test entityx/python/PythonSystem_test.cc entityx_python ${Boost_PYTHON_LIBRARY} ${PYTHON_LIBRARIES})
- endif (Boost_PYTHON_LIBRARY)
if (ENTITYX_RUN_BENCHMARKS)
message("-- Running benchmarks")
add_definitions(-DGTEST_USE_OWN_TR1_TUPLE=1 -DBOOST_NO_CXX11_NUMERIC_LIMITS=1)
diff --git a/entityx/python/PythonSystem.cc b/entityx/python/PythonSystem.cc
deleted file mode 100644
index 373948d..0000000
--- a/entityx/python/PythonSystem.cc
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2013 Alec Thomas <alec@swapoff.org>
- * All rights reserved.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution.
- *
- * Author: Alec Thomas <alec@swapoff.org>
- */
-
-// http://docs.python.org/2/extending/extending.html
-#include <boost/noncopyable.hpp>
-#include <Python.h>
-#include <cassert>
-#include <string>
-#include <iostream>
-#include <sstream>
-#include "entityx/python/PythonSystem.h"
-
-namespace py = boost::python;
-
-namespace entityx {
-namespace python {
-
-
-static const py::object None;
-
-
-class PythonEntityXLogger {
-public:
- PythonEntityXLogger() {}
- explicit PythonEntityXLogger(PythonSystem::LoggerFunction logger) : logger_(logger) {}
- ~PythonEntityXLogger() { flush(true); }
-
- void write(const std::string &text) {
- line_ += text;
- flush();
- }
-
-private:
- void flush(bool force = false) {
- size_t offset;
- while ((offset = line_.find('\n')) != std::string::npos) {
- std::string text = line_.substr(0, offset);
- logger_(text);
- line_ = line_.substr(offset + 1);
- }
- if (force && line_.size()) {
- logger_(line_);
- line_ = "";
- }
- }
-
- PythonSystem::LoggerFunction logger_;
- std::string line_;
-};
-
-
-/**
- * Base class for Python entities.
- */
-struct PythonEntity {
- explicit PythonEntity(ptr<EntityManager> entity_manager, Entity::Id id) : _entity(Entity(entity_manager, id)) {} // NOLINT
- virtual ~PythonEntity() {}
-
- void destroy() {
- _entity.destroy();
- }
-
- operator Entity () const { return _entity; }
-
- virtual void update(float dt, int frame) {}
-
- Entity::Id _entity_id() const {
- return _entity.id();
- }
-
- Entity _entity;
-};
-
-static std::string PythonEntity_repr(const PythonEntity &entity) {
- std::stringstream repr;
- repr << "<Entity " << entity._entity.id().index() << "." << entity._entity.id().version() << ">";
- return repr.str();
-}
-
-
-static std::string Entity_Id_repr(Entity::Id id) {
- std::stringstream repr;
- repr << "<Entity::Id " << id.index() << "." << id.version() << ">";
- return repr.str();
-}
-
-
-// static std::string entity_repr(Entity entity) {
-// std::stringstream repr;
-// repr << "<Entity::Id " << entity.id().index() << "." << entity.id().version() << ">";
-// return repr.str();
-// }
-
-// static bool entity_eq(Entity left, Entity right) {
-// return left.id() == right.id();
-// }
-
-
-// A to-Python converter from Entity to PythonEntity.
-struct EntityToPythonEntity {
- static PyObject *convert(Entity entity) {
- auto python = entity.component<PythonComponent>();
- assert(python && "Entity does not have a PythonComponent");
- return py::incref(python->object.ptr());
- }
-};
-
-
-Entity::Id EntityManager_configure(ptr<EntityManager> entity_manager, py::object self) {
- Entity entity = entity_manager->create();
- entity.assign<PythonComponent>(self);
- return entity.id();
-}
-
-BOOST_PYTHON_MODULE(_entityx) {
- py::to_python_converter<Entity, EntityToPythonEntity>();
-
- py::class_<PythonEntityXLogger>("Logger", py::no_init)
- .def("write", &PythonEntityXLogger::write);
-
- py::class_<BaseEvent, ptr<BaseEvent>, boost::noncopyable>("BaseEvent", py::no_init);
-
- py::class_<PythonEntity>("Entity", py::init<ptr<EntityManager>, Entity::Id>())
- .def_readonly("_entity_id", &PythonEntity::_entity_id)
- .def("update", &PythonEntity::update)
- .def("destroy", &PythonEntity::destroy)
- .def("__repr__", &PythonEntity_repr);
-
- py::class_<Entity::Id>("EntityId", py::no_init)
- .def_readonly("id", &Entity::Id::id)
- .def_readonly("index", &Entity::Id::index)
- .def_readonly("version", &Entity::Id::version)
- .def("__repr__", &Entity_Id_repr);
-
- py::class_<PythonComponent, ptr<PythonComponent>>("PythonComponent", py::init<py::object>())
- .def("assign_to", &assign_to<PythonComponent>)
- .def("get_component", &get_component<PythonComponent>)
- .staticmethod("get_component");
-
- py::class_<EntityManager, ptr<EntityManager>, boost::noncopyable>("EntityManager", py::no_init)
- .def("configure", &EntityManager_configure);
-
- void (EventManager::*emit)(const BaseEvent &) = &EventManager::emit;
-
- py::class_<EventManager, ptr<EventManager>, boost::noncopyable>("EventManager", py::no_init)
- .def("emit", emit);
-
- py::implicitly_convertible<PythonEntity, Entity>();
-}
-
-
-static void log_to_stderr(const std::string &text) {
- std::cerr << "python stderr: " << text << std::endl;
-}
-
-static void log_to_stdout(const std::string &text) {
- std::cout << "python stdout: " << text << std::endl;
-}
-
-// PythonSystem below here
-
-bool PythonSystem::initialized_ = false;
-
-PythonSystem::PythonSystem(ptr<EntityManager> entity_manager)
- : entity_manager_(entity_manager), stdout_(log_to_stdout), stderr_(log_to_stderr) {
- if (!initialized_) {
- initialize_python_module();
- }
- Py_Initialize();
- if (!initialized_) {
- init_entityx();
- initialized_ = true;
- }
-}
-
-PythonSystem::~PythonSystem() {
- try {
- py::object entityx = py::import("_entityx");
- entityx.attr("_entity_manager").del();
- entityx.attr("_event_manager").del();
- py::object sys = py::import("sys");
- sys.attr("stdout").del();
- sys.attr("stderr").del();
- py::object gc = py::import("gc");
- gc.attr("collect")();
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- throw;
- }
- // FIXME: It would be good to do this, but it is not supported by boost::python:
- // http://www.boost.org/doc/libs/1_53_0/libs/python/todo.html#pyfinalize-safety
- // Py_Finalize();
-}
-
-void PythonSystem::add_installed_library_path() {
- add_path(ENTITYX_INSTALLED_PYTHON_PACKAGE_DIR);
-}
-
-void PythonSystem::add_path(const std::string &path) {
- python_paths_.push_back(path);
-}
-
-void PythonSystem::initialize_python_module() {
- assert(PyImport_AppendInittab("_entityx", init_entityx) != -1 && "Failed to initialize _entityx Python module");
-}
-
-void PythonSystem::configure(ptr<EventManager> event_manager) {
- event_manager->subscribe<EntityDestroyedEvent>(*this);
- event_manager->subscribe<ComponentAddedEvent<PythonComponent>>(*this);
-
- try {
- py::object main_module = py::import("__main__");
- py::object main_namespace = main_module.attr("__dict__");
-
- // Initialize logging.
- py::object sys = py::import("sys");
- sys.attr("stdout") = PythonEntityXLogger(stdout_);
- sys.attr("stderr") = PythonEntityXLogger(stderr_);
-
- // Add paths to interpreter sys.path
- for (auto path : python_paths_) {
- py::str dir = path.c_str();
- sys.attr("path").attr("insert")(0, dir);
- }
-
- py::object entityx = py::import("_entityx");
- entityx.attr("_entity_manager") = entity_manager_.lock();
- entityx.attr("_event_manager") = event_manager;
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- throw;
- }
-}
-
-void PythonSystem::update(ptr<EntityManager> entity_manager, ptr<EventManager> event_manager, double dt) {
- for (auto entity : entity_manager->entities_with_components<PythonComponent>()) {
- ptr<PythonComponent> python = entity.component<PythonComponent>();
-
- try {
- python->object.attr("update")(dt, frame_);
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- throw;
- }
- }
- frame_++;
-}
-
-void PythonSystem::log_to(LoggerFunction stdout, LoggerFunction stderr) {
- stdout_ = stdout;
- stderr_ = stderr;
-}
-
-void PythonSystem::receive(const EntityDestroyedEvent &event) {
- for (auto proxy : event_proxies_) {
- proxy->delete_receiver(event.entity);
- }
-}
-
-void PythonSystem::receive(const ComponentAddedEvent<PythonComponent> &event) {
- // If the component was created in C++ it won't have a Python object
- // associated with it. Create one.
- if (!event.component->object) {
- py::object module = py::import(event.component->module.c_str());
- py::object cls = module.attr(event.component->cls.c_str());
- py::object from_raw_entity = cls.attr("_from_raw_entity");
- if (py::len(event.component->args) == 0) {
- event.component->object = from_raw_entity(event.entity.id());
- } else {
- py::list args;
- args.append(event.entity.id());
- args.extend(event.component->args);
- event.component->object = from_raw_entity(*py::tuple(args));
- }
- }
-
- for (auto proxy : event_proxies_) {
- if (proxy->can_send(event.component->object)) {
- proxy->add_receiver(event.entity);
- }
- }
-}
-
-} // namespace python
-} // namespace entityx
diff --git a/entityx/python/PythonSystem.h b/entityx/python/PythonSystem.h
deleted file mode 100644
index d63fa38..0000000
--- a/entityx/python/PythonSystem.h
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2013 Alec Thomas <alec@swapoff.org>
- * All rights reserved.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution.
- *
- * Author: Alec Thomas <alec@swapoff.org>
- */
-
-#pragma once
-
-// http://docs.python.org/2/extending/extending.html
-#include <Python.h>
-#include <boost/python.hpp>
-#include <boost/function.hpp>
-#include <list>
-#include <vector>
-#include <string>
-#include "entityx/System.h"
-#include "entityx/Entity.h"
-#include "entityx/Event.h"
-#include "entityx/config.h"
-
-// boost::python smart pointer adapter for std::shared_ptr<T>
-#ifdef ENTITYX_NEED_GET_POINTER_SHARED_PTR_SPECIALIZATION
-
-#include <memory>
-
-
-namespace std {
-
-// This may or may not work... it definitely does not work on OSX.
-template <class T> inline T * get_pointer(const std::shared_ptr<T> &p) {
- return p.get();
-}
-
-} // namespace std
-
-
-#endif
-
-
-namespace entityx {
-namespace python {
-
-/**
- * An EntityX component that represents a Python script.
- */
-class PythonComponent : public entityx::Component<PythonComponent> {
-public:
- /**
- * Create a new PythonComponent from a Python Entity class.
- *
- * @param module The Python module where the Entity subclass resides.
- * @param cls The Class within the module. Must inherit from entityx.Entity.
- * @param args The *args to pass to the Python constructor.
- */
- template <typename ...Args>
- PythonComponent(const std::string &module, const std::string &cls, Args ... args) : module(module), cls(cls) {
- unpack_args(args...);
- }
-
- /**
- * Create a new PythonComponent from an existing Python instance.
- */
- explicit PythonComponent(boost::python::object object) : object(object) {}
-
- boost::python::object object;
- boost::python::list args;
- const std::string module, cls;
-
-private:
- template <typename A, typename ...Args>
- void unpack_args(A &arg, Args ... remainder) {
- args.append(arg);
- unpack_args(remainder...);
- }
-
- void unpack_args() {}
-};
-
-
-class PythonSystem;
-
-
-/**
- * Proxies C++ EntityX events to Python entities.
- */
-class PythonEventProxy {
-public:
- friend class PythonSystem;
-
- /**
- * Construct a new event proxy.
- *
- * @param handler_name The default implementation of can_send() tests for
- * the existence of this attribute on an Entity.
- */
- explicit PythonEventProxy(const std::string &handler_name) : handler_name(handler_name) {}
- virtual ~PythonEventProxy() {}
-
- /**
- * Return true if this event can be sent to the provided Python entity.
- *
- * @param object The Python entity to test for event delivery.
- */
- virtual bool can_send(const boost::python::object &object) const {
- return PyObject_HasAttrString(object.ptr(), handler_name.c_str());
- }
-
-protected:
- std::list<Entity> entities;
- const std::string handler_name;
-
-private:
- /**
- * Add an Entity receiver to this proxy. This is called automatically by PythonSystem.
- *
- * @param entity The entity that will receive events.
- */
- void add_receiver(Entity entity) {
- entities.push_back(entity);
- }
-
- /**
- * Delete an Entity receiver. This is called automatically by PythonSystem.
- *
- * @param entity The entity that was receiving events.
- */
- void delete_receiver(Entity entity) {
- for (auto i = entities.begin(); i != entities.end(); ++i) {
- if (entity == *i) {
- entities.erase(i);
- break;
- }
- }
- }
-};
-
-
-/**
- * A helper function for class_ to assign a component to an entity.
- */
-template <typename Component>
-void assign_to(ptr<Component> component, ptr<EntityManager> entity_manager, Entity::Id id) {
- entity_manager->assign<Component>(id, component);
-}
-
-
-/**
- * A helper function for retrieving an existing component associated with an
- * entity.
- */
-template <typename Component>
-ptr<Component> get_component(ptr<EntityManager> entity_manager, Entity::Id id) {
- return entity_manager->component<Component>(id);
-}
-
-
-/**
- * A PythonEventProxy that broadcasts events to all entities with a matching
- * handler method.
- */
-template <typename Event>
-class BroadcastPythonEventProxy : public PythonEventProxy, public Receiver<BroadcastPythonEventProxy<Event>> {
-public:
- BroadcastPythonEventProxy(const std::string &handler_name) : PythonEventProxy(handler_name) {}
- virtual ~BroadcastPythonEventProxy() {}
-
- void receive(const Event &event) {
- for (auto entity : entities) {
- auto py_entity = entity.template component<PythonComponent>();
- py_entity->object.attr(handler_name.c_str())(event);
- }
- }
-};
-
-/**
- * An entityx::System that bridges EntityX and Python.
- *
- * This system handles exposing entityx functionality to Python. The Python
- * support differs in design from the C++ design in the following ways:
- *
- * - Entities contain logic and can receive events.
- * - Systems and Components can not be implemented in Python.
- */
-class PythonSystem : public entityx::System<PythonSystem>, public entityx::Receiver<PythonSystem> {
-public:
- typedef boost::function<void (const std::string &)> LoggerFunction;
-
- PythonSystem(ptr<EntityManager> entity_manager); // NOLINT
- virtual ~PythonSystem();
-
- /**
- * Add system-installed entityx Python path to the interpreter.
- */
- void add_installed_library_path();
-
- /**
- * Add a Python path to the interpreter.
- */
- void add_path(const std::string &path);
-
- /**
- * Add a sequence of paths to the interpreter.
- */
- template <typename T>
- void add_paths(const T &paths) {
- for (auto path : paths) {
- add_path(path);
- }
- }
-
- /// Return the Python paths the system is configured with.
- const std::vector<std::string> &python_paths() const {
- return python_paths_;
- }
-
- virtual void configure(ptr<EventManager> event_manager) override;
- virtual void update(ptr<EntityManager> entities, ptr<EventManager> event_manager, double dt) override;
-
- /**
- * Set line-based (not including \n) logger for stdout and stderr.
- */
- void log_to(LoggerFunction stdout, LoggerFunction stderr);
-
- /**
- * Proxy events of type Event to any Python entity with a handler_name method.
- */
- template <typename Event>
- void add_event_proxy(ptr<EventManager> event_manager, const std::string &handler_name) {
- ptr<BroadcastPythonEventProxy<Event>> proxy(new BroadcastPythonEventProxy<Event>(handler_name));
- event_manager->subscribe<Event>(*proxy.get());
- event_proxies_.push_back(static_pointer_cast<PythonEventProxy>(proxy));
- }
-
- /**
- * Proxy events of type Event using the given PythonEventProxy implementation.
- */
- template <typename Event, typename Proxy>
- void add_event_proxy(ptr<EventManager> event_manager, ptr<Proxy> proxy) {
- event_manager->subscribe<Event>(*proxy);
- event_proxies_.push_back(static_pointer_cast<PythonEventProxy>(proxy));
- }
-
- void receive(const EntityDestroyedEvent &event);
- void receive(const ComponentAddedEvent<PythonComponent> &event);
-
-private:
- void initialize_python_module();
-
- weak_ptr<EntityManager> entity_manager_;
- std::vector<std::string> python_paths_;
- LoggerFunction stdout_, stderr_;
- static bool initialized_;
- std::vector<ptr<PythonEventProxy>> event_proxies_;
- int frame_ = 0;
-};
-
-} // namespace python
-} // namespace entityx
diff --git a/entityx/python/PythonSystem_test.cc b/entityx/python/PythonSystem_test.cc
deleted file mode 100644
index d4d531c..0000000
--- a/entityx/python/PythonSystem_test.cc
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2013 Alec Thomas <alec@swapoff.org>
- * All rights reserved.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution.
- *
- * Author: Alec Thomas <alec@swapoff.org>
- */
-
-// NOTE: MUST be first include. See http://docs.python.org/2/extending/extending.html
-#include <Python.h>
-#include <gtest/gtest.h>
-#include <boost/python.hpp>
-#include <cassert>
-#include <vector>
-#include <string>
-#include <iostream>
-#include "entityx/Entity.h"
-#include "entityx/Event.h"
-#include "entityx/python/PythonSystem.h"
-
-namespace py = boost::python;
-using std::cerr;
-using std::endl;
-using namespace entityx;
-using namespace entityx::python;
-
-
-struct Position : public Component<Position> {
- Position(float x = 0.0, float y = 0.0) : x(x), y(y) {}
-
- float x, y;
-};
-
-
-struct Direction : public Component<Direction> {
- Direction(float x = 0.0, float y = 0.0) : x(x), y(y) {}
-
- float x, y;
-};
-
-
-struct CollisionEvent : public Event<CollisionEvent> {
- CollisionEvent(Entity a, Entity b) : a(a), b(b) {}
-
- Entity a, b;
-};
-
-
-struct CollisionEventProxy : public PythonEventProxy, public Receiver<CollisionEvent> {
- CollisionEventProxy() : PythonEventProxy("on_collision") {}
-
- void receive(const CollisionEvent &event) {
- for (auto entity : entities) {
- if (entity == event.a || entity == event.b) {
- auto py_entity = entity.component<PythonComponent>();
- py_entity->object.attr("on_collision")(event);
- }
- }
- }
-};
-
-
-BOOST_PYTHON_MODULE(entityx_python_test) {
- py::class_<Position, ptr<Position>>("Position", py::init<py::optional<float, float>>())
- .def("assign_to", &assign_to<Position>)
- .def("get_component", &get_component<Position>)
- .staticmethod("get_component")
- .def_readwrite("x", &Position::x)
- .def_readwrite("y", &Position::y);
-
- py::class_<Direction, ptr<Direction>>("Direction", py::init<py::optional<float, float>>())
- .def("assign_to", &assign_to<Direction>)
- .def("get_component", &get_component<Direction>)
- .staticmethod("get_component")
- .def_readwrite("x", &Direction::x)
- .def_readwrite("y", &Direction::y);
-
- py::class_<CollisionEvent, ptr<CollisionEvent>, py::bases<BaseEvent>>("Collision", py::init<Entity, Entity>())
- .add_property("a", py::make_getter(&CollisionEvent::a, py::return_value_policy<py::return_by_value>()))
- .add_property("b", py::make_getter(&CollisionEvent::b, py::return_value_policy<py::return_by_value>()));
-}
-
-
-class PythonSystemTest : public ::testing::Test {
-protected:
- PythonSystemTest() {
- assert(PyImport_AppendInittab("entityx_python_test", initentityx_python_test) != -1 && "Failed to initialize entityx_python_test Python module");
- }
-
- void SetUp() {
- ev.reset(new EventManager());
- em.reset(new EntityManager(ev));
- system.reset(new PythonSystem(em));
- system->add_path(ENTITYX_PYTHON_TEST_DATA);
- if (!initialized) {
- initentityx_python_test();
- initialized = true;
- }
- system->add_event_proxy<CollisionEvent>(ev, ptr<CollisionEventProxy>(new CollisionEventProxy()));
- system->configure(ev);
- }
-
- void TearDown() {
- weak_ptr<EventManager> v = ev;
- weak_ptr<EntityManager> e = em;
- em->destroy_all();
- system.reset();
- em.reset();
- ev.reset();
- }
-
- ptr<PythonSystem> system;
- ptr<EventManager> ev;
- ptr<EntityManager> em;
- static bool initialized;
-};
-
-bool PythonSystemTest::initialized = false;
-
-
-TEST_F(PythonSystemTest, TestSystemUpdateCallsEntityUpdate) {
- try {
- Entity e = em->create();
- auto script = e.assign<PythonComponent>("entityx.tests.update_test", "UpdateTest");
- ASSERT_FALSE(py::extract<bool>(script->object.attr("updated")));
- system->update(em, ev, 0.1);
- ASSERT_TRUE(py::extract<bool>(script->object.attr("updated")));
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- ASSERT_FALSE(true) << "Python exception.";
- }
-}
-
-
-TEST_F(PythonSystemTest, TestComponentAssignmentCreationInPython) {
- try {
- Entity e = em->create();
- auto script = e.assign<PythonComponent>("entityx.tests.assign_test", "AssignTest");
- ASSERT_TRUE(static_cast<bool>(e.component<Position>()));
- ASSERT_TRUE(script->object);
- ASSERT_TRUE(script->object.attr("test_assign_create"));
- script->object.attr("test_assign_create")();
- auto position = e.component<Position>();
- ASSERT_TRUE(static_cast<bool>(position));
- ASSERT_EQ(position->x, 1.0);
- ASSERT_EQ(position->y, 2.0);
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- ASSERT_FALSE(true) << "Python exception.";
- }
-}
-
-
-TEST_F(PythonSystemTest, TestComponentAssignmentCreationInCpp) {
- try {
- Entity e = em->create();
- e.assign<Position>(2, 3);
- auto script = e.assign<PythonComponent>("entityx.tests.assign_test", "AssignTest");
- ASSERT_TRUE(static_cast<bool>(e.component<Position>()));
- ASSERT_TRUE(script->object);
- ASSERT_TRUE(script->object.attr("test_assign_existing"));
- script->object.attr("test_assign_existing")();
- auto position = e.component<Position>();
- ASSERT_TRUE(static_cast<bool>(position));
- ASSERT_EQ(position->x, 3.0);
- ASSERT_EQ(position->y, 4.0);
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- ASSERT_FALSE(true) << "Python exception.";
- }
-}
-
-
-TEST_F(PythonSystemTest, TestEntityConstructorArgs) {
- try {
- Entity e = em->create();
- auto script = e.assign<PythonComponent>("entityx.tests.constructor_test", "ConstructorTest", 4.0, 5.0);
- auto position = e.component<Position>();
- ASSERT_TRUE(static_cast<bool>(position));
- ASSERT_EQ(position->x, 4.0);
- ASSERT_EQ(position->y, 5.0);
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- ASSERT_FALSE(true) << "Python exception.";
- }
-}
-
-
-TEST_F(PythonSystemTest, TestEventDelivery) {
- try {
- Entity f = em->create();
- Entity e = em->create();
- Entity g = em->create();
- auto scripte = e.assign<PythonComponent>("entityx.tests.event_test", "EventTest");
- auto scriptf = f.assign<PythonComponent>("entityx.tests.event_test", "EventTest");
- auto scriptg = g.assign<PythonComponent>("entityx.tests.event_test", "EventTest");
- ASSERT_FALSE(scripte->object.attr("collided"));
- ASSERT_FALSE(scriptf->object.attr("collided"));
- ev->emit<CollisionEvent>(f, g);
- ASSERT_TRUE(scriptf->object.attr("collided"));
- ASSERT_FALSE(scripte->object.attr("collided"));
- ev->emit<CollisionEvent>(e, f);
- ASSERT_TRUE(scriptf->object.attr("collided"));
- ASSERT_TRUE(scripte->object.attr("collided"));
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- ASSERT_FALSE(true) << "Python exception.";
- }
-}
-
-
-TEST_F(PythonSystemTest, TestDeepEntitySubclass) {
- try {
- Entity e = em->create();
- auto script = e.assign<PythonComponent>("entityx.tests.deep_subclass_test", "DeepSubclassTest");
- ASSERT_TRUE(script->object.attr("test_deep_subclass"));
- script->object.attr("test_deep_subclass")();
-
- Entity e2 = em->create();
- auto script2 = e2.assign<PythonComponent>("entityx.tests.deep_subclass_test", "DeepSubclassTest2");
- ASSERT_TRUE(script2->object.attr("test_deeper_subclass"));
- script2->object.attr("test_deeper_subclass")();
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- ASSERT_FALSE(true) << "Python exception.";
- }
-}
-
-
-TEST_F(PythonSystemTest, TestEntityCreationFromPython) {
- try {
- py::object test = py::import("entityx.tests.create_entities_from_python_test");
- test.attr("create_entities_from_python_test")();
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- ASSERT_FALSE(true) << "Python exception.";
- }
-}
-
-
-TEST_F(PythonSystemTest, TestEventEmissionFromPython) {
- try {
- struct CollisionReceiver : public Receiver<CollisionReceiver> {
- void receive(const CollisionEvent &event) {
- a = event.a;
- b = event.b;
- }
-
- Entity a, b;
- };
-
- CollisionReceiver receiver;
- ev->subscribe<CollisionEvent>(receiver);
-
- ASSERT_FALSE(receiver.a);
- ASSERT_FALSE(receiver.b);
-
- py::object test = py::import("entityx.tests.event_emit_test");
- test.attr("emit_collision_from_python")();
-
- ASSERT_TRUE(receiver.a);
- ASSERT_TRUE(receiver.b);
- } catch(...) {
- PyErr_Print();
- PyErr_Clear();
- ASSERT_FALSE(true) << "Python exception.";
- }
-}
diff --git a/entityx/python/README.md b/entityx/python/README.md
deleted file mode 100644
index cf83ab5..0000000
--- a/entityx/python/README.md
+++ /dev/null
@@ -1,165 +0,0 @@
-# Python Scripting System for EntityX (α Alpha)
-
-This system adds the ability to extend entity logic with Python scripts. The goal is to allow ad-hoc behaviour to be assigned to entities, in contract to the more pure entity-component system approach.
-
-## Limitations
-
-Planned features that are currently unimplemented:
-
-- Emitting events from Python.
-
-## Design
-
-- Python scripts are attached to entities via `PythonComponent`.
-- Systems and components can not be created from Python, primarily for performance reasons.
-- Events are proxied directly to Python entities via `PythonEventProxy` objects.
- - Each event to be handled in Python must have an associated `PythonEventProxy`implementation.
- - As a convenience `BroadcastPythonEventProxy<Event>(handler_method)` can be used. It will broadcast events to all `PythonComponent` entities with a `<handler_method>`.
-- `PythonSystem` manages scripted entity lifecycle and event delivery.
-
-## Summary
-
-To add scripting support to your system, something like the following steps should be followed:
-
-1. Expose C++ `Component` and `Event` classes to Python with `BOOST_PYTHON_MODULE`.
-2. Initialize the module with `PyImport_AppendInittab`.
-3. Create a Python package.
-4. Add classes to the package, inheriting from `entityx.Entity` and using the `entityx.Component` descriptor to assign components.
-5. Create a `PythonSystem`, passing in the list of paths to add to Python's import search path.
-6. Optionally attach any event proxies.
-7. Create an entity and associate it with a Python script by assigning `PythonComponent`, passing it the package name, class name, and any constructor arguments.
-8. When finished, call `EntityManager::destroy_all()`.
-
-## Interfacing with Python
-
-`entityx::python` primarily uses standard `boost::python` to interface with Python, with some helper classes and functions.
-
-### Exposing C++ Components to Python
-
-In most cases, this should be pretty simple. Given a component, provide a `boost::python` class definition wrapped in `entityx::ptr<T>`, with two extra methods defined with EntityX::Python helper functions `assign_to<Component>` and `get_component<Component>`. These are used from Python to assign Python-created components to an entity and to retrieve existing components from an entity, respectively.
-
-Here's an example:
-
-```c++
-namespace py = boost::python;
-using namespace entityx;
-using namespace entityx::python;
-
-
-struct Position : public Component<Position> {
- Position(float x = 0.0, float y = 0.0) : x(x), y(y) {}
-
- float x, y;
-};
-
-void export_position_to_python() {
- py::class_<PythonPosition, ptr<PythonPosition>>("Position", py::init<py::optional<float, float>>())
- .def("assign_to", &assign_to<Position>) // Allows this component to be assigned to an entity
- .def("get_component", &get_component<Position>) // Allows this component to be retrieved from an entity.
- .staticmethod("get_component") // (as above)
- .def_readwrite("x", &PythonPosition::x)
- .def_readwrite("y", &PythonPosition::y);
-}
-
-BOOST_PYTHON_MODULE(mygame) {
- export_position_to_python();
-}
-```
-
-### Delivering events to Python entities
-
-Unlike in C++, where events are typically handled by systems, EntityX::Python
-explicitly provides support for sending events to entities. To bridge this gap
-use the `PythonEventProxy` class to receive C++ events and proxy them to
-Python entities.
-
-The class takes a single parameter, which is the name of the attribute on a
-Python entity. If this attribute exists, the entity will be added to
-`PythonEventProxy::entities (std::list<Entity>)`, so that matching entities
-will be accessible from any event handlers.
-
-This checking is performed in `PythonEventProxy::can_send()`, and can be
-overridden, but further checking can also be done in the event `receive()`
-method.
-
-A helper template class called `BroadcastPythonEventProxy<Event>` is provided
-that will broadcast events to any entity with the corresponding handler method.
-
-To implement more refined logic, subclass `PythonEventProxy` and operate on
-the protected member `entities`. Here's a collision example, where the proxy
-only delivers collision events to the colliding entities themselves:
-
-```c++
-struct CollisionEvent : public Event<CollisionEvent> {
- CollisionEvent(Entity a, Entity b) : a(a), b(b) {}
-
- Entity a, b;
-};
-
-struct CollisionEventProxy : public PythonEventProxy, public Receiver<CollisionEvent> {
- CollisionEventProxy() : PythonEventProxy("on_collision") {}
-
- void receive(const CollisionEvent &event) {
- // "entities" is a protected data member, populated by
- // PythonSystem, with Python entities that pass can_send().
- for (auto entity : entities) {
- auto py_entity = entity.template component<PythonComponent>();
- if (entity == event.a || entity == event.b) {
- py_entity->object.attr(handler_name.c_str())(event);
- }
- }
- }
-};
-
-void export_collision_event_to_python() {
- py::class_<CollisionEvent, ptr<CollisionEvent>, py::bases<BaseEvent>>("Collision", py::init<Entity, Entity>())
- .def_readonly("a", &CollisionEvent::a)
- .def_readonly("b", &CollisionEvent::b);
-}
-
-
-BOOST_PYTHON_MODULE(mygame) {
- export_position_to_python();
- export_collision_event_to_python();
-}
-```
-
-
-### Sending events from Python
-
-This is relatively straight forward. Once you have exported a C++ event to Python:
-
-```python
-from entityx import Entity, emit
-from mygame import Collision
-
-
-class AnEntity(Entity): pass
-
-
-emit(Collision(AnEntity(), AnEntity()))
-```
-
-
-### Initialization
-
-Finally, initialize the `mygame` module once, before using `PythonSystem`, with something like this:
-
-```c++
-// This should only be performed once, at application initialization time.
-CHECK(PyImport_AppendInittab("mygame", initmygame) != -1)
- << "Failed to initialize mygame Python module";
-```
-
-Then create and destroy `PythonSystem` as necessary:
-
-```c++
-// Initialize the PythonSystem.
-vector<string> paths;
-paths.push_back(MYGAME_PYTHON_PATH);
-// +any other Python paths...
-ptr<PythonSystem> python(new PythonSystem(paths));
-
-// Add any Event proxies.
-python->add_event_proxy<CollisionEvent>(ev, ptr<CollisionEventProxy>(new CollisionEventProxy()));
-```
diff --git a/entityx/python/entityx/__init__.py b/entityx/python/entityx/__init__.py
deleted file mode 100644
index 339c90b..0000000
--- a/entityx/python/entityx/__init__.py
+++ /dev/null
@@ -1,111 +0,0 @@
-import _entityx
-
-
-"""These classes provide a convenience layer on top of the raw entityx::python
-primitives.
-
-They allow you to declare your entities and components in an intuitive way:
-
- class Player(Entity):
- position = Component(Position)
- direction = Component(Direction)
- sprite = Component(Sprite, 'player.png') # Sprite component with constructor argument
-
- def update(self, dt, frame):
- self.position.x += self.direction.x * dt
- self.position.x += self.direction.y * dt
-
-Note that components assigned from C++ must be assigned prior to assigning
-PythonComponent, otherwise they will be created by the Entity constructor.
-"""
-
-
-__all__ = ['Entity', 'Component']
-
-
-class Component(object):
- """A field that manages Component creation/retrieval.
-
- Use like so:
-
- class Player(Entity):
- position = Component(Position)
-
- def move_to(self, x, y):
- self.position.x = x
- self.position.y = y
- """
- def __init__(self, cls, *args, **kwargs):
- self._cls = cls
- self._args = args
- self._kwargs = kwargs
-
- def _build(self, entity_id):
- component = self._cls.get_component(_entityx._entity_manager, entity_id)
- if not component:
- component = self._cls(*self._args, **self._kwargs)
- component.assign_to(_entityx._entity_manager, entity_id)
- return component
-
-
-class EntityMetaClass(_entityx.Entity.__class__):
- """Collect registered components from class attributes.
-
- This is done at class creation time to reduce entity creation overhead.
- """
-
- def __new__(cls, name, bases, dct):
- dct['_components'] = components = {}
- # Collect components from base classes
- for base in bases:
- if '_components' in base.__dict__:
- components.update(base.__dict__['_components'])
- # Collect components
- for key, value in dct.items():
- if isinstance(value, Component):
- components[key] = value
- return type.__new__(cls, name, bases, dct)
-
-
-class Entity(_entityx.Entity):
- """Base Entity class.
-
- Python Enitities differ in semantics from C++ components, in that they
- contain logic, receive events, and so on.
- """
-
- __metaclass__ = EntityMetaClass
-
- def __new__(cls, *args, **kwargs):
- entity_id = kwargs.pop('entity_id', None)
- self = _entityx.Entity.__new__(cls)
- if entity_id is None:
- entity_id = _entityx._entity_manager.configure(self)
- _entityx.Entity.__init__(self, _entityx._entity_manager, entity_id)
- for k, v in self._components.items():
- setattr(self, k, v._build(self._entity_id))
- return self
-
- def __init__(self):
- """Default constructor."""
-
- def __repr__(self):
- return '<%s.%s %d.%d>' % (self.__class__.__module__, self.__class__.__name__, self._entity_id.index, self._entity_id.version)
-
- @classmethod
- def _from_raw_entity(cls, entity_id, *args, **kwargs):
- """Create a new Entity from a raw entity.
-
- This is called from C++.
- """
- self = Entity.__new__(cls, entity_id=entity_id)
- cls.__init__(self, *args, **kwargs)
- return self
-
-
-def emit(event):
- """Emit an event.
-
- :param event: A Python-exposed C++ subclass of entityx::BaseEvent.
- """
- return _entityx._event_manager.emit(event)
diff --git a/entityx/python/entityx/tests/__init__.py b/entityx/python/entityx/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/entityx/python/entityx/tests/__init__.py
+++ /dev/null
diff --git a/entityx/python/entityx/tests/assign_test.py b/entityx/python/entityx/tests/assign_test.py
deleted file mode 100644
index 670d9b9..0000000
--- a/entityx/python/entityx/tests/assign_test.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from entityx import Entity, Component
-from entityx_python_test import Position
-
-
-class AssignTest(Entity):
- position = Component(Position)
-
- def test_assign_create(self):
- assert self.position.x == 0.0, self.position.x
- assert self.position.y == 0.0, self.position.y
- self.position.x = 1
- self.position.y = 2
-
- def test_assign_existing(self):
- assert self.position.x == 2, self.position.x
- assert self.position.y == 3, self.position.y
- self.position.x += 1
- self.position.y += 1
diff --git a/entityx/python/entityx/tests/constructor_test.py b/entityx/python/entityx/tests/constructor_test.py
deleted file mode 100644
index 6dd62de..0000000
--- a/entityx/python/entityx/tests/constructor_test.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from entityx import Entity, Component
-from entityx_python_test import Position
-
-
-class ConstructorTest(Entity):
- position = Component(Position)
-
- def __init__(self, x, y):
- self.position.x = x
- self.position.y = y
diff --git a/entityx/python/entityx/tests/create_entities_from_python_test.py b/entityx/python/entityx/tests/create_entities_from_python_test.py
deleted file mode 100644
index 8f8f3a4..0000000
--- a/entityx/python/entityx/tests/create_entities_from_python_test.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import entityx
-from entityx_python_test import Position
-
-
-class EntityA(entityx.Entity):
- position = entityx.Component(Position, 1, 2)
-
- def __init__(self, x=None, y=None):
- if x is not None:
- self.position.x = x
- if y is not None:
- self.position.y = y
-
-
-def create_entities_from_python_test():
- a = EntityA()
- assert a._entity_id.index == 0
- assert a.position.x == 1.0
- assert a.position.y == 2.0
-
- b = EntityA()
- assert b._entity_id.index == 1
-
- a.destroy()
- c = EntityA()
- # Reuse destroyed index of "a".
- assert c._entity_id.index == 0
- # However, version is different
- assert a._entity_id.id != c._entity_id.id and c._entity_id.id > a._entity_id.id
-
- d = EntityA(2.0, 3.0)
- assert d.position.x == 2.0
- assert d.position.y == 3.0
diff --git a/entityx/python/entityx/tests/deep_subclass_test.py b/entityx/python/entityx/tests/deep_subclass_test.py
deleted file mode 100644
index 801b487..0000000
--- a/entityx/python/entityx/tests/deep_subclass_test.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from entityx import Entity, Component
-from entityx_python_test import Position, Direction
-
-
-class BaseEntity(Entity):
- direction = Component(Direction)
-
-
-class DeepSubclassTest(BaseEntity):
- position = Component(Position)
-
- def test_deep_subclass(self):
- assert self.direction
- assert self.position
-
-
-class DeepSubclassTest2(DeepSubclassTest):
- position2 = Component(Position)
-
- def test_deeper_subclass(self):
- assert self.direction
- assert self.position
- assert self.position2
- assert self.position.x == self.position2.x and self.position.y == self.position2.y
- self.position.x += 1
- assert self.position.x == self.position2.x and self.position.y == self.position2.y
diff --git a/entityx/python/entityx/tests/event_emit_test.py b/entityx/python/entityx/tests/event_emit_test.py
deleted file mode 100644
index 4696e26..0000000
--- a/entityx/python/entityx/tests/event_emit_test.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from entityx import Entity, emit
-from entityx_python_test import Collision
-
-
-class AnEntity(Entity):
- pass
-
-
-def emit_collision_from_python():
- a = AnEntity()
- b = AnEntity()
- collision = Collision(a, b)
- emit(collision)
diff --git a/entityx/python/entityx/tests/event_test.py b/entityx/python/entityx/tests/event_test.py
deleted file mode 100644
index 8edf8c5..0000000
--- a/entityx/python/entityx/tests/event_test.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from entityx import Entity
-
-
-class EventTest(Entity):
- collided = False
-
- def on_collision(self, event):
- assert event.a
- assert event.b
- assert event.a == self or event.b == self
- self.collided = True
diff --git a/entityx/python/entityx/tests/update_test.py b/entityx/python/entityx/tests/update_test.py
deleted file mode 100644
index 7585aa6..0000000
--- a/entityx/python/entityx/tests/update_test.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import entityx
-
-
-class UpdateTest(entityx.Entity):
- updated = False
-
- def update(self, dt, frame):
- self.updated = True
diff --git a/entityx/python/setup.py b/entityx/python/setup.py
deleted file mode 100644
index 50e2b03..0000000
--- a/entityx/python/setup.py
+++ /dev/null
@@ -1,12 +0,0 @@
-try:
- from distribute import setup
-except ImportError:
- from setuptools import setup
-
-setup(
- name='entityx',
- version='0.0.1',
- packages=['entityx'],
- license='MIT',
- description='EntityX Entity-Component Framework Python bindings',
- )