diff --git a/.travis.yml b/.travis.yml index c3d5ac4..50a889e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "3.5" - "3.6" - "3.7" + - "3.8" - "nightly" - "pypy3" diff --git a/LICENSE.rst b/LICENSE.rst index 7ea44e2..feb7e65 100644 --- a/LICENSE.rst +++ b/LICENSE.rst @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2019 Benjamin Moran +Copyright (c) 2020 Benjamin Moran Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/RELEASE_NOTES b/RELEASE_NOTES new file mode 100644 index 0000000..1045aa9 --- /dev/null +++ b/RELEASE_NOTES @@ -0,0 +1,119 @@ +esper 1.3 +========= +Feature release + +Changes +------- +- Add new `World.has_components` method which allows multiple Component queries. Returns a boolean. +- Add new `World.try_components` method which allows multiple Component queries. +- Add Python 3.8 to Continuous Integration testing. + + +esper 1.2 +========= +Feature release + +Changes +------- +- Calls to `super()` are no longer necessary in your Processor subclasses. +- Update README with more usage examples. All methods should now have at least one example. +- Include wheels for PyPi to help with packaging systems that only support wheels. (#38) + + +esper 1.0.0 +=========== +Feature release + +Changes +------- +- Use lru_caching internally by default. The cache is currently +- Allow passing kwargs to Processors. +- Include Python 3.7 in Continuous Integration testing. + + +esper 0.9.9 +=========== +Feature release + +Changes +------- +- Condense esper into a single file -> esper.py. + + +esper 0.9.8 +=========== +Feature release + +Changes +------- +- New timer argument for World to assist in profiling Processor execution times. +- Consolidate and clean up the benchmarks. + + +esper 0.9.7 +=========== +Feature release + +Changes +------- +- Lazily delete entities by default, preventing errors while iterating. + + +esper 0.9.6 +=========== +Feature release + +Changes +------- +- Add new `World.get_processor` convenience method which returns a Processor instance by type. + + +esper 0.9.5 +=========== +Feature release + +Changes +------- +- Add `World.components_for_entity` method which returns a tuple of an Entity's Components. +- The `World.component_for_entity` method will raise a KeyError if the Entity ID does not exist. + + +esper 0.9.4 +=========== +Feature release + +Changes +------- +- Add new method `World.has_component` which returns a Boolean (True/False). + + +esper 0.9.3 +=========== +Feature release + +Changes +------- +- Rename `World.delete_component` to `World.remove_component` for API consistency. +- `World.delete_entity` and `World.remove_component` will raise a KeyError if the Entity or + Component do not exist. + + +esper 0.9.2 +=========== +Feature release + +Changes +------- +- Switch to different internal database structure. (No API changes) +- Add examples for pyglet. +- Multiple Component queries are faster. + + +esper 0.9.0 +=========== +Feature release + +Changes +------- +- First usable release. +- Included examples for Pygame and PySDL2. diff --git a/docs/conf.py b/docs/conf.py index 9d09b80..c93f9c6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,13 +12,16 @@ # import os import sys +import time +import datetime sys.path.insert(0, os.path.abspath('..')) +YEAR = datetime.datetime.fromtimestamp(time.time()).year # -- Project information ----------------------------------------------------- project = 'esper' -copyright = '2019, Benjamin Moran' +copyright = f'{YEAR}, Benjamin Moran' author = 'Benjamin Moran' # -- General configuration --------------------------------------------------- diff --git a/esper.py b/esper.py index c3c6ed1..9e968cd 100644 --- a/esper.py +++ b/esper.py @@ -1,10 +1,18 @@ import time as _time from functools import lru_cache as _lru_cache -from typing import List, Type, TypeVar, Any, Tuple, Iterable +from typing import List as _List +from typing import Type as _Type +from typing import TypeVar as _TypeVar +from typing import Any as _Any +from typing import Tuple as _Tuple +from typing import Iterable as _Iterable -C = TypeVar('C') -P = TypeVar('P') + +version = '1.3' + +C = _TypeVar('C') +P = _TypeVar('P') class Processor: @@ -75,7 +83,7 @@ def remove_processor(self, processor_type: Processor) -> None: processor.world = None self._processors.remove(processor) - def get_processor(self, processor_type: Type[P]) -> P: + def get_processor(self, processor_type: _Type[P]) -> P: """Get a Processor instance, by type. This method returns a Processor instance by type. This could be @@ -135,7 +143,7 @@ def delete_entity(self, entity: int, immediate=False) -> None: else: self._dead_entities.add(entity) - def component_for_entity(self, entity: int, component_type: Type[C]) -> C: + def component_for_entity(self, entity: int, component_type: _Type[C]) -> C: """Retrieve a Component instance for a specific Entity. Retrieve a Component instance for a specific Entity. In some cases, @@ -149,7 +157,7 @@ def component_for_entity(self, entity: int, component_type: Type[C]) -> C: """ return self._entities[entity][component_type] - def components_for_entity(self, entity: int) -> Tuple[C, ...]: + def components_for_entity(self, entity: int) -> _Tuple[C, ...]: """Retrieve all Components for a specific Entity, as a Tuple. Retrieve all Components for a specific Entity. The method is probably @@ -165,7 +173,7 @@ def components_for_entity(self, entity: int) -> Tuple[C, ...]: """ return tuple(self._entities[entity].values()) - def has_component(self, entity: int, component_type: Any) -> bool: + def has_component(self, entity: int, component_type: _Any) -> bool: """Check if a specific Entity has a Component of a certain type. :param entity: The Entity you are querying. @@ -175,7 +183,7 @@ def has_component(self, entity: int, component_type: Any) -> bool: """ return component_type in self._entities[entity] - def has_components(self, entity: int, *component_types: Any) -> bool: + def has_components(self, entity: int, *component_types: _Any) -> bool: """Check if an Entity has all of the specified Component types. :param entity: The Entity you are querying. @@ -185,7 +193,7 @@ def has_components(self, entity: int, *component_types: Any) -> bool: """ return all(comp_type in self._entities[entity] for comp_type in component_types) - def add_component(self, entity: int, component_instance: Any) -> None: + def add_component(self, entity: int, component_instance: _Any) -> None: """Add a new Component instance to an Entity. Add a Component instance to an Entiy. If a Component of the same type @@ -207,7 +215,7 @@ def add_component(self, entity: int, component_instance: Any) -> None: self._entities[entity][component_type] = component_instance self.clear_cache() - def remove_component(self, entity: int, component_type: Any) -> int: + def remove_component(self, entity: int, component_type: _Any) -> int: """Remove a Component instance from an Entity, by type. A Component instance can be removed by providing it's type. @@ -232,7 +240,7 @@ def remove_component(self, entity: int, component_type: Any) -> int: self.clear_cache() return entity - def _get_component(self, component_type: Type[C]) -> Iterable[Tuple[int, C]]: + def _get_component(self, component_type: _Type[C]) -> _Iterable[_Tuple[int, C]]: """Get an iterator for Entity, Component pairs. :param component_type: The Component type to retrieve. @@ -243,7 +251,7 @@ def _get_component(self, component_type: Type[C]) -> Iterable[Tuple[int, C]]: for entity in self._components.get(component_type, []): yield entity, entity_db[entity][component_type] - def _get_components(self, *component_types: Type)-> Iterable[Tuple[int, ...]]: + def _get_components(self, *component_types: _Type) -> _Iterable[_Tuple[int, ...]]: """Get an iterator for Entity and multiple Component sets. :param component_types: Two or more Component types. @@ -260,14 +268,14 @@ def _get_components(self, *component_types: Type)-> Iterable[Tuple[int, ...]]: pass @_lru_cache() - def get_component(self, component_type: Type[C]) -> List[Tuple[int, C]]: + def get_component(self, component_type: _Type[C]) -> _List[_Tuple[int, C]]: return [query for query in self._get_component(component_type)] @_lru_cache() - def get_components(self, *component_types: Type): + def get_components(self, *component_types: _Type): return [query for query in self._get_components(*component_types)] - def try_component(self, entity: int, component_type: Type): + def try_component(self, entity: int, component_type: _Type): """Try to get a single component type for an Entity. This method will return the requested Component if it exists, but @@ -285,7 +293,7 @@ def try_component(self, entity: int, component_type: Type): else: return None - def try_components(self, entity: int, *component_types: Type): + def try_components(self, entity: int, *component_types: _Type): """Try to get a multiple component types for an Entity. This method will return the requested Components if they exist, but diff --git a/setup.py b/setup.py index 15fcf9b..f3c4b12 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,21 @@ from setuptools import setup -readme = open('README.rst').read() +with open('esper.py') as f: + info = {} + for line in f.readlines(): + if line.startswith('version'): + exec(line, info) + break + +README = open('README.rst').read() setup(name='esper', - version='1.2', + version=info['version'], author='Benjamin Moran', author_email='benmoran@protonmail.com', - description="Esper is a lightweight Entity System for Python, with a focus on performance.", - long_description=readme, + description="esper is a lightweight Entity System (ECS) for Python, with a focus on performance.", + long_description=README, license='MIT', keywords='ecs,entity component system,game', url='https://github.com/benmoran56/esper',