rootlp 0.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rootlp/__init__.py +23 -0
- rootlp/modules/Section_LP/Section.py +177 -0
- rootlp/modules/Section_LP/__init__.py +0 -0
- rootlp/modules/Section_LP/test_Section.py +44 -0
- rootlp/modules/__init__.py +0 -0
- rootlp/modules/getfunction_LP/__init__.py +0 -0
- rootlp/modules/getfunction_LP/getfunction.py +97 -0
- rootlp/modules/getfunction_LP/test_getfunction.py +79 -0
- rootlp/modules/main_LP/__init__.py +0 -0
- rootlp/modules/main_LP/main.py +437 -0
- rootlp/modules/main_LP/test_main.py +74 -0
- rootlp/modules/menu_LP/__init__.py +0 -0
- rootlp/modules/menu_LP/menu.py +78 -0
- rootlp/modules/menu_LP/test_menu.py +79 -0
- rootlp/modules/mo_LP/__init__.py +0 -0
- rootlp/modules/mo_LP/mo.py +23 -0
- rootlp/modules/mo_LP/test_mo.py +79 -0
- rootlp/modules/print_LP/__init__.py +0 -0
- rootlp/modules/print_LP/print.py +337 -0
- rootlp/modules/print_LP/test_print.py +109 -0
- rootlp/modules/project_server_LP/__init__.py +0 -0
- rootlp/modules/project_server_LP/project_server.py +86 -0
- rootlp/modules/project_server_LP/test_project_server.py +79 -0
- rootlp/modules/readme_string_LP/__init__.py +0 -0
- rootlp/modules/readme_string_LP/readme_string.py +53 -0
- rootlp/modules/readme_string_LP/test_readme_string.py +79 -0
- rootlp/modules/user_inputs_LP/__init__.py +0 -0
- rootlp/modules/user_inputs_LP/test_user_inputs.py +46 -0
- rootlp/modules/user_inputs_LP/user_inputs.py +103 -0
- rootlp/modules.json +56 -0
- rootlp/py.typed +0 -0
- rootlp/scripts/__init__.py +0 -0
- rootlp/scripts.json +1 -0
- rootlp-0.1.7.dist-info/METADATA +41 -0
- rootlp-0.1.7.dist-info/RECORD +36 -0
- rootlp-0.1.7.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Date : 2026-01-18
|
|
4
|
+
# Author : Lancelot PINCET
|
|
5
|
+
# GitHub : https://github.com/LancelotPincet
|
|
6
|
+
# Library : rootLP
|
|
7
|
+
# Module : mo
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
This file allows to test mo
|
|
11
|
+
|
|
12
|
+
mo : Marimo library shortcut.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# %% Libraries
|
|
18
|
+
from corelp import print, debug
|
|
19
|
+
import pytest
|
|
20
|
+
from rootlp import mo
|
|
21
|
+
debug_folder = debug(__file__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# %% Function test
|
|
26
|
+
def test_function() :
|
|
27
|
+
'''
|
|
28
|
+
Test mo function
|
|
29
|
+
'''
|
|
30
|
+
print('Hello world!')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# %% Instance fixture
|
|
35
|
+
@pytest.fixture()
|
|
36
|
+
def instance() :
|
|
37
|
+
'''
|
|
38
|
+
Create a new instance at each test function
|
|
39
|
+
'''
|
|
40
|
+
return mo()
|
|
41
|
+
|
|
42
|
+
def test_instance(instance) :
|
|
43
|
+
'''
|
|
44
|
+
Test on fixture
|
|
45
|
+
'''
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# %% Returns test
|
|
50
|
+
@pytest.mark.parametrize("args, kwargs, expected, message", [
|
|
51
|
+
#([], {}, None, ""),
|
|
52
|
+
([], {}, None, ""),
|
|
53
|
+
])
|
|
54
|
+
def test_returns(args, kwargs, expected, message) :
|
|
55
|
+
'''
|
|
56
|
+
Test mo return values
|
|
57
|
+
'''
|
|
58
|
+
assert mo(*args, **kwargs) == expected, message
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# %% Error test
|
|
63
|
+
@pytest.mark.parametrize("args, kwargs, error, error_message", [
|
|
64
|
+
#([], {}, None, ""),
|
|
65
|
+
([], {}, None, ""),
|
|
66
|
+
])
|
|
67
|
+
def test_errors(args, kwargs, error, error_message) :
|
|
68
|
+
'''
|
|
69
|
+
Test mo error values
|
|
70
|
+
'''
|
|
71
|
+
with pytest.raises(error, match=error_message) :
|
|
72
|
+
mo(*args, **kwargs)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# %% Test function run
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
from corelp import test
|
|
79
|
+
test(__file__)
|
|
File without changes
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Date : 2025-08-27
|
|
4
|
+
# Author : Lancelot PINCET
|
|
5
|
+
# GitHub : https://github.com/LancelotPincet
|
|
6
|
+
# Library : coreLP
|
|
7
|
+
# Module : print
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
This function overrides python built in print function to add functionnalities.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# %% Libraries
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# %% Libraries
|
|
20
|
+
from corelp import prop
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from datetime import datetime, timedelta
|
|
23
|
+
from rich import print as richprint
|
|
24
|
+
from rich.console import Console
|
|
25
|
+
from rich.theme import Theme
|
|
26
|
+
from rich.markdown import Markdown
|
|
27
|
+
from rich.traceback import Traceback
|
|
28
|
+
from rich.progress import (
|
|
29
|
+
Progress,
|
|
30
|
+
BarColumn,
|
|
31
|
+
TextColumn,
|
|
32
|
+
TaskProgressColumn,
|
|
33
|
+
TimeElapsedColumn,
|
|
34
|
+
TimeRemainingColumn,
|
|
35
|
+
ProgressColumn,
|
|
36
|
+
)
|
|
37
|
+
import traceback as tb_module
|
|
38
|
+
from time import perf_counter
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
import marimo as mo
|
|
41
|
+
pyprint = print
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# %% Class
|
|
46
|
+
@dataclass(slots=True, kw_only=True)
|
|
47
|
+
class Print() :
|
|
48
|
+
r"""
|
|
49
|
+
Enhanced replacement for the built-in :func:`print` function, adding muting,
|
|
50
|
+
logging, rich formatting, and progress utilities.
|
|
51
|
+
|
|
52
|
+
This class is callable and behaves like :func:`print`, with extra arguments.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
*strings : tuple
|
|
57
|
+
The objects to print. Its :meth:`__str__` representation is used.
|
|
58
|
+
verbose : bool, optional
|
|
59
|
+
If ``True`` (default), printing is performed.
|
|
60
|
+
If ``False``, printing is skipped unless overridden.
|
|
61
|
+
return_string : bool, optional
|
|
62
|
+
If ``True``, return the processed string instead of ``None``.
|
|
63
|
+
file : str or pathlib.Path or None, optional
|
|
64
|
+
If provided, overrides the configured log file.
|
|
65
|
+
mode : {"w", "a"}, optional
|
|
66
|
+
File mode used when writing logs. Default is ``"a"``.
|
|
67
|
+
end : str, optional
|
|
68
|
+
End-of-line character(s). Defaults to ``"\n"``.
|
|
69
|
+
**kwargs :
|
|
70
|
+
Additional keyword arguments passed to :func:`print` or Rich's :func:`Console.print`.
|
|
71
|
+
|
|
72
|
+
Examples
|
|
73
|
+
--------
|
|
74
|
+
Basic usage::
|
|
75
|
+
|
|
76
|
+
>>> from corelp import print
|
|
77
|
+
>>> s = "Hello *world*!\nThis is a print **example**"
|
|
78
|
+
>>> print(s)
|
|
79
|
+
|
|
80
|
+
Muting::
|
|
81
|
+
|
|
82
|
+
>>> print.verbose = False
|
|
83
|
+
>>> print(s) # muted
|
|
84
|
+
>>> print(s, verbose=True) # forced printing
|
|
85
|
+
>>> print.verbose = True
|
|
86
|
+
>>> print(s) # prints again
|
|
87
|
+
>>> print(s, verbose=False) # forced mute
|
|
88
|
+
|
|
89
|
+
Access to underlying print functions::
|
|
90
|
+
|
|
91
|
+
>>> print.pyprint(s) # built-in print
|
|
92
|
+
>>> print.richprint(s) # rich.print
|
|
93
|
+
>>> print.print(s) # Console.print
|
|
94
|
+
>>> print.log(s) # Console.log
|
|
95
|
+
|
|
96
|
+
Logging::
|
|
97
|
+
|
|
98
|
+
>>> print.file = "log.txt"
|
|
99
|
+
>>> print("Hello") # also writes to file
|
|
100
|
+
|
|
101
|
+
Console styling::
|
|
102
|
+
|
|
103
|
+
>>> print.theme = {"success": "green"}
|
|
104
|
+
>>> print("Done!", style="success")
|
|
105
|
+
>>> try:
|
|
106
|
+
... 1/0
|
|
107
|
+
... except Exception:
|
|
108
|
+
... print.error()
|
|
109
|
+
>>> print.export_html("log.html")
|
|
110
|
+
|
|
111
|
+
Progress / Clock::
|
|
112
|
+
|
|
113
|
+
>>> from time import sleep
|
|
114
|
+
>>> for i in print.clock(15, "Outer"):
|
|
115
|
+
... for j in print.clock(10, "Inner"):
|
|
116
|
+
... sleep(1)
|
|
117
|
+
|
|
118
|
+
Attributes
|
|
119
|
+
----------
|
|
120
|
+
verbose : bool
|
|
121
|
+
Global muting switch.
|
|
122
|
+
pyprint : callable
|
|
123
|
+
Built-in Python :func:`print`.
|
|
124
|
+
richprint : callable
|
|
125
|
+
:mod:`rich` print function.
|
|
126
|
+
console : rich.console.Console
|
|
127
|
+
The Rich console instance used for styled printing.
|
|
128
|
+
file : pathlib.Path or None
|
|
129
|
+
Path to the log file.
|
|
130
|
+
progress : rich.progress.Progress
|
|
131
|
+
Active Rich progress manager.
|
|
132
|
+
bars : dict
|
|
133
|
+
Dictionary storing active progress bars.
|
|
134
|
+
theme : dict
|
|
135
|
+
Custom Rich style definitions.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
# Main function
|
|
139
|
+
def __call__(self, *strings, verbose=None, do_stdout=True, do_file=True, return_string=False, file=None, mode='a', end='\n', **kwargs) :
|
|
140
|
+
|
|
141
|
+
# Muting
|
|
142
|
+
verbose = verbose if verbose is not None else self.verbose
|
|
143
|
+
if not verbose :
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
# Formatting string
|
|
147
|
+
string = ", ".join([str(string) for string in strings]) + end
|
|
148
|
+
|
|
149
|
+
# Printing markdown
|
|
150
|
+
if do_stdout :
|
|
151
|
+
string2print = Markdown(string) if self.buffer is None else string
|
|
152
|
+
self.print(string2print, **kwargs)
|
|
153
|
+
|
|
154
|
+
# Writting to file
|
|
155
|
+
if do_file :
|
|
156
|
+
file = file if file is not None else self.file
|
|
157
|
+
if file is not None :
|
|
158
|
+
with open(Path(file), mode) as file :
|
|
159
|
+
file.write(string)
|
|
160
|
+
|
|
161
|
+
# Refresh buffer
|
|
162
|
+
if self.buffer is not None :
|
|
163
|
+
mo.output.replace(mo.md(self.buffer.getvalue()))
|
|
164
|
+
|
|
165
|
+
# Return
|
|
166
|
+
if return_string :
|
|
167
|
+
return string
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# MUTING
|
|
171
|
+
verbose : bool = True # True to print
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# PRINT
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def print(self) :
|
|
179
|
+
return self.console.print
|
|
180
|
+
@property
|
|
181
|
+
def log(self) :
|
|
182
|
+
return self.console.log
|
|
183
|
+
pyprint = pyprint # python print
|
|
184
|
+
richprint = richprint # rich prints
|
|
185
|
+
buffer : object = field(default=None, repr=False) # Marimo buffer where to print
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# LOGGING
|
|
190
|
+
|
|
191
|
+
_file : Path = None
|
|
192
|
+
@property
|
|
193
|
+
def file(self) :
|
|
194
|
+
return self._file
|
|
195
|
+
@file.setter
|
|
196
|
+
def file(self, value) :
|
|
197
|
+
self._file = Path(value)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# CONSOLE
|
|
202
|
+
|
|
203
|
+
_theme = {}
|
|
204
|
+
@property
|
|
205
|
+
def theme(self) :
|
|
206
|
+
return self._theme
|
|
207
|
+
@theme.setter
|
|
208
|
+
def theme(self, value) :
|
|
209
|
+
self._theme.update(value)
|
|
210
|
+
self._console = None
|
|
211
|
+
|
|
212
|
+
_console : Console = field(default=None, repr=False)
|
|
213
|
+
@prop(cache=True)
|
|
214
|
+
def console(self) :
|
|
215
|
+
theme = Theme(self.theme)
|
|
216
|
+
return Console(theme=theme, record=True)
|
|
217
|
+
|
|
218
|
+
def error(self) :
|
|
219
|
+
rich_tb = Traceback.from_exception(*tb_module.sys.exc_info())
|
|
220
|
+
self.console.print(rich_tb)
|
|
221
|
+
|
|
222
|
+
def print_locals(self) :
|
|
223
|
+
self.console.log(log_locals=True)
|
|
224
|
+
|
|
225
|
+
def export_html(self, path) :
|
|
226
|
+
if path is None :
|
|
227
|
+
return
|
|
228
|
+
path = Path(path)
|
|
229
|
+
html_content = self.console.export_html(inline_styles=True)
|
|
230
|
+
with open(path, "w", encoding="utf-8") as file:
|
|
231
|
+
file.write(html_content)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# CLOCK
|
|
236
|
+
|
|
237
|
+
def clock(self, iterable, title="Working...") :
|
|
238
|
+
|
|
239
|
+
# Get iterable
|
|
240
|
+
iterable = range(iterable) if isinstance(iterable, int) else iterable
|
|
241
|
+
iterable = list(iterable)
|
|
242
|
+
|
|
243
|
+
# Marimo
|
|
244
|
+
if self.buffer is not None :
|
|
245
|
+
verbose, self.verbose = self.verbose, False
|
|
246
|
+
for item in mo.status.progress_bar(iterable, title=title, show_rate=True, show_eta=True, remove_on_exit=True) :
|
|
247
|
+
yield item
|
|
248
|
+
self.verbose = verbose
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
# Detect if progressbar already exists
|
|
252
|
+
first_bar = getattr(self, "_progress", None) is None
|
|
253
|
+
progress = self.progress
|
|
254
|
+
bars = self.bars
|
|
255
|
+
|
|
256
|
+
# Opens progress
|
|
257
|
+
if first_bar :
|
|
258
|
+
verbose, self.verbose = self.verbose, False
|
|
259
|
+
|
|
260
|
+
# Write to file
|
|
261
|
+
if self.file is not None :
|
|
262
|
+
with open(Path(self.file), "a") as file :
|
|
263
|
+
file.write(f'{title}...\n')
|
|
264
|
+
progress.start()
|
|
265
|
+
|
|
266
|
+
# Create new task
|
|
267
|
+
task = bars.get(title, None)
|
|
268
|
+
if task is None : # No bar with this name exists
|
|
269
|
+
task = progress.add_task(title, total=len(iterable), avg_time=0.0)
|
|
270
|
+
bars[title] = task # store it
|
|
271
|
+
else :
|
|
272
|
+
progress.reset(task)
|
|
273
|
+
|
|
274
|
+
# Loop
|
|
275
|
+
loop_counter = 0
|
|
276
|
+
start = perf_counter()
|
|
277
|
+
for item in iterable :
|
|
278
|
+
yield item
|
|
279
|
+
loop_counter += 1
|
|
280
|
+
elapsed = perf_counter() - start
|
|
281
|
+
avg_time = elapsed / loop_counter if loop_counter else 0
|
|
282
|
+
progress.update(task, advance=1, avg_time=avg_time)
|
|
283
|
+
|
|
284
|
+
# Clean up
|
|
285
|
+
if first_bar :
|
|
286
|
+
progress.stop()
|
|
287
|
+
del(self.bars)
|
|
288
|
+
del(self.progress)
|
|
289
|
+
self.verbose = verbose
|
|
290
|
+
|
|
291
|
+
_progress : Progress = field(default=None, repr=False)
|
|
292
|
+
@prop(cache=True)
|
|
293
|
+
def progress(self) :
|
|
294
|
+
return Progress(
|
|
295
|
+
TextColumn("{task.description}"),
|
|
296
|
+
BarColumn(),
|
|
297
|
+
TaskProgressColumn(),
|
|
298
|
+
TextColumn("[magenta]/{task.total}[/]"),
|
|
299
|
+
TimeElapsedColumn(),
|
|
300
|
+
AvgLoopTimeColumn(),
|
|
301
|
+
TimeRemainingColumn(),
|
|
302
|
+
EndTimeColumn(),
|
|
303
|
+
transient=False,
|
|
304
|
+
console=self.console
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
_bars : dict = field(default=None, repr=False)
|
|
308
|
+
@prop(cache=True)
|
|
309
|
+
def bars(self) :
|
|
310
|
+
return {}
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# Get instance
|
|
315
|
+
print = Print() # Instance to use everywhere
|
|
316
|
+
|
|
317
|
+
# Custom Progress bar columns
|
|
318
|
+
class AvgLoopTimeColumn(ProgressColumn):
|
|
319
|
+
def render(self, task):
|
|
320
|
+
avg_time = task.fields.get("avg_time", None)
|
|
321
|
+
if avg_time is not None and task.completed > 0:
|
|
322
|
+
string = f"[yellow]↻ {avg_time:.2f}s[/]" if avg_time > 1 else f"[yellow]↻ {avg_time*1000:.2f}ms[/]"
|
|
323
|
+
return string
|
|
324
|
+
return ""
|
|
325
|
+
class EndTimeColumn(ProgressColumn):
|
|
326
|
+
def render(self, task):
|
|
327
|
+
if task.time_remaining is not None:
|
|
328
|
+
end_time = datetime.now() + timedelta(seconds=task.time_remaining)
|
|
329
|
+
return f"[cyan]{end_time:%m-%d %H:%M:%S}[/] "
|
|
330
|
+
return ""
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# %% Test function run
|
|
335
|
+
if __name__ == "__main__":
|
|
336
|
+
from corelp import test
|
|
337
|
+
test(__file__)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Date : 2025-08-27
|
|
4
|
+
# Author : Lancelot PINCET
|
|
5
|
+
# GitHub : https://github.com/LancelotPincet
|
|
6
|
+
# Library : coreLP
|
|
7
|
+
# Module : print
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
This file allows to test print
|
|
11
|
+
|
|
12
|
+
print : This function overrides python built in print function to add functionnalities.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# %% Libraries
|
|
18
|
+
from rootlp import print
|
|
19
|
+
from corelp import debug
|
|
20
|
+
import pytest
|
|
21
|
+
from time import sleep
|
|
22
|
+
debug_folder = debug(__file__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# %% test prints
|
|
26
|
+
def test_print() :
|
|
27
|
+
'''
|
|
28
|
+
Test print
|
|
29
|
+
'''
|
|
30
|
+
string = "# TEST\nHello *world*!\n\nThis is 1 print **example**"
|
|
31
|
+
print(string, style="magenta")
|
|
32
|
+
print.print(string, style="magenta")
|
|
33
|
+
print.log(string, style="magenta")
|
|
34
|
+
print.pyprint(string)
|
|
35
|
+
print.richprint(string)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# %% test verbose
|
|
40
|
+
def test_verbose() :
|
|
41
|
+
'''
|
|
42
|
+
Test verbose
|
|
43
|
+
'''
|
|
44
|
+
print.verbose = False # Muting
|
|
45
|
+
print("Should not print") # Does not print
|
|
46
|
+
print("Should print", verbose=True) # Does print
|
|
47
|
+
print("Should not print") # Does not print
|
|
48
|
+
print.verbose = True # Unmuting
|
|
49
|
+
print("Should print") # Does print
|
|
50
|
+
print("Should not print", verbose=False) # Does not print
|
|
51
|
+
print("Should print") # Does print
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# %% test logging
|
|
56
|
+
def test_logging() :
|
|
57
|
+
'''
|
|
58
|
+
Test logging
|
|
59
|
+
'''
|
|
60
|
+
print.theme = {"success" : "green"}
|
|
61
|
+
string = "# TEST\nHello *world*!\n\nThis is 1 print **example**"
|
|
62
|
+
print(string, style="success")
|
|
63
|
+
print.print_locals()
|
|
64
|
+
try :
|
|
65
|
+
1/0
|
|
66
|
+
except Exception :
|
|
67
|
+
print.error()
|
|
68
|
+
file = debug_folder / "log.html"
|
|
69
|
+
print.export_html(file)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# %% test console
|
|
74
|
+
def test_console() :
|
|
75
|
+
'''
|
|
76
|
+
Test console
|
|
77
|
+
'''
|
|
78
|
+
file = debug_folder / 'log.md'
|
|
79
|
+
print.file = file
|
|
80
|
+
string = "# TEST\nHello *world*!\n\nThis is 1 print **example**"
|
|
81
|
+
print(string, style="magenta")
|
|
82
|
+
assert file.exists()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# %% test clock
|
|
87
|
+
def test_clock() :
|
|
88
|
+
'''
|
|
89
|
+
Test clock
|
|
90
|
+
'''
|
|
91
|
+
for i in print.clock(5, "Outer loop") :
|
|
92
|
+
print("Should not print")
|
|
93
|
+
for j in print.clock(5, "Inner loop") :
|
|
94
|
+
sleep(1)
|
|
95
|
+
print("Should not print")
|
|
96
|
+
|
|
97
|
+
for i in print.clock(10, "Other loop") :
|
|
98
|
+
sleep(1)
|
|
99
|
+
print("Should not print")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# %% Test function run
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
from corelp import test
|
|
109
|
+
test(__file__)
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Date : 2026-01-18
|
|
4
|
+
# Author : Lancelot PINCET
|
|
5
|
+
# GitHub : https://github.com/LancelotPincet
|
|
6
|
+
# Library : rootLP
|
|
7
|
+
# Module : project_server
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
This module creates and run a custom marimo server for the project.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# %% Libraries
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
import marimo as mo
|
|
18
|
+
from fastapi import FastAPI
|
|
19
|
+
from fastapi.responses import RedirectResponse
|
|
20
|
+
import uvicorn
|
|
21
|
+
import webbrowser
|
|
22
|
+
import threading
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# %% Function
|
|
27
|
+
def project_server(file) :
|
|
28
|
+
'''
|
|
29
|
+
This module creates and run a custom marimo server for the project.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
file : str
|
|
34
|
+
String with the main file path.
|
|
35
|
+
|
|
36
|
+
Examples
|
|
37
|
+
--------
|
|
38
|
+
>>> from rootlp import project_server
|
|
39
|
+
...
|
|
40
|
+
>>> project_server(__file__)
|
|
41
|
+
'''
|
|
42
|
+
|
|
43
|
+
# Folder
|
|
44
|
+
folder = Path(file).parent # folder / main.py
|
|
45
|
+
name = folder.name
|
|
46
|
+
|
|
47
|
+
# Notebooks
|
|
48
|
+
pages = [notebook.stem for notebook in (folder / 'pages').iterdir() if not notebook.name.startswith('__')]
|
|
49
|
+
scripts = [notebook.stem for notebook in (folder / 'scripts').iterdir() if not notebook.name.startswith('__')]
|
|
50
|
+
figures = [notebook.stem for notebook in (folder / 'figures').iterdir() if not notebook.name.startswith('__')]
|
|
51
|
+
|
|
52
|
+
# Create a marimo asgi app
|
|
53
|
+
server = mo.create_asgi_app()
|
|
54
|
+
server = server.with_app(path=f"/", root=folder/"pages/home.py")
|
|
55
|
+
for page in pages :
|
|
56
|
+
server = server.with_app(path=f"/{page}", root=folder/f"pages/{page}.py")
|
|
57
|
+
for script in scripts :
|
|
58
|
+
server = server.with_app(path=f"/{script}", root=folder/f"scripts/{script}.py")
|
|
59
|
+
for figure in figures :
|
|
60
|
+
server = server.with_app(path=f"/{figure}", root=folder/f"figures/{figure}.py")
|
|
61
|
+
|
|
62
|
+
# Create and configure FastAPI
|
|
63
|
+
app = FastAPI()
|
|
64
|
+
app.mount(f"/{name}", server.build())
|
|
65
|
+
|
|
66
|
+
# Prints
|
|
67
|
+
url = f'http://localhost:9797/{name}/'
|
|
68
|
+
print("\n" + "*" * (len(name) + 4))
|
|
69
|
+
print(f"| {name.upper()} |")
|
|
70
|
+
print("*" * (len(name) + 4) + "\n")
|
|
71
|
+
print(f"Ctrl+Click in terminal or copy-paste the following URL in browser:\n")
|
|
72
|
+
print(f" ➜ {url} \n")
|
|
73
|
+
print(f"📌 Do not close this terminal window until you finished with {name}!")
|
|
74
|
+
|
|
75
|
+
# Open browser in a separate thread (so it doesn't block the server)
|
|
76
|
+
threading.Timer(1.0, lambda: webbrowser.open(url)).start()
|
|
77
|
+
|
|
78
|
+
# Run the server
|
|
79
|
+
uvicorn.run(app, host="localhost", port=9797, log_level="critical")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# %% Test function run
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
from corelp import test
|
|
86
|
+
test(__file__)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Date : 2026-01-18
|
|
4
|
+
# Author : Lancelot PINCET
|
|
5
|
+
# GitHub : https://github.com/LancelotPincet
|
|
6
|
+
# Library : rootLP
|
|
7
|
+
# Module : project_server
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
This file allows to test project_server
|
|
11
|
+
|
|
12
|
+
project_server : This module creates and run a custom marimo server for the project.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# %% Libraries
|
|
18
|
+
from corelp import print, debug
|
|
19
|
+
import pytest
|
|
20
|
+
from rootlp import project_server
|
|
21
|
+
debug_folder = debug(__file__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# %% Function test
|
|
26
|
+
def test_function() :
|
|
27
|
+
'''
|
|
28
|
+
Test project_server function
|
|
29
|
+
'''
|
|
30
|
+
print('Hello world!')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# %% Instance fixture
|
|
35
|
+
@pytest.fixture()
|
|
36
|
+
def instance() :
|
|
37
|
+
'''
|
|
38
|
+
Create a new instance at each test function
|
|
39
|
+
'''
|
|
40
|
+
return project_server()
|
|
41
|
+
|
|
42
|
+
def test_instance(instance) :
|
|
43
|
+
'''
|
|
44
|
+
Test on fixture
|
|
45
|
+
'''
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# %% Returns test
|
|
50
|
+
@pytest.mark.parametrize("args, kwargs, expected, message", [
|
|
51
|
+
#([], {}, None, ""),
|
|
52
|
+
([], {}, None, ""),
|
|
53
|
+
])
|
|
54
|
+
def test_returns(args, kwargs, expected, message) :
|
|
55
|
+
'''
|
|
56
|
+
Test project_server return values
|
|
57
|
+
'''
|
|
58
|
+
assert project_server(*args, **kwargs) == expected, message
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# %% Error test
|
|
63
|
+
@pytest.mark.parametrize("args, kwargs, error, error_message", [
|
|
64
|
+
#([], {}, None, ""),
|
|
65
|
+
([], {}, None, ""),
|
|
66
|
+
])
|
|
67
|
+
def test_errors(args, kwargs, error, error_message) :
|
|
68
|
+
'''
|
|
69
|
+
Test project_server error values
|
|
70
|
+
'''
|
|
71
|
+
with pytest.raises(error, match=error_message) :
|
|
72
|
+
project_server(*args, **kwargs)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# %% Test function run
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
from corelp import test
|
|
79
|
+
test(__file__)
|
|
File without changes
|