esper 3.1__tar.gz → 3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. esper-3.2/.github/workflows/type-checking.yml +25 -0
  2. {esper-3.1 → esper-3.2}/.github/workflows/unit-tests.yml +1 -18
  3. {esper-3.1 → esper-3.2}/.mypy.ini +2 -2
  4. {esper-3.1 → esper-3.2}/PKG-INFO +35 -30
  5. {esper-3.1 → esper-3.2}/README.md +34 -29
  6. {esper-3.1 → esper-3.2}/RELEASE_NOTES +10 -0
  7. {esper-3.1 → esper-3.2}/esper/__init__.py +39 -36
  8. {esper-3.1 → esper-3.2}/tests/test_world.py +34 -2
  9. {esper-3.1 → esper-3.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  10. {esper-3.1 → esper-3.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  11. {esper-3.1 → esper-3.2}/.github/ISSUE_TEMPLATE/question-or-comment.md +0 -0
  12. {esper-3.1 → esper-3.2}/.gitignore +0 -0
  13. {esper-3.1 → esper-3.2}/.readthedocs.yaml +0 -0
  14. {esper-3.1 → esper-3.2}/.ruff.toml +0 -0
  15. {esper-3.1 → esper-3.2}/LICENSE +0 -0
  16. {esper-3.1 → esper-3.2}/MANIFEST.in +0 -0
  17. {esper-3.1 → esper-3.2}/docs/Makefile +0 -0
  18. {esper-3.1 → esper-3.2}/docs/conf.py +0 -0
  19. {esper-3.1 → esper-3.2}/docs/index.rst +0 -0
  20. {esper-3.1 → esper-3.2}/docs/make.bat +0 -0
  21. {esper-3.1 → esper-3.2}/esper/py.typed +0 -0
  22. {esper-3.1 → esper-3.2}/examples/benchmark.py +0 -0
  23. {esper-3.1 → esper-3.2}/examples/benchmark_cache.py +0 -0
  24. {esper-3.1 → esper-3.2}/examples/bluesquare.png +0 -0
  25. {esper-3.1 → esper-3.2}/examples/headless_example.py +0 -0
  26. {esper-3.1 → esper-3.2}/examples/pygame_example.py +0 -0
  27. {esper-3.1 → esper-3.2}/examples/pyglet_example.py +0 -0
  28. {esper-3.1 → esper-3.2}/examples/pyglet_example_batch.py +0 -0
  29. {esper-3.1 → esper-3.2}/examples/pysdl2_example.py +0 -0
  30. {esper-3.1 → esper-3.2}/examples/pythonista_ios_example.py +0 -0
  31. {esper-3.1 → esper-3.2}/examples/redsquare.png +0 -0
  32. {esper-3.1 → esper-3.2}/make.py +0 -0
  33. {esper-3.1 → esper-3.2}/pyproject.toml +0 -0
  34. {esper-3.1 → esper-3.2}/tests/__init__.py +0 -0
@@ -0,0 +1,25 @@
1
+ name: type checking
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+
8
+ jobs:
9
+ typechecking:
10
+ runs-on: ${{ matrix.os }}
11
+ strategy:
12
+ matrix:
13
+ os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ]
14
+ python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12-dev' ]
15
+ steps:
16
+ - name: Python ${{ matrix.python-version }} ${{ matrix.os }}
17
+ uses: actions/checkout@v3
18
+ - name: Set up Python ${{ matrix.python-version }}
19
+ uses: actions/setup-python@v4
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ - name: Install mypy
23
+ run: pip install mypy
24
+ - name: Run mypy
25
+ run: mypy -v esper
@@ -11,7 +11,7 @@ jobs:
11
11
  strategy:
12
12
  matrix:
13
13
  os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ]
14
- python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12-dev', 'pypy-3.7' ]
14
+ python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12-dev', 'pypy-3.10' ]
15
15
  steps:
16
16
  - name: Python ${{ matrix.python-version }} ${{ matrix.os }}
17
17
  uses: actions/checkout@v3
@@ -23,20 +23,3 @@ jobs:
23
23
  run: pip install pytest
24
24
  - name: Run tests
