Initial commit
This commit is contained in:
+227
@@ -0,0 +1,227 @@
|
||||
import json
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Union, Type
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import pygame
|
||||
|
||||
PYGAME_AVAILABLE = True
|
||||
except ImportError:
|
||||
PYGAME_AVAILABLE = False
|
||||
|
||||
|
||||
class EnumSerializer:
|
||||
"""
|
||||
A class to serialize and deserialize Enum classes and collections to/from JSON files.
|
||||
Handles dynamic Enum structures, various collection types, and Pygame keys.
|
||||
"""
|
||||
|
||||
# Cache for Pygame key mappings
|
||||
_pygame_key_to_name = None
|
||||
_pygame_name_to_key = None
|
||||
|
||||
@classmethod
|
||||
def _init_pygame_mappings(cls):
|
||||
"""Initialize Pygame key mappings if not already done."""
|
||||
if not PYGAME_AVAILABLE or cls._pygame_key_to_name is not None:
|
||||
return
|
||||
|
||||
cls._pygame_key_to_name = {}
|
||||
cls._pygame_name_to_key = {}
|
||||
|
||||
# Get all K_* constants from pygame
|
||||
for attr_name in dir(pygame):
|
||||
if attr_name.startswith('K_'):
|
||||
key_value = getattr(pygame, attr_name)
|
||||
cls._pygame_key_to_name[key_value] = attr_name
|
||||
cls._pygame_name_to_key[attr_name] = key_value
|
||||
|
||||
@classmethod
|
||||
def _is_pygame_key(cls, value: Any) -> bool:
|
||||
"""Check if a value is a Pygame key constant."""
|
||||
if not PYGAME_AVAILABLE or not isinstance(value, int):
|
||||
return False
|
||||
|
||||
cls._init_pygame_mappings()
|
||||
return value in cls._pygame_key_to_name
|
||||
|
||||
@classmethod
|
||||
def _serialize_pygame_key(cls, key_value: int) -> Dict[str, Any]:
|
||||
"""Serialize a Pygame key to human-readable format."""
|
||||
cls._init_pygame_mappings()
|
||||
return {
|
||||
'__type__': 'PygameKey',
|
||||
'name': cls._pygame_key_to_name[key_value],
|
||||
'value': key_value # Keep numeric value as backup
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _serialize_enum_class(enum_class: Type[Enum]) -> Dict[str, Any]:
|
||||
"""Serialize an Enum class definition to a dictionary."""
|
||||
return {
|
||||
'__type__': 'EnumClass',
|
||||
'name': enum_class.__name__,
|
||||
'members': {member.name: member.value for member in enum_class}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _serialize_enum_instance(enum_instance: Enum) -> Dict[str, Any]:
|
||||
"""Serialize an Enum instance to a dictionary."""
|
||||
return {
|
||||
'__type__': 'EnumInstance',
|
||||
'class_name': enum_instance.__class__.__name__,
|
||||
'name': enum_instance.name,
|
||||
'value': enum_instance.value
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _serialize_value(value: Any) -> Any:
|
||||
"""Recursively serialize a value, handling Enums, Pygame keys, and collections."""
|
||||
# Check for Pygame key first
|
||||
if EnumSerializer._is_pygame_key(value):
|
||||
return EnumSerializer._serialize_pygame_key(value)
|
||||
elif isinstance(value, Enum):
|
||||
return EnumSerializer._serialize_enum_instance(value)
|
||||
elif isinstance(value, dict):
|
||||
return {k: EnumSerializer._serialize_value(v) for k, v in value.items()}
|
||||
elif isinstance(value, (list, tuple, set)):
|
||||
serialized = [EnumSerializer._serialize_value(item) for item in value]
|
||||
return {
|
||||
'__type__': type(value).__name__,
|
||||
'items': serialized
|
||||
}
|
||||
else:
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def save_to_file(data: Dict[str, Any], filename: str) -> None:
|
||||
"""
|
||||
Save data (Enums, Pygame keys, and collections) to a JSON file.
|
||||
|
||||
Args:
|
||||
data: Dictionary containing the data to save. Keys are variable names,
|
||||
values can be Enum classes, Enum instances, Pygame keys, or collections.
|
||||
filename: Path to the JSON file where data will be saved.
|
||||
|
||||
Example:
|
||||
serializer = EnumSerializer()
|
||||
data = {
|
||||
'color_enum': Color,
|
||||
'current_color': Color.RED,
|
||||
'color_list': [Color.RED, Color.BLUE],
|
||||
'settings': {'theme': Color.DARK, 'count': 5},
|
||||
'move_left': pygame.K_LEFT,
|
||||
'controls': {'left': pygame.K_LEFT, 'right': pygame.K_RIGHT}
|
||||
}
|
||||
serializer.save_to_file(data, 'config.json')
|
||||
"""
|
||||
serialized_data = {}
|
||||
|
||||
for key, value in data.items():
|
||||
if isinstance(value, type) and issubclass(value, Enum):
|
||||
# Serialize Enum class definition
|
||||
serialized_data[key] = EnumSerializer._serialize_enum_class(value)
|
||||
else:
|
||||
# Serialize value (could be Enum instance, Pygame key, or collection)
|
||||
serialized_data[key] = EnumSerializer._serialize_value(value)
|
||||
|
||||
Path(filename).parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
json.dump(serialized_data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
@staticmethod
|
||||
def _deserialize_enum_class(data: Dict[str, Any]) -> Type[Enum]:
|
||||
"""Recreate an Enum class from serialized data."""
|
||||
name = data['name']
|
||||
members = data['members']
|
||||
return Enum(name, members)
|
||||
|
||||
@classmethod
|
||||
def _deserialize_value(cls, value: Any, enum_registry: Dict[str, Type[Enum]]) -> Any:
|
||||
"""Recursively deserialize a value, handling Enums, Pygame keys, and collections."""
|
||||
if isinstance(value, dict):
|
||||
if '__type__' in value:
|
||||
type_name = value['__type__']
|
||||
|
||||
if type_name == 'PygameKey':
|
||||
# Restore Pygame key
|
||||
if PYGAME_AVAILABLE:
|
||||
cls._init_pygame_mappings()
|
||||
key_name = value['name']
|
||||
if key_name in cls._pygame_name_to_key:
|
||||
return cls._pygame_name_to_key[key_name]
|
||||
# Fallback to numeric value if Pygame not available
|
||||
return value['value']
|
||||
|
||||
elif type_name == 'EnumInstance':
|
||||
# Restore Enum instance
|
||||
class_name = value['class_name']
|
||||
if class_name in enum_registry:
|
||||
enum_class = enum_registry[class_name]
|
||||
return enum_class[value['name']]
|
||||
else:
|
||||
# Return raw data if Enum class not found
|
||||
return value
|
||||
|
||||
elif type_name in ('list', 'tuple', 'set'):
|
||||
# Restore collection
|
||||
items = [cls._deserialize_value(item, enum_registry)
|
||||
for item in value['items']]
|
||||
if type_name == 'tuple':
|
||||
return tuple(items)
|
||||
elif type_name == 'set':
|
||||
return set(items)
|
||||
else:
|
||||
return items
|
||||
else:
|
||||
# Regular dictionary
|
||||
return {k: cls._deserialize_value(v, enum_registry)
|
||||
for k, v in value.items()}
|
||||
else:
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def load_from_file(filename: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Load data (Enums, Pygame keys, and collections) from a JSON file.
|
||||
|
||||
Args:
|
||||
filename: Path to the JSON file to load.
|
||||
|
||||
Returns:
|
||||
Dictionary with deserialized data. Enum classes, instances, and Pygame keys are restored.
|
||||
|
||||
Example:
|
||||
serializer = EnumSerializer()
|
||||
data = serializer.load_from_file('config.json')
|
||||
Color = data['color_enum']
|
||||
current_color = data['current_color']
|
||||
left_key = data['move_left'] # Will be pygame.K_LEFT
|
||||
"""
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
serialized_data = json.load(f)
|
||||
|
||||
# First pass: restore all Enum class definitions
|
||||
enum_registry = {}
|
||||
for key, value in serialized_data.items():
|
||||
if isinstance(value, dict) and value.get('__type__') == 'EnumClass':
|
||||
enum_class = EnumSerializer._deserialize_enum_class(value)
|
||||
enum_registry[enum_class.__name__] = enum_class
|
||||
|
||||
# Second pass: deserialize all values
|
||||
deserialized_data = {}
|
||||
for key, value in serialized_data.items():
|
||||
if isinstance(value, dict) and value.get('__type__') == 'EnumClass':
|
||||
# Return the Enum class itself
|
||||
deserialized_data[key] = enum_registry[value['name']]
|
||||
else:
|
||||
# Deserialize value
|
||||
deserialized_data[key] = EnumSerializer._deserialize_value(value, enum_registry)
|
||||
|
||||
return deserialized_data
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
Reference in New Issue
Block a user