Ajout de vérification de types et de documentation

This commit is contained in:
Tipragot 2023-10-24 11:58:43 +02:00
parent b6b4de2436
commit 3ec12b5bd5
9 changed files with 110 additions and 128 deletions

3
.pylintrc Normal file
View file

@ -0,0 +1,3 @@
[MESSAGES CONTROL]
disable=all
enable= missing-docstring

6
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,6 @@
{
"recommendations": [
"matangover.mypy",
"ms-python.pylint"
]
}

3
.vscode/launch.json vendored
View file

@ -6,7 +6,8 @@
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"module": "main", "module": "main",
"justMyCode": true "justMyCode": true,
"preLaunchTask": "Python: Check",
} }
] ]
} }

29
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,29 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Python: MyPy",
"type": "shell",
"command": "mypy",
"args": [
"."
]
},
{
"label": "Python: Pylint",
"type": "shell",
"command": "pylint",
"args": [
"**/*.py"
]
},
{
"label": "Python: Check",
"dependsOrder": "sequence",
"dependsOn": [
"Python: MyPy",
"Python: Pylint",
]
},
]
}

View file

@ -1,23 +1,33 @@
from typing import Iterator, Callable """
Système de gestion d'entités, composants et ressources.
"""
from typing import Iterator, Callable, Tuple, TypeVar, Type, Optional
from logging import error from logging import error
_T = TypeVar("_T")
"""
Un type générique utilisé pour les composants et les ressources.
"""
class World: class World:
""" """
Un monde contenant des entités et des ressources. Un monde contenant des entités et des ressources.
""" """
def __init__(self): def __init__(self) -> None:
""" """
Permet de créer un nouveau monde vide. Permet de créer un nouveau monde vide.
""" """
self._to_apply: set[Entity] = set() self._to_apply: set[Entity] = set()
self._entities: set[Entity] = set() self._entities: set[Entity] = set()
self._mapping: dict[type, set[Entity]] = {} self._mapping: dict[Type, set[Entity]] = {}
self._to_apply_resources: list[tuple[type, object]] = [] self._to_apply_resources: list[tuple[Type, Optional[object]]] = []
self._resources: dict[type, object] = {} self._resources: dict[Type, object] = {}
def create_entity(self, *components): def create_entity(self, *components: _T) -> "Entity":
""" """
Crée une entité avec les composants donnés en paramètres. Crée une entité avec les composants donnés en paramètres.
@ -29,7 +39,7 @@ class World:
""" """
return Entity(self, *components) return Entity(self, *components)
def remove_entity(self, entity: "Entity"): def remove_entity(self, entity: "Entity") -> None:
""" """
Supprime une entité du monde. Supprime une entité du monde.
@ -39,7 +49,7 @@ class World:
entity._deleted = True entity._deleted = True
self._to_apply.add(entity) self._to_apply.add(entity)
def set(self, *resources): def set(self, *resources: _T) -> None:
""" """
Définit les ressources données en paramètres. Définit les ressources données en paramètres.
Si les ressources existent deja, elles seront remplacées. Si les ressources existent deja, elles seront remplacées.
@ -50,7 +60,7 @@ class World:
for resource in resources: for resource in resources:
self._to_apply_resources.append((type(resource), resource)) self._to_apply_resources.append((type(resource), resource))
def remove(self, *resource_types): def remove(self, *resource_types: Type[_T]) -> None:
""" """
Supprime les ressources données en paramètres. Supprime les ressources données en paramètres.
@ -61,7 +71,7 @@ class World:
if resource_type in self._resources: if resource_type in self._resources:
self._to_apply_resources.append((resource_type, None)) self._to_apply_resources.append((resource_type, None))
def apply(self): def apply(self) -> None:
""" """
Applique les changements réaliser dans le monde. Applique les changements réaliser dans le monde.
""" """
@ -77,7 +87,7 @@ class World:
del entity._components[component_type] del entity._components[component_type]
self._mapping[component_type].remove(entity) self._mapping[component_type].remove(entity)
else: else:
entity._components[component_type] = component entity._components[component_type] = component # type: ignore[assignment]
self._mapping.setdefault(component_type, set()).add(entity) self._mapping.setdefault(component_type, set()).add(entity)
entity._to_apply.clear() entity._to_apply.clear()
self._to_apply.clear() self._to_apply.clear()
@ -88,7 +98,9 @@ class World:
self._resources[resource_type] = resource self._resources[resource_type] = resource
self._to_apply_resources.clear() self._to_apply_resources.clear()
def query(self, *needed: type, without: tuple[type] = []) -> Iterator["Entity"]: def query(
self, *needed: Type[_T], without: Tuple[Type[_T], ...] = ()
) -> Iterator["Entity"]:
""" """
Renvoie les entités qui ont les composants de *needed et sans les composants de *without. Renvoie les entités qui ont les composants de *needed et sans les composants de *without.
@ -112,7 +124,7 @@ class World:
) and all(without_type not in entity for without_type in without): ) and all(without_type not in entity for without_type in without):
yield entity yield entity
def __getitem__(self, resource_type: type) -> object: def __getitem__(self, resource_type: Type[_T]) -> _T:
""" """
Renvoie la ressource de type *resource_type. Renvoie la ressource de type *resource_type.
@ -122,19 +134,23 @@ class World:
Retourne: Retourne:
La ressource de type *resource_type. La ressource de type *resource_type.
""" """
return self._resources[resource_type] resource: _T = self._resources[resource_type] # type: ignore[assignment]
return resource
def __contains__(self, resource_type: type) -> bool: def __contains__(self, *resource_types: Type[_T]) -> bool:
""" """
Renvoie si la ressource de type *resource_type existe. Renvoie True si le monde contient toutes les ressources de *resource_types.
Paramètres: Paramètres:
resource_type: Le type de ressource à tester. *resource_types: Les types de ressource à tester.
Retourne: Retourne:
Si la ressource de type *resource_type existe. Si toutes les ressources de *resource_types sont dans le monde.
""" """
return resource_type in self._resources for resource_type in resource_types:
if resource_type not in self._resources:
return False
return True
class Entity: class Entity:
@ -142,7 +158,7 @@ class Entity:
Une entité du monde. Une entité du monde.
""" """
def __init__(self, world: World, *components): def __init__(self, world: World, *components: _T):
""" """
Créer une entité avec les composants en paramètres et l'ajoute au monde. Créer une entité avec les composants en paramètres et l'ajoute au monde.
@ -151,14 +167,14 @@ class Entity:
*components: Les composants de l'entité. *components: Les composants de l'entité.
""" """
self._world = world self._world = world
self._to_apply: list[tuple[type, object]] = [ self._to_apply: list[tuple[Type, Optional[object]]] = [
(type(component), component) for component in components (type(component), component) for component in components
] ]
self._components: dict[type, object] = {} self._components: dict[Type[_T], _T] = {}
self._deleted = False self._deleted = False
self._world._to_apply.add(self) self._world._to_apply.add(self)
def set(self, *components): def set(self, *components: _T) -> None:
""" """
Définit les composants de l'entité donnés en paramètres. Définit les composants de l'entité donnés en paramètres.
@ -169,7 +185,7 @@ class Entity:
self._to_apply.append((type(component), component)) self._to_apply.append((type(component), component))
self._world._to_apply.add(self) self._world._to_apply.add(self)
def remove(self, *component_types: type): def remove(self, *component_types: Type[_T]) -> None:
""" """
Supprime les composants de l'entité donnés en paramètres. Supprime les composants de l'entité donnés en paramètres.
@ -181,7 +197,7 @@ class Entity:
self._to_apply.append((component_type, None)) self._to_apply.append((component_type, None))
self._world._to_apply.add(self) self._world._to_apply.add(self)
def __getitem__(self, component_type: type) -> object: def __getitem__(self, component_type: Type[_T]) -> _T:
""" """
Renvoie le composant de type *component_type. Renvoie le composant de type *component_type.
@ -193,17 +209,20 @@ class Entity:
""" """
return self._components[component_type] return self._components[component_type]
def __contains__(self, component_type: type) -> bool: def __contains__(self, *component_types: Type[_T]) -> bool:
""" """
Renvoie si le composant de type *component_type existe. Renvoie True si l'entité contient tous les composants de *component_types.
Paramètres: Paramètres:
component_type: Le type du composant à tester. component_type: Les types des composants à tester.
Retourne: Retourne:
Si le composant de type *component_type existe. Si tous les composants de *component_types sont dans l'entité.
""" """
return component_type in self._components for component_type in component_types:
if component_type not in self._components:
return False
return True
class Game: class Game:
@ -211,7 +230,7 @@ class Game:
Permet de faire une simple boucle de jeu. Permet de faire une simple boucle de jeu.
""" """
def __init__(self): def __init__(self) -> None:
""" """
Créer une un jeu. Créer une un jeu.
""" """
@ -220,7 +239,7 @@ class Game:
self._update_tasks: dict[int, list[Callable[[World], None]]] = {} self._update_tasks: dict[int, list[Callable[[World], None]]] = {}
self._shutdown_tasks: dict[int, list[Callable[[World], None]]] = {} self._shutdown_tasks: dict[int, list[Callable[[World], None]]] = {}
def add_startup_tasks(self, priority: int, *tasks: Callable[[World], None]): def add_startup_tasks(self, priority: int, *tasks: Callable[[World], None]) -> None:
""" """
Ajoute des taches qui s'executeront au démarrage du jeu. Ajoute des taches qui s'executeront au démarrage du jeu.
@ -232,7 +251,7 @@ class Game:
raise RuntimeError("Cannot add startup task while the loop is running") raise RuntimeError("Cannot add startup task while the loop is running")
self._startup_tasks.setdefault(priority, []).extend(tasks) self._startup_tasks.setdefault(priority, []).extend(tasks)
def add_tasks(self, priority: int, *tasks: Callable[[World], None]): def add_tasks(self, priority: int, *tasks: Callable[[World], None]) -> None:
""" """
Ajoute des taches qui s'executeront a chaque mise à jour du jeu. Ajoute des taches qui s'executeront a chaque mise à jour du jeu.
@ -244,7 +263,9 @@ class Game:
raise RuntimeError("Cannot add task while the loop is running") raise RuntimeError("Cannot add task while the loop is running")
self._update_tasks.setdefault(priority, []).extend(tasks) self._update_tasks.setdefault(priority, []).extend(tasks)
def add_shutdown_tasks(self, priority: int, *tasks: Callable[[World], None]): def add_shutdown_tasks(
self, priority: int, *tasks: Callable[[World], None]
) -> None:
""" """
Ajoute des taches qui s'executeront à la fin de la boucle de jeu. Ajoute des taches qui s'executeront à la fin de la boucle de jeu.
@ -256,7 +277,9 @@ class Game:
raise RuntimeError("Cannot add shutdown task while the loop is running") raise RuntimeError("Cannot add shutdown task while the loop is running")
self._shutdown_tasks.setdefault(priority, []).extend(tasks) self._shutdown_tasks.setdefault(priority, []).extend(tasks)
def _run_tasks(self, tasks: dict[int, list[Callable[[World], None]]], world: World): def _run_tasks(
self, tasks: dict[int, list[Callable[[World], None]]], world: World
) -> None:
""" """
Execute toutes les taches donnes en paramètres en respectant la priorité. Execute toutes les taches donnes en paramètres en respectant la priorité.
""" """
@ -277,7 +300,7 @@ class Game:
self._running = True self._running = True
# On initialize le monde # On initialize le monde
world = World() world: World = World()
world.set(self) world.set(self)
# On applique les moddifications pour l'ajout de la ressource # On applique les moddifications pour l'ajout de la ressource
@ -305,7 +328,7 @@ class Game:
# On retourne le monde # On retourne le monde
return world return world
def stop(self): def stop(self) -> None:
""" """
Demande la fin de la boucle de jeu. La boucle s'arretera a la prochaine mise à jour. Demande la fin de la boucle de jeu. La boucle s'arretera a la prochaine mise à jour.
""" """