25
25
  run: pytest -v tests
26
- typechecking:
27
- runs-on: ${{ matrix.os }}
28
- strategy:
29
- matrix:
30
- os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ]
31
- python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12-dev' ]
32
- steps:
33
- - name: Python ${{ matrix.python-version }} ${{ matrix.os }}
34
- uses: actions/checkout@v3
35
- - name: Set up Python ${{ matrix.python-version }}
36
- uses: actions/setup-python@v4
37
- with:
38
- python-version: ${{ matrix.python-version }}
39
- - name: Install mypy
40
- run: pip install mypy
41
- - name: Run mypy
42
- run: mypy -v esper
@@ -10,11 +10,11 @@ warn_unused_ignores = True
10
10
  warn_return_any = True
11
11
  no_implicit_reexport = True
12
12
  strict_equality = True
13
- strict_concatenate = True
13
+ extra_checks = True
14
14
 
15
15
  [mypy-esper]
16
16
  disallow_untyped_defs = True
17
17
  disallow_untyped_calls = True
18
18
 
19
19
  [mypy-tests]
20
- check_untyped_defs = True
20
+ check_untyped_defs = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: esper
3
- Version: 3.1
3
+ Version: 3.2
4
4
  Summary: esper is a lightweight Entity System (ECS) for Python, with a focus on performance
5
5
  Author-email: Benjamin Moran <benmoran@protonmail.com>
6
6
  Requires-Python: >=3.8
@@ -16,11 +16,14 @@ Esper is a lightweight Entity System module for Python, with a focus on performa
16
16
  ===================================================================================
17
17
 
18
18
  Esper is an MIT licensed Entity System, or, Entity Component System (ECS).
19
- The design is based on the Entity System concepts outlined by Adam Martin in his blog at
20
- http://t-machine.org/, and others. The primary focus is on keeping it as lightweight and
21
- performant as possible, while handling common use cases.
22
-
23
- The following Wikipedia article provides a summary of the ECS pattern:
19
+ The design is based on the Entity System concepts originally popularized by
20
+ Adam Martin and others. The primary focus for esper is to maximize perfomance,
21
+ while handling most common use cases.
22
+
23
+ For more information on the ECS pattern, you might find the following
24
+ resources interesting:
25
+ https://github.com/SanderMertens/ecs-faq/blob/master/README.md
26
+ https://github.com/jslee02/awesome-entity-component-system/blob/master/README.md
24
27
  https://en.wikipedia.org/wiki/Entity_component_system
25
28
 
26
29
  API documentation is hosted at ReadTheDocs: https://esper.readthedocs.io
@@ -48,22 +51,22 @@ documentation.
48
51
 
49
52
  Compatibility
50
53
  =============
51
- Esper attempts to target all currently supported Python releases (not EOL). Esper is written in
52
- 100% pure Python, so *any* compliant interpreter should work. Automated testing is currently
53
- done for both CPython and PyPy3.
54
+ Esper attempts to target all currently supported Python releases (any Python version that is
55
+ not EOL). Esper is written in 100% pure Python, so *any* compliant interpreter should work.
56
+ Automated testing is currently done for both CPython and PyPy3.
54
57
 
55
58
 
56
59
  Installation
57
60
  ============
58
- Esper is a pure Python package with no dependencies, so installation is not strictly
59
- necessary. You can simply copy the *esper* folder into your project, and *import esper*.
60
- If you do want to install it into your site-packages, you can do so by using `setup.py`::
61
+ Esper is a pure Python package with no dependencies, so installation is flexible.
62
+ You can simply copy the *esper* folder right into your project, and *import esper*.
63
+ You can also install into your site-packages from PyPi via `pip`::
61
64
 
62
- python setup.py install --user
65
+ pip install --user --upgrade esper
63
66
 
64
- Or from PyPi via pip::
67
+ Or from the source directory::
65
68
 
66
- pip install --user --upgrade esper
69
+ pip install . --user
67
70
 
68
71
 
69
72
  Design
@@ -71,8 +74,8 @@ Design
71
74
 
72
75
  * World Context
73
76
 
