Skip to content

Commit 6344308

Browse files
Update docs
1 parent e0fef0f commit 6344308

File tree

5 files changed

+244
-174
lines changed

5 files changed

+244
-174
lines changed

docs/apps/creating-apps.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ com.micropythonos.helloworld/
2525

2626
In `hello.py`, put:
2727

28-
```
28+
```python
2929
from mpos import Activity
30+
import lvgl as lv
3031

3132
class Hello(Activity):
3233

@@ -106,8 +107,9 @@ If the app is installed into the /apps/ folder, it should show up in the launche
106107

107108
You can also launch it manually by typing this in the MicroPython REPL:
108109

109-
```
110-
from mpos import AppManager ; AppManager.start_app('apps/com.micropythonos.helloworld/')
110+
```python
111+
from mpos import AppManager
112+
AppManager.start_app('com.micropythonos.helloworld')
111113
```
112114

113115

docs/architecture/frameworks.md

Lines changed: 116 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -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
6262
class 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]
260281
class 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
327376
from 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
381438
import unittest
@@ -384,8 +441,7 @@ from mpos import MyFramework
384441
class 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

Comments
 (0)