View file

@ -1,90 +0,0 @@
from engine import World # Doit être mis dans le dossier principale pour fonctionner
# Création de composants pouvant être ajouté a des entitées
class Name(str):
pass
class Age(int):
pass
# Création d'un monde
world = World()
# Création de plusieurs entités
david = world.create_entity(Name("David"), Age(25))
fred = world.create_entity(Name("Fred"), Age(30))
paul_sans_age = world.create_entity(Name("Paul"))
age_tout_cour = world.create_entity(Age(14))
# On applique les moddifications
world.apply()
print("Récupération de toutes les entitées qui ont un nom")
for entity in world.query(Name):
print(entity[Name])
print("Récupération de toutes les entitées qui ont un age")
for entity in world.query(Age):
print(entity[Age])
# On change l'age de Fred
fred.set(Age(45))
# On applique les moddifications
world.apply()
print("Récupération de toutes les entités qui ont un nom et un age")
for entity in world.query(Name, Age):
print(entity[Name], entity[Age])
print("Récupération de toutes les entitées qui ont un nom mais pas d'age")
for entity in world.query(Name, without=(Age,)):
print(entity[Name])
print("Récupération de toutes les entités qui ont un age mais pas de nom")
for entity in world.query(Age, without=(Name,)):
print(entity[Age])
print("Récupération de toutes les entités")
for entity in world.query():
if Name in entity:
print(entity[Name], end=" ")
if Age in entity:
print(entity[Age], end=" ")
print()
# Création d'une ressource pouvant être ajoutée a un monde
class Gravity(float):
pass
# On peut aussi ajouter des ressources globales
world.set(Gravity(9.81))
print("On vérifie que la ressource Gravity existe")
print(Gravity in world)
# On applique les moddifications
world.apply()
print("On vérifie que la ressource Gravity existe après l'application")
print(Gravity in world)
print("Récupération de la ressource Gravity")
print(world[Gravity])
# On supprime la ressource Gravity
world.remove(Gravity)
print("On vérifie que la ressource Gravity n'existe plus")
print(Gravity in world)
# On applique les moddifications
world.apply()
print("On vérifie que la ressource Gravity n'existe plus")
print(Gravity in world)

View file

@ -1,3 +1,8 @@
"""
Ceci est un exemple de comment l'on peut utiliser le moteur du jeu.
"""
from engine import Game from engine import Game

3
mypy.ini Normal file
View file

@ -0,0 +1,3 @@
[mypy]
check_untyped_defs = True
disallow_untyped_defs = True

View file

@ -0,0 +1,2 @@
mypy
pylint