74
- Esper uses the concept of "World" contexts. When you import esper, a default context is active.
75
- You create Entities, assign Components, register Processesors, etc., by calling functions
77
+ Esper uses the concept of "World" contexts. When you first `import esper`, a default context is
78
+ active. You create Entities, assign Components, register Processesors, etc., by calling functions
76
79
  on the `esper` module. Entities, Components and Processors can be created, assigned, or deleted
77
80
  while your game is running. A simple call to `esper.process()` is all that's needed for each
78
81
  iteration of your game loop. Advanced users can switch contexts, which can be useful for
@@ -91,14 +94,15 @@ Creating an Entity is done with the `esper.create_entity()` function.
91
94
 
92
95
  Components are defined as simple Python classes. In keeping with a pure Entity System
93
96
  design philosophy, they should not contain any logic. They might have initialization
94
- code, but no processing logic whatsoever. A simple Component can be defined as::
97
+ code or perhaps Python properties, but no processing logic whatsoever. A simple
98
+ Component can be defined as::
95
99
 
96
100
  class Position:
97
101
  def __init__(self, x=0.0, y=0.0):
98
102
  self.x = x
99
103
  self.y = y
100
104
 
101
- In addition, the excellent `dataclass` decorator is available in Python 3.7+.
105
+ To save on typing, the standard library dataclass decorator is quite useful.
102
106
  https://docs.python.org/3/library/dataclasses.html#module-dataclasses
103
107
  This decorator simplifies defining your Component classes. The attribute names don't need to
104
108
  be repeated, and you can still instantiate the Component with positional or keyword arguments::
@@ -190,19 +194,20 @@ General Usage
190
194
  World Contexts
191
195
  --------------
192
196
  Esper has the capability of supporting multiple "World" contexts. On import, a "default" World is
193
- active. All creation of Entities, assignment of Processors, and all operations exist within the
194
- confines of a World. For advanced use cases Esper allows you to switch between multiple Worlds,
195
- which are completely isolated from each other. This can be useful when different scenes in your
196
- game have different Entities and Processor requirements. World context operations are done with
197
- the following functions::
198
-
197
+ active. All creation of Entities, assignment of Processors, and all other operations occur within
198
+ the confines of the active World. In other words, the World contexts are completely isolated from
199
+ each other. For basic games and designs, you may not need to bother with this functionality. A
200
+ single default World context can often be enough. For advanced use cases, such as when different
201
+ scenes in your game have different Entities and Processor requirements, this functionality can be
202
+ quite useful. World context operations are done with the following functions::
203
+ *
199
204
  * esper.list_worlds()
200
205
  * esper.switch_world(name)
201
206
  * esper.delete_world(name)
202
207
 
203
- When switching Worlds, be careful of the `name`. If a World doesn't exist, it will be created.
204
- You can delete old Worlds which are no longer needed, but you cannot delete the currently active
205
- World.
208
+ When switching Worlds, be mindful of the `name`. If a World doesn't exist, it will be created when
209
+ you first switch to it. You can delete old Worlds if they are no longer needed, but you can not
210
+ delete the currently active World.
206
211
 
207
212
  Adding and Removing Processors
208
213
  ------------------------------
@@ -396,8 +401,8 @@ Contributions to Esper are always welcome, but there are some specific project g
396
401
 
397
402
  - Pure Python code only: no binary extensions, Cython, etc.
398
403
  - Try to target all non-EOL Python versions. Exceptions can be made if there is a compelling reason.
399
- - Avoid bloat as much as possible. New features will be considered if they are commonly useful. Generally speaking, we don't want to add functionality that is better handled in another module or library.
400
- - Performance is preferrable to readability.
404
+ - Avoid bloat as much as possible. New features will be considered if they are commonly useful. Generally speaking, we don't want to add functionality that is better served by another module or library.
405
+ - Performance is preferrable to readability. The public API should remain clean, but ugly internal code is acceptable if it provides a performance benefit. Every cycle counts!
401
406
 
402
407
  If you have any questions before contributing, feel free to [open an issue].
403
408
 
@@ -6,11 +6,14 @@ Esper is a lightweight Entity System module for Python, with a focus on performa
6
6
  ===================================================================================
