22#
33# For the full list of built-in configuration values, see the documentation:
44# https://www.sphinx-doc.org/en/master/usage/configuration.html
5+
56import os
67import sys
78import time as _time
8- from mock import Mock as MagicMock
9+ from unittest . mock import MagicMock
910
1011
11- class DictMock ( MagicMock ) :
12- """Mock that supports dictionary-like behavior ."""
12+ class HashableMock :
13+ """A simple hashable mock that can be used as dictionary keys ."""
1314
14- def __getitem__ (self , key ):
15- return self .__dict__ .setdefault (key , DictMock ())
15+ _counter = 0
1616
17- def __setitem__ (self , key , value ):
18- self .__dict__ [key ] = value
17+ def __init__ (self , name = "mock" ):
18+ HashableMock ._counter += 1
19+ self ._name = name
20+ self ._id = HashableMock ._counter
1921
20- def __iter__ (self ):
21- return iter ([])
22+ def __hash__ (self ):
23+ return self . _id
2224
23- def __contains__ (self , key ):
25+ def __eq__ (self , other ):
26+ if isinstance (other , HashableMock ):
27+ return self ._id == other ._id
2428 return False
2529
26- def get (self , key , default = None ):
27- return self .__dict__ .get (key , default )
28-
29- # Support arithmetic operations
30- def __sub__ (self , other ):
31- return 0
32-
33- def __rsub__ (self , other ):
34- return 0
30+ def __repr__ (self ):
31+ return f"<HashableMock { self ._name } >"
3532
36- def __add__ (self , other ):
37- return 0
38-
39- def __radd__ (self , other ):
40- return 0
41-
42- def __mul__ (self , other ):
43- return 0
33+ def __call__ (self , * args , ** kwargs ):
34+ return HashableMock (self ._name )
4435
45- def __rmul__ (self , other ):
46- return 0
36+ def __getattr__ (self , name ):
37+ return HashableMock ( f" { self . _name } . { name } " )
4738
48- def __truediv__ (self , other ):
39+ def __int__ (self ):
4940 return 0
5041
51- def __rtruediv__ (self , other ):
52- return 0
42+ def __bool__ (self ):
43+ return False
5344
54- def __floordiv__ (self , other ):
55- return 0
5645
57- def __rfloordiv__ ( self , other ):
58- return 0
46+ class MockModule ( MagicMock ):
47+ """Mock module that returns HashableMock for attributes that might be used as dict keys."""
5948
60- def __int__ (self ):
61- return 0
49+ # Attributes known to be used as dict keys
50+ HASHABLE_ATTRS = {
51+ "MODE_AES" ,
52+ "MODE_CTR" ,
53+ "MODE_CBC" ,
54+ "MODE_ECB" ,
55+ "AES" ,
56+ "CTR" ,
57+ "CBC" ,
58+ "ECB" ,
59+ }
6260
63- def __float__ (self ):
64- return 0
61+ def __getattr__ (self , name ):
62+ if name in self .HASHABLE_ATTRS or name .startswith ("MODE_" ):
63+ return HashableMock (name )
64+ return MagicMock ()
6565
66- def __bool__ (self ):
67- return False
6866
69- def __len__ ( self ) :
70- return 0
67+ class MockWordlist :
68+ """Mock for embit.wordlists.bip39.WORDLIST - returns list of strings."""
7169
72- def __call__ (self , * args , ** kwargs ):
73- return DictMock ()
70+ WORDLIST = ["abandon" ] * 2048 # Dummy wordlist
7471
7572
7673class MockTime :
7774 """Mock time module with MicroPython-specific functions."""
7875
79- # Preserve all standard time functions
8076 def __getattr__ (self , name ):
81- return getattr (_time , name )
77+ if hasattr (_time , name ):
78+ return getattr (_time , name )
79+ return MagicMock ()
8280
83- # Add MicroPython-specific functions
8481 @staticmethod
8582 def ticks_ms ():
8683 return 0
@@ -91,11 +88,11 @@ def ticks_us():
9188
9289 @staticmethod
9390 def ticks_diff (a , b ):
94- return a - b
91+ return 0
9592
9693 @staticmethod
9794 def ticks_add (a , b ):
98- return a + b
95+ return 0
9996
10097 @staticmethod
10198 def sleep_ms (ms ):
@@ -106,110 +103,119 @@ def sleep_us(us):
106103 pass
107104
108105
109- # Create mock for board with required config structure
110- MOCK_BOARD = DictMock ()
111- MOCK_BOARD .config = MOCK_BOARD .config = {
112- "type" : "amigo" ,
113- "lcd" : {"height" : 320 , "width" : 480 , "invert" : 0 , "dir" : 40 , "lcd_type" : 1 },
114- "sdcard" : {"sclk" : 11 , "mosi" : 10 , "miso" : 6 , "cs" : 26 },
115- "board_info" : {
116- "BOOT_KEY" : 23 ,
117- "CONNEXT_A" : 7 ,
118- "CONNEXT_B" : 9 ,
119- "LED_R" : 14 ,
120- "LED_G" : 15 ,
121- "LED_B" : 17 ,
122- "LED_W" : 32 ,
123- "BACK" : 23 ,
124- "ENTER" : 16 ,
125- "NEXT" : 20 ,
126- "WIFI_TX" : 6 ,
127- "WIFI_RX" : 7 ,
128- "WIFI_EN" : 8 ,
129- "I2S0_MCLK" : 13 ,
130- "I2S0_SCLK" : 21 ,
131- "I2S0_WS" : 18 ,
132- "I2S0_IN_D0" : 35 ,
133- "I2S0_OUT_D2" : 34 ,
134- "I2C_SDA" : 27 ,
135- "I2C_SCL" : 24 ,
136- "SPI_SCLK" : 11 ,
137- "SPI_MOSI" : 10 ,
138- "SPI_MISO" : 6 ,
139- "SPI_CS" : 12 ,
140- },
141- "krux" : {
142- "pins" : {
143- "BUTTON_A" : 16 ,
144- "BUTTON_B" : 20 ,
145- "BUTTON_C" : 23 ,
146- "TOUCH_IRQ" : 33 ,
106+ class MockBoard :
107+ """Mock board module with config structure."""
108+
109+ config = {
110+ "type" : "amigo" ,
111+ "lcd" : {"height" : 320 , "width" : 480 , "invert" : 0 , "dir" : 40 , "lcd_type" : 1 },
112+ "sdcard" : {"sclk" : 11 , "mosi" : 10 , "miso" : 6 , "cs" : 26 },
113+ "board_info" : {
114+ "BOOT_KEY" : 23 ,
115+ "CONNEXT_A" : 7 ,
116+ "CONNEXT_B" : 9 ,
117+ "LED_R" : 14 ,
118+ "LED_G" : 15 ,
119+ "LED_B" : 17 ,
147120 "LED_W" : 32 ,
121+ "BACK" : 23 ,
122+ "ENTER" : 16 ,
123+ "NEXT" : 20 ,
124+ "WIFI_TX" : 6 ,
125+ "WIFI_RX" : 7 ,
126+ "WIFI_EN" : 8 ,
148127 "I2C_SDA" : 27 ,
149128 "I2C_SCL" : 24 ,
150129 },
151- "display" : {"touch" : True , "font" : [12 , 24 ], "font_wide" : [24 , 24 ]},
152- },
153- }
154-
155- MOCK_TIME = MockTime ()
130+ "krux" : {
131+ "pins" : {
132+ "BUTTON_A" : 16 ,
133+ "BUTTON_B" : 20 ,
134+ "BUTTON_C" : 23 ,
135+ "TOUCH_IRQ" : 33 ,
136+ "LED_W" : 32 ,
137+ "I2C_SDA" : 27 ,
138+ "I2C_SCL" : 24 ,
139+ },
140+ "display" : {"touch" : True , "font" : [12 , 24 ], "font_wide" : [24 , 24 ]},
141+ },
142+ }
156143
144+ def __getattr__ (self , name ):
145+ return MagicMock ()
157146
158- def ignore_modules (* args ):
159- for mod_name in list (args ):
160- if mod_name == "board" :
161- sys .modules [mod_name ] = MOCK_BOARD
162- elif mod_name == "time" :
163- sys .modules [mod_name ] = MOCK_TIME
164- else :
165- sys .modules [mod_name ] = DictMock ()
166-
167-
168- # Mock ALL MicroPython and hardware-dependent modules
169- ignore_modules (
170- # MicroPython built-ins
171- "machine" ,
172- "board" ,
173- "pmu" ,
174- "time" ,
175- "lcd" ,
176- "ujson" ,
177- "urandom" ,
178- "ucryptolib" ,
179- "uhashlib" ,
180- "uctypes" ,
181- "uos" ,
182- "usys" ,
183- "gc" ,
184- # K210/Maix specific
185- "Maix" ,
186- "fpioa_manager" ,
187- "sensor" ,
188- "image" ,
189- "flash" ,
190- # Crypto
191- "secp256k1" ,
192- "qrcode" ,
193- # Scientific (if used)
194- "numpy" ,
195- "scipy" ,
196- "scipy. linalg" ,
197- "scipy.signal" ,
198- )
199147
148+ # Create mock instances
149+ MOCK_TIME = MockTime ()
150+ MOCK_BOARD = MockBoard ()
151+ MOCK_UCRYPTOLIB = MockModule ()
152+ MOCK_UCRYPTOLIB .aes = MockModule ()
153+
154+ # Mock for embit.wordlists.bip39
155+ MOCK_BIP39 = type ("MockBip39" , (), {"WORDLIST" : ["abandon" ] * 2048 })()
156+
157+
158+ def setup_mocks ():
159+ """Setup all mocked modules."""
160+
161+ mocks = {
162+ # MicroPython built-ins
163+ "machine" : MagicMock (),
164+ "board" : MOCK_BOARD ,
165+ "pmu" : MagicMock (),
166+ "time" : MOCK_TIME ,
167+ "lcd" : MagicMock (),
168+ "ujson" : MagicMock (),
169+ "urandom" : MagicMock (),
170+ "ucryptolib" : MOCK_UCRYPTOLIB ,
171+ "uhashlib" : MagicMock (),
172+ "uhashlib_hw" : MagicMock (),
173+ "uctypes" : MagicMock (),
174+ "uos" : MagicMock (),
175+ "usys" : MagicMock (),
176+ "gc" : MagicMock (),
177+ # K210/Maix specific
178+ "Maix" : MagicMock (),
179+ "fpioa_manager" : MagicMock (),
180+ "sensor" : MagicMock (),
181+ "image" : MagicMock (),
182+ "flash" : MagicMock (),
183+ # Crypto/QR
184+ "secp256k1" : MagicMock (),
185+ "qrcode" : MagicMock (),
186+ # Embit (Bitcoin library)
187+ "embit" : MagicMock (),
188+ "embit.wordlists" : MagicMock (),
189+ "embit.wordlists.bip39" : MOCK_BIP39 ,
190+ "embit.psbt" : MagicMock (),
191+ "embit.networks" : MagicMock (),
192+ "embit.descriptor" : MagicMock (),
193+ "embit.descriptor.descriptor" : MagicMock (),
194+ "embit.descriptor.arguments" : MagicMock (),
195+ # Scientific (if needed)
196+ "numpy" : MagicMock (),
197+ "scipy" : MagicMock (),
198+ "scipy.linalg" : MagicMock (),
199+ "scipy.signal" : MagicMock (),
200+ }
201+
202+ for mod_name , mock_obj in mocks .items ():
203+ sys .modules [mod_name ] = mock_obj
204+
205+
206+ # Setup mocks before importing anything
207+ setup_mocks ()
208+
209+ # Add source path
200210sys .path .insert (0 , os .path .abspath ("../src/" ))
201211
202212# -- Project information -----------------------------------------------------
203- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
204-
205213project = "Krux"
206214copyright = "2022, MIT"
207215author = "Selfcustody"
208216release = "2022"
209217
210218# -- General configuration ---------------------------------------------------
211- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
212-
213219extensions = [
214220 "sphinx.ext.autodoc" ,
215221 "sphinx.ext.coverage" ,
@@ -224,9 +230,23 @@ def ignore_modules(*args):
224230templates_path = ["_templates" ]
225231exclude_patterns = []
226232
233+ # Suppress duplicate object warnings
234+ suppress_warnings = ["autodoc" , "autodoc.import_object" ]
227235
228236# -- Options for HTML output -------------------------------------------------
229- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
230-
231237html_theme = "sphinx_rtd_theme"
232- html_static_path = ["_static" ]
238+ html_static_path = [] # Remove _static to avoid warning
239+
240+ # Suppress warnings
241+ suppress_warnings = ["autodoc" ]
242+
243+
244+ def skip_mock_members (app , what , name , obj , skip , options ):
245+ """Skip mock objects that can't be documented."""
246+ if "Mock" in str (type (obj )):
247+ return True
248+ return skip
249+
250+
251+ def setup (app ):
252+ app .connect ("autodoc-skip-member" , skip_mock_members )
0 commit comments