@@ -56,44 +56,65 @@ Frameworks are centralized services that provide access to system capabilities l
5656
5757## Unified Framework Pattern
5858
59- All frameworks follow this standardized structure:
59+ All frameworks follow a ** singleton pattern with class method delegation ** . This provides a clean API where you call class methods directly without needing to instantiate or call ` .get() ` .
6060
6161``` python
6262class MyFramework :
6363 """ Centralized service for [purpose]."""
6464
65- _initialized = False
66- _instance_data = {}
65+ _instance = None # Singleton instance
6766
68- @ classmethod
69- def init (cls , * args , ** kwargs ):
70- """ Initialize the framework (call once at startup)."""
71- cls ._initialized = True
67+ def __init__ (self ):
68+ """ Initialize singleton instance (called once)."""
69+ if MyFramework._instance:
70+ return
71+ MyFramework._instance = self
7272 # initialization logic
73- return True
7473
7574 @ classmethod
76- def is_available (cls ):
77- """ Check if framework is available."""
78- return cls ._initialized
75+ def get (cls ):
76+ """ Get or create the singleton instance."""
77+ if cls ._instance is None :
78+ cls ._instance = cls ()
79+ return cls ._instance
7980
80- @ classmethod
81- def method_name (cls , * args , ** kwargs ):
82- """ Framework methods as class methods."""
81+ def method_name (self , * args , ** kwargs ):
82+ """ Instance methods (implementation)."""
8383 # implementation
8484 return result
85+
86+ # Class method delegation (at module level)
87+ _original_methods = {}
88+ _methods_to_delegate = [' method_name' ]
89+
90+ for method_name in _methods_to_delegate:
91+ _original_methods[method_name] = getattr (MyFramework, method_name)
92+
93+ def _make_class_method (method_name ):
94+ """ Create a class method that delegates to the singleton instance."""
95+ original_method = _original_methods[method_name]
96+
97+ @ classmethod
98+ def class_method (cls , * args , ** kwargs ):
99+ instance = cls .get()
100+ return original_method(instance, * args, ** kwargs)
101+
102+ return class_method
103+
104+ for method_name in _methods_to_delegate:
105+ setattr (MyFramework, method_name, _make_class_method(method_name))
85106```
86107
87108### Key Characteristics
88109
89110| Aspect | Details |
90111| --------| ---------|
91- | ** Instantiation** | No instance creation needed - use class methods directly |
92- | ** Initialization** | Call ` Framework.init() ` once at startup |
93- | ** State Management** | Use class variables ( ` _instance_data ` , ` _initialized ` ) |
94- | ** API Access** | ` Framework.method_name(...) ` - no ` .get() ` needed |
95- | ** Async Support** | Class methods can be async (` async def ` ) |
96- | ** Testing** | Easy to mock class methods and class variables |
112+ | ** Instantiation** | Singleton pattern - single instance created on first use |
113+ | ** Initialization** | Call ` Framework.init() ` once at startup (or auto-initialize on first use) |
114+ | ** State Management** | Instance variables stored in singleton instance |
115+ | ** API Access** | ` Framework.method_name(...) ` - class methods delegate to singleton |
116+ | ** Async Support** | Instance methods can be async (` async def ` ) |
117+ | ** Testing** | Easy to mock by replacing ` _instance ` |
97118
98119## Available Frameworks
99120
@@ -260,11 +281,23 @@ MyFramework - [Brief description of what this framework does]
260281class MyFramework :
261282 """ Centralized service for [purpose]."""
262283
263- _initialized = False
264- _instance_data = {}
284+ _instance = None # Singleton instance
285+
286+ def __init__ (self ):
287+ """ Initialize singleton instance."""
288+ if MyFramework._instance:
289+ return
290+ MyFramework._instance = self
291+ # initialization logic
265292
266293 @ classmethod
267- def init (cls , ** kwargs ):
294+ def get (cls ):
295+ """ Get or create the singleton instance."""
296+ if cls ._instance is None :
297+ cls ._instance = cls ()
298+ return cls ._instance
299+
300+ def init (self , ** kwargs ):
268301 """
269302 Initialize the framework.
270303
@@ -274,21 +307,15 @@ class MyFramework:
274307 Returns:
275308 bool: True if initialization successful
276309 """
277- if cls ._initialized:
278- return True
279-
280310 # Perform initialization
281- cls ._instance_data[' config' ] = kwargs
282- cls ._initialized = True
311+ self ._config = kwargs
283312 return True
284313
285- @ classmethod
286- def is_available (cls ):
314+ def is_available (self ):
287315 """ Check if framework is available."""
288- return cls ._initialized
316+ return hasattr ( self , ' _config ' )
289317
290- @ classmethod
291- def your_method (cls , arg1 , arg2 ):
318+ def your_method (self , arg1 , arg2 ):
292319 """
293320 Do something.
294321
@@ -299,34 +326,63 @@ class MyFramework:
299326 Returns:
300327 Result of operation
301328 """
302- if not cls .is_available():
329+ if not self .is_available():
303330 raise RuntimeError (" MyFramework not initialized" )
304331
305332 # Implementation
306333 return result
334+
335+ # Class method delegation (at module level)
336+ _original_methods = {}
337+ _methods_to_delegate = [' init' , ' is_available' , ' your_method' ]
338+
339+ for method_name in _methods_to_delegate:
340+ _original_methods[method_name] = getattr (MyFramework, method_name)
341+
342+ def _make_class_method (method_name ):
343+ """ Create a class method that delegates to the singleton instance."""
344+ original_method = _original_methods[method_name]
345+
346+ @ classmethod
347+ def class_method (cls , * args , ** kwargs ):
348+ instance = cls .get()
349+ return original_method(instance, * args, ** kwargs)
350+
351+ return class_method
352+
353+ for method_name in _methods_to_delegate:
354+ setattr (MyFramework, method_name, _make_class_method(method_name))
307355```
308356
309357### Checklist for New Frameworks
310358
311- - [ ] Inherit from no base class (use class methods only)
312- - [ ] Include ` _initialized ` class variable
313- - [ ] Include ` _instance_data ` class variable for state
314- - [ ] Implement ` init() ` classmethod for initialization
315- - [ ] Implement ` is_available() ` classmethod to check status
316- - [ ] All public methods are classmethods
359+ - [ ] Implement singleton pattern with ` _instance ` class variable
360+ - [ ] Implement ` __init__() ` to initialize singleton
361+ - [ ] Implement ` get() ` classmethod to get/create singleton
362+ - [ ] Implement ` init() ` instance method for initialization
363+ - [ ] Implement ` is_available() ` instance method to check status
364+ - [ ] All public methods are instance methods
365+ - [ ] Add class method delegation at module level
317366- [ ] Add docstrings to all methods
318367- [ ] Import in ` mpos/__init__.py `
319368- [ ] Add to board initialization files
320369- [ ] Write tests in ` MicroPythonOS/tests/ `
321370
322371## Import Pattern
323372
324- All frameworks are imported consistently as classes:
373+ All frameworks are imported consistently as classes from the main ` mpos ` module :
325374
326375``` python
327376from mpos import AppearanceManager, AudioFlinger, CameraManager, ConnectivityManager, DownloadManager, SensorManager, SharedPreferences, TaskManager, WifiService, AppManager
377+
378+ # Then use class methods directly (no .get() needed)
379+ AppearanceManager.init(prefs)
380+ AudioFlinger.play_wav(" music.wav" )
381+ SensorManager.read_sensor(accel)
328382```
329383
384+ ** Note:** Some frameworks like ` AudioFlinger ` and ` SensorManager ` use singleton patterns internally, but the API is the same - call class methods directly without needing to call ` .get() ` .
385+
330386## Benefits of Harmonization
331387
332388| Aspect | Before | After |
@@ -357,25 +413,26 @@ prefs.set_string("theme", "dark")
357413
358414### Do's
359415
360- ✅ Call ` Framework.init() ` once at system startup
361- ✅ Use class methods directly without ` .get() `
362- ✅ Check ` is_available() ` before using framework
363- ✅ Use class variables for state management
364- ✅ Document initialization requirements
365- ✅ Write tests for framework behavior
416+ ✅ Call ` Framework.init() ` once at system startup
417+ ✅ Use class methods directly (they delegate to singleton)
418+ ✅ Check ` is_available() ` before using framework
419+ ✅ Store state in singleton instance variables
420+ ✅ Document initialization requirements
421+ ✅ Write tests for framework behavior
422+ ✅ Use class method delegation pattern for clean API
366423
367424### Don'ts
368425
369- ❌ Create instances of framework classes
370- ❌ Call ` .get() ` to access frameworks
371- ❌ Mix module-level functions with class methods
372- ❌ Store framework state in global variables
373- ❌ Skip initialization in board files
374- ❌ Use different patterns for different frameworks
426+ ❌ Create multiple instances of framework classes
427+ ❌ Call ` .get() ` directly in app code (it's internal)
428+ ❌ Mix instance methods with class methods in public API
429+ ❌ Store framework state in global variables
430+ ❌ Skip initialization in board files
431+ ❌ Use different patterns for different frameworks
375432
376433## Testing Frameworks
377434
378- Frameworks are easy to test due to their class method design :
435+ Frameworks are easy to test due to their singleton pattern :
379436
380437``` python
381438import unittest
@@ -384,8 +441,7 @@ from mpos import MyFramework
384441class TestMyFramework (unittest .TestCase ):
385442 def setUp (self ):
386443 """ Reset framework state before each test."""
387- MyFramework._initialized = False
388- MyFramework._instance_data = {}
444+ MyFramework._instance = None # Reset singleton
389445
390446 def test_initialization (self ):
391447 """ Test framework initialization."""
@@ -398,6 +454,10 @@ class TestMyFramework(unittest.TestCase):
398454 MyFramework.init()
399455 result = MyFramework.your_method(" arg1" , " arg2" )
400456 self .assertEqual(result, expected_value)
457+
458+ def tearDown (self ):
459+ """ Clean up after each test."""
460+ MyFramework._instance = None # Reset singleton
401461```
402462
403463## See Also
0 commit comments