7
7
 
8
8
  Esper is an MIT licensed Entity System, or, Entity Component System (ECS).
9
- The design is based on the Entity System concepts outlined by Adam Martin in his blog at
10
- http://t-machine.org/, and others. The primary focus is on keeping it as lightweight and
11
- performant as possible, while handling common use cases.
12
-
13
- The following Wikipedia article provides a summary of the ECS pattern:
9
+ The design is based on the Entity System concepts originally popularized by
10
+ Adam Martin and others. The primary focus for esper is to maximize perfomance,
11
+ while handling most common use cases.
12
+
13
+ For more information on the ECS pattern, you might find the following
14
+ resources interesting:
15
+ https://github.com/SanderMertens/ecs-faq/blob/master/README.md
16
+ https://github.com/jslee02/awesome-entity-component-system/blob/master/README.md
14
17
  https://en.wikipedia.org/wiki/Entity_component_system
15
18
 
16
19
  API documentation is hosted at ReadTheDocs: https://esper.readthedocs.io
@@ -38,22 +41,22 @@ documentation.
38
41
 
39
42
  Compatibility
40
43
  =============
41
- Esper attempts to target all currently supported Python releases (not EOL). Esper is written in
42
- 100% pure Python, so *any* compliant interpreter should work. Automated testing is currently
43
- done for both CPython and PyPy3.
44
+ Esper attempts to target all currently supported Python releases (any Python version that is
45
+ not EOL). Esper is written in 100% pure Python, so *any* compliant interpreter should work.
46
+ Automated testing is currently done for both CPython and PyPy3.
44
47
 
45
48
 
46
49
  Installation
47
50
  ============
48
- Esper is a pure Python package with no dependencies, so installation is not strictly
49
- necessary. You can simply copy the *esper* folder into your project, and *import esper*.
50
- If you do want to install it into your site-packages, you can do so by using `setup.py`::
51
+ Esper is a pure Python package with no dependencies, so installation is flexible.
52
+ You can simply copy the *esper* folder right into your project, and *import esper*.
53
+ You can also install into your site-packages from PyPi via `pip`::
51
54
 
52
- python setup.py install --user
55
+ pip install --user --upgrade esper
53
56
 
54
- Or from PyPi via pip::
57
+ Or from the source directory::
55
58
 
56
- pip install --user --upgrade esper
59
+ pip install . --user
57
60
 
58
61
 
59
62
  Design
@@ -61,8 +64,8 @@ Design
61
64
 
62
65
  * World Context
63
66
 
64
- Esper uses the concept of "World" contexts. When you import esper, a default context is active.
65
- You create Entities, assign Components, register Processesors, etc., by calling functions
67
+ Esper uses the concept of "World" contexts. When you first `import esper`, a default context is
68
+ active. You create Entities, assign Components, register Processesors, etc., by calling functions
66
69
  on the `esper` module. Entities, Components and Processors can be created, assigned, or deleted
67
70
  while your game is running. A simple call to `esper.process()` is all that's needed for each
68
71
  iteration of your game loop. Advanced users can switch contexts, which can be useful for
@@ -81,14 +84,15 @@ Creating an Entity is done with the `esper.create_entity()` function.
81
84
 
82
85
  Components are defined as simple Python classes. In keeping with a pure Entity System
83
86
  design philosophy, they should not contain any logic. They might have initialization
84
- code, but no processing logic whatsoever. A simple Component can be defined as::
87
+ code or perhaps Python properties, but no processing logic whatsoever. A simple
88
+ Component can be defined as::
85
89
 
86
90
  class Position:
87
91
  def __init__(self, x=0.0, y=0.0):
88
92
  self.x = x
89
93
  self.y = y
90
94
 
91
- In addition, the excellent `dataclass` decorator is available in Python 3.7+.
95
+ To save on typing, the standard library dataclass decorator is quite useful.
92
96
  https://docs.python.org/3/library/dataclasses.html#module-dataclasses
93
97
  This decorator simplifies defining your Component classes. The attribute names don't need to
94
98
  be repeated, and you can still instantiate the Component with positional or keyword arguments::
@@ -180,19 +184,20 @@ General Usage
180
184
  World Contexts
181
185
  --------------
182
186
  Esper has the capability of supporting multiple "World" contexts. On import, a "default" World is
183
- active. All creation of Entities, assignment of Processors, and all operations exist within the
184
- confines of a World. For advanced use cases Esper allows you to switch between multiple Worlds,
185
- which are completely isolated from each other. This can be useful when different scenes in your
186
- game have different Entities and Processor requirements. World context operations are done with
187
- the following functions::
188
-
187
+ active. All creation of Entities, assignment of Processors, and all other operations occur within
188
+ the confines of the active World. In other words, the World contexts are completely isolated from
189
+ each other. For basic games and designs, you may not need to bother with this functionality. A
190
+ single default World context can often be enough. For advanced use cases, such as when different
191
+ scenes in your game have different Entities and Processor requirements, this functionality can be
192
+ quite useful. World context operations are done with the following functions::
193
+ *
189
194
  * esper.list_worlds()
190
195
  * esper.switch_world(name)
191
196
  * esper.delete_world(name)
192
197
 
193
- When switching Worlds, be careful of the `name`. If a World doesn't exist, it will be created.
194
- You can delete old Worlds which are no longer needed, but you cannot delete the currently active
195
- World.
198
+ When switching Worlds, be mindful of the `name`. If a World doesn't exist, it will be created when
199
+ you first switch to it. You can delete old Worlds if they are no longer needed, but you can not
200
+ delete the currently active World.
196
201
 
197
202
  Adding and Removing Processors
198
203
  ------------------------------
@@ -386,8 +391,8 @@ Contributions to Esper are always welcome, but there are some specific project g
386
391
 
387
392
  - Pure Python code only: no binary extensions, Cython, etc.
388
393
  - Try to target all non-EOL Python versions. Exceptions can be made if there is a compelling reason.
389
- - Avoid bloat as much as possible. New features will be considered if they are commonly useful. Generally speaking, we don't want to add functionality that is better handled in another module or library.
390
- - Performance is preferrable to readability.
394
+ - Avoid bloat as much as possible. New features will be considered if they are commonly useful. Generally speaking, we don't want to add functionality that is better served by another module or library.
395
+ - Performance is preferrable to readability. The public API should remain clean, but ugly internal code is acceptable if it provides a performance benefit. Every cycle counts!
391
396
 
392
397
  If you have any questions before contributing, feel free to [open an issue].
393
398
 
@@ -1,3 +1,13 @@
1
+ esper 3.2
2
+ =========
3
+ Maintenance release
4
+
5
+ Changes
6
+ -------
7
+ - Add `esper.current_world` property to easily check the current World context.
8
+ - Made some minor docstring corrections, and added some programmer notes.
9
+
10
+
1
11
  esper 3.1
2
12
  =========
3
13
  Maintenance release
@@ -23,9 +23,7 @@ from weakref import WeakMethod as _WeakMethod
23
23
 
24
24
  from itertools import count as _count
25
25
 
26
-
27
- version = '3.1'
28
- __version__ = version
26
+ __version__ = version = '3.2'
29
27
 
30
28
 
31
29
  ###################
@@ -50,6 +48,7 @@ def dispatch_event(name: str, *args: _Any) -> None:
50
48
 
51
49
  def _make_callback(name: str) -> _Callable[[_Any], None]:
52
50
  """Create an internal callback to remove dead handlers."""
51
+
53
52
  def callback(weak_method: _Any) -> None:
54
53
  event_registry[name].remove(weak_method)
55
54
  if not event_registry[name]:
@@ -106,23 +105,19 @@ class Processor:
106
105
 
107
106
  Processor instances must contain a `process` method, but you are otherwise
108
107
  free to define the class any way you wish. Processors should be instantiated,
109
- and then added to a :py:class:`esper.World` instance by calling
110
- :py:func:`esper.World.add_processor`. For example::
111
-
112
- my_world = World()
108
+ and then added to the current World context by calling :py:func:`esper.add_processor`.
109
+ For example::
113
110
 
114
111
  my_processor_instance = MyProcessor()
115
- my_world.add_processor(my_processor_instance)
112
+ esper.add_processor(my_processor_instance)
116
113
 
117
- After adding your Processors to a :py:class:`esper.World`, Processor.world
118
- will be set to the World it is in. This allows easy access to the World and
119
- it's methods from your Processor methods. All Processors in a World will have
120
- their `process` methods called by a single call to :py:func:`esper.World.process`,
121
- so you will generally want to iterate over entities with one (or more) calls to
122
- the appropriate world methods::
114
+ All the Processors that have been added to the World context will have their
115
+ :py:meth:`esper.Processor.process` methods called by a single call to
116
+ :py:func:`esper.process`. Inside the `process` method is generally where you
117
+ should iterate over Entities with one (or more) calls to the appropriate methods::
123
118
 
124
119
  def process(self):
125
- for ent, (rend, vel) in self.world.get_components(Renderable, Velocity):
120
+ for ent, (rend, vel) in esper.get_components(Renderable, Velocity):
126
121
  your_code_here()
127
122
  """
128
123
 
@@ -132,7 +127,6 @@ class Processor:
132
127
  raise NotImplementedError
133
128
 
134
129
 
135
-
136
130
  ###################
137
131
  # ECS functions
138
132
  ###################
@@ -506,34 +500,33 @@ def process(*args: _Any, **kwargs: _Any) -> None:
506
500
  def timed_process(*args: _Any, **kwargs: _Any) -> None:
507
501
  """Track Processor execution time for benchmarking.
508
502
 
509
- This function is identical to :py:func:`esper.process`,
510
- but it will additionally record the elapsed time of each
511
- processor call in the `esper.process_times` dictionary.
503
+ This function is identical to :py:func:`esper.process`, but
504
+ it additionally records the elapsed time of each processor
505
+ call (in milliseconds) in the`esper.process_times` dictionary.
512
506
  """
513
507
  clear_dead_entities()
514
508
  for processor in _processors:
515
509
  start_time = _time.process_time()
516
510
  processor.process(*args, **kwargs)
517
- process_time = int(round((_time.process_time() - start_time) * 1000, 2))
518
- process_times[processor.__class__.__name__] = process_time
511
+ process_times[processor.__class__.__name__] = int((_time.process_time() - start_time) * 1000)
519
512
 
520
513
 
521
514
  def list_worlds() -> _List[str]:
522
515
  """A list all World context names."""
523
- return list(_context_map.keys())
516
+ return list(_context_map)
524
517
 
525
518
 
526
519
  def delete_world(name: str) -> None:
527
520
  """Delete a World context.
528
521
 
529
- This will completely delete the World, including any entities
522
+ This will completely delete the World, including all entities
530
523
  that are contained within it.
531
524
 
532
- Raises a NameError if you attempt to delete the currently
525
+ Raises `PermissionError` if you attempt to delete the currently
533
526
  active World context.
534
527
  """
535
528
  if _current_context == name:
536
- raise NameError("Cannot delete active World context.")
529
+ raise PermissionError("The active World context cannot be deleted.")
537
530
 
538
531
  del _context_map[name]
539
532
 
@@ -542,19 +535,19 @@ def switch_world(name: str) -> None:
542
535
  """Switch to a new World context by name.
543
536
 
544
537
  Esper can have one or more "Worlds". Each World is a dedicated
545
- context, and does not share Entities, Components, etc. For
546
- some game designs, it simplifies things to use a dedicated
547
- World for each scene. For other designs, a single World may
548
- be sufficient. This function will allow you to create and
549
- switch between as many World contexts as required. If the
550
- requested name does not exist, a new context is created
551
- automatically with that name.
552
-
553
- .. note:: At startup, a "default" context exists, and is
554
- already active.
538
+ context, and does not share Entities, Components, events, etc.
539
+ Some game designs can benefit from using a dedicated World
540
+ for each scene. For other designs, a single World may
541
+ be sufficient.
542
+
543
+ This function will allow you to create and switch between as
544
+ many World contexts as required. If the requested name does not
545
+ exist, a new context is created automatically with that name.
546
+
547
+ .. note:: At startup, a "default" World context is active.
555
548
  """
556
549
  if name not in _context_map:
557
- # Create a new
550
+ # Create a new context if the name does not already exist:
558
551
  _context_map[name] = (_count(start=1), {}, {}, set(), {}, {}, [], {}, {})
559
552
 
560
553
  global _current_context
@@ -568,6 +561,16 @@ def switch_world(name: str) -> None:
568
561
  global process_times
569
562
  global event_registry
570
563
 
564
+ # switch the references to the objects in the named context_map:
571
565
  (_entity_count, _components, _entities, _dead_entities, _get_component_cache,
572
566
  _get_components_cache, _processors, process_times, event_registry) = _context_map[name]
573
567
  _current_context = name
568
+
569
+
570
+ @property # type: ignore
571
+ def current_world() -> str:
572
+ """The currently active World context.
573
+
574
+ To switch World contexts, see :py:func:`esper.switch_world`.
575
+ """
576
+ return _current_context
@@ -275,14 +275,24 @@ def test_clear_dead_entities():
275
275
 
276
276
 
277
277
  def test_switch_world():
278
+ # The `create_entities` helper will add <number>/2 of
279
+ # 'ComponentA' to the World context. Make a new
280
+ # "left" context, and confirm this is True:
278
281
  esper.switch_world("left")
279
282
  assert len(esper.get_component(ComponentA)) == 0
280
283
  create_entities(200)
281
284
  assert len(esper.get_component(ComponentA)) == 100
285
+
286
+ # Switching to a new "right" World context, no
287
+ # 'ComponentA' Components should yet exist.
282
288
  esper.switch_world("right")
283
289
  assert len(esper.get_component(ComponentA)) == 0
284
290
  create_entities(300)
285
291
  assert len(esper.get_component(ComponentA)) == 150
292
+
293
+ # Switching back to the original "left" context,
294
+ # the original 100 Components should still exist.
295
+ # From there, 200 more should be added:
286
296
  esper.switch_world("left")
287
297
  assert len(esper.get_component(ComponentA)) == 100
288
298
  create_entities(400)
@@ -293,6 +303,16 @@ def test_switch_world():
293
303
  # Some helper functions and Component templates:
294
304
  ##################################################
295
305
  def create_entities(number):
306
+ """This function will create X number of entities.
307
+
308
+ The entities are created with a mix of Components,
309
+ so the World context will see an addition of
310
+ ComponentA * number * 1
311
+ ComponentB * number * 1
312
+ ComponentC * number * 2
313
+ ComponentD * number * 1
314
+ ComponentE * number * 1
315
+ """
296
316
  for _ in range(number // 2):
297
317
  esper.create_entity(ComponentA(), ComponentB(), ComponentC())
298
318
  esper.create_entity(ComponentC(), ComponentD(), ComponentE())
@@ -459,17 +479,29 @@ def test_event_handler_switch_world():
459
479
  def handler():
460
480
  nonlocal called
461
481
  called += 1
482
+
483
+ # Switch to a new "left" World context, and register
484
+ # an event handler. Confirm that it is being called
485
+ # by checking that the 'called' variable is incremented.
462
486
  esper.switch_world("left")
463
487
  esper.set_handler("foo", handler)
464
488
  assert called == 0
465
489
  esper.dispatch_event("foo")
466
490
  assert called == 1
491
+
492
+ # Here we switch to a new "right" World context.
493
+ # The handler is registered to the "left" context only,
494
+ # so dispatching the event should have no effect. The
495
+ # handler is not attached, and so the 'called' value
496
+ # should not be incremented further.
467
497
  esper.switch_world("right")
468
- assert called == 1
469
498
  esper.dispatch_event("foo")
470
499
  assert called == 1
500
+
501
+ # Switching back to the "left" context and dispatching
502
+ # the event, the handler should still be registered and
503
+ # the 'called' variable should be incremented by 1.
471
504
  esper.switch_world("left")
472
- assert called == 1
473
505
  esper.dispatch_event("foo")
474
506
  assert called == 2
475
507
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes