corelp 1.0.37__py3-none-any.whl → 1.0.38__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.
@@ -15,7 +15,7 @@ Path : This function is a wrapper around the pathlib.Path and returns a compatib
15
15
 
16
16
 
17
17
  # %% Libraries
18
- from corelp import print, debug
18
+ from corelp import debug
19
19
  import pytest
20
20
  from corelp import Path
21
21
  debug_folder = debug(__file__)
@@ -15,7 +15,7 @@ rfrom : This function allows to do a relative import that works in module and sc
15
15
 
16
16
 
17
17
  # %% Libraries
18
- from corelp import print, debug
18
+ from corelp import debug
19
19
  import pytest
20
20
  from corelp import rfrom
21
21
  debug_folder = debug(__file__)
corelp/modules.json CHANGED
@@ -5,12 +5,6 @@
5
5
  "module": "modules/Path_LP/Path",
6
6
  "object": "Path"
7
7
  },
8
- "Section": {
9
- "date": "2025-08-28",
10
- "description": "This class defines decorator instances allowing to create section functions.",
11
- "module": "modules/Section_LP/Section",
12
- "object": "Section"
13
- },
14
8
  "debug": {
15
9
  "date": "2025-08-25",
16
10
  "description": "This function will give and create the debug folder for a given python file.",
@@ -35,18 +29,6 @@
35
29
  "module": "modules/kwargsself_LP/kwargsself",
36
30
  "object": "kwargsself"
37
31
  },
38
- "main": {
39
- "date": "2025-08-28",
40
- "description": "This function can decorate the main function of a script.",
41
- "module": "modules/main_LP/main",
42
- "object": "main"
43
- },
44
- "print": {
45
- "date": "2025-08-27",
46
- "description": "This function overrides python built in print function to add functionnalities.",
47
- "module": "modules/print_LP/print",
48
- "object": "print"
49
- },
50
32
  "prop": {
51
33
  "date": "2025-08-25",
52
34
  "description": "This function serves as an improved property decorator.",
@@ -70,11 +52,5 @@
70
52
  "description": "This function will launch the testfile for the current file using pytest library.",
71
53
  "module": "modules/test_LP/test",
72
54
  "object": "test"
73
- },
74
- "user_inputs": {
75
- "date": "2025-11-30",
76
- "description": "Gets last user inputs dictionnary from global variables.",
77
- "module": "modules/user_inputs_LP/user_inputs",
78
- "object": "user_inputs"
79
55
  }
80
56
  }
@@ -1,13 +1,8 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: corelp
3
- Version: 1.0.37
3
+ Version: 1.0.38
4
4
  Summary: A library that gathers core functions for python programming.
5
- Requires-Dist: joblib ; extra == 'script'
6
- Requires-Dist: rich ; extra == 'script'
7
- Requires-Dist: marimo ; extra == 'script'
8
5
  Requires-Python: >=3.12
9
- Provides-Extra: lib
10
- Provides-Extra: script
11
6
  Description-Content-Type: text/markdown
12
7
 
13
8
  # coreLP
@@ -2,10 +2,7 @@ corelp/__init__.py,sha256=JMGz_b8Xz6heIsLVRtZpfpZ1DMF_Vsi-Xw8l7pnqy6c,684
2
2
  corelp/icon_pythonLP.png,sha256=pg386kYEKGspDDRbRF0alOmHXPEm0geMoYIPaxEfQ_o,3805
3
3
  corelp/modules/Path_LP/Path.py,sha256=wfvO1DuewIUSEA9_g66O5nCEp95Pr5XM-yxaOOEimTs,2418
4
4
  corelp/modules/Path_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- corelp/modules/Path_LP/test_Path.py,sha256=8VSn9VVbhc3EDxXpxQV18rnAq_T2a1uuoRFKdmsqX-I,715
6
- corelp/modules/Section_LP/Section.py,sha256=0uqvqzuBd5qSoOBqqpx4mN7SZERMCOdZfAEMBsQL004,6098
7
- corelp/modules/Section_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- corelp/modules/Section_LP/test_Section.py,sha256=zcrIbJueMDT90t0_M-RD40IWpkx4OT7Q52tE6HzbfIo,812
5
+ corelp/modules/Path_LP/test_Path.py,sha256=E4m0n4aEaS0_KCNNpOxqnCQpiapHv3uAIaJSWwXpSY0,708
9
6
  corelp/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
7
  corelp/modules/debug_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
8
  corelp/modules/debug_LP/debug.py,sha256=Mkn3WPTkaKSXdfmY8ioEwu4OXQqiDoM2Akhbz62Nnis,1623
@@ -18,32 +15,23 @@ corelp/modules/getmodule_LP/getmodule.py,sha256=7I4ehsBtgfi-wUyiQ5eT07lc8FiUVN7F
18
15
  corelp/modules/kwargsself_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
16
  corelp/modules/kwargsself_LP/kwargsself.py,sha256=24HVn1G7ALdhQFSLT0U2dVjVzop6c2s-nr7DrMK53iw,1865
20
17
  corelp/modules/kwargsself_LP/test_kwargsself.py,sha256=BaqGu0bqFuFemiYUEDMviFfegTb0aF6lmeFm3KJa8sw,1241
21
- corelp/modules/main_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- corelp/modules/main_LP/main.py,sha256=nbb35whCWI1fnjqCr_3QI1qDdEAVO8DHts39HnrguwY,10063
23
- corelp/modules/main_LP/test_main.py,sha256=mxL645pZdkJ8J5MFdj0K-z8xRCGKQ0zw1NvmW3iPGB4,1741
24
- corelp/modules/print_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- corelp/modules/print_LP/print.py,sha256=TXnxPc4bHf6zSaQkHJ-3iQNkERCG7hvb-tmCAgp94r0,8980
26
- corelp/modules/print_LP/test_print.py,sha256=uMmCrnyIVSlZg_ZEOk2mr5zDlEpTN2KMq4jFu5p6n4I,2292
27
18
  corelp/modules/prop_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
19
  corelp/modules/prop_LP/prop.py,sha256=lfsENmjg65mpXxt9V9n3Nn__wHgNJA61l8_UXBKn1lc,3999
29
20
  corelp/modules/prop_LP/test_prop.py,sha256=X5GJwIyik5Gzd6Z28h1kGdZzVVhb1D5FXTHXvXBbwfA,2302
30
21
  corelp/modules/rfrom_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
22
  corelp/modules/rfrom_LP/rfrom.py,sha256=f5-zPUUxlxInnaBM_WMuAaSMjdaoyOjsn5WbpLIfSVk,1690
32
- corelp/modules/rfrom_LP/test_rfrom.py,sha256=e6Q-PmvsNT7CrZOjrjW_-xrLDYyfqfTkLwC6y0lhz9Q,673
23
+ corelp/modules/rfrom_LP/test_rfrom.py,sha256=Sq9FeWnzpItiT8RGh6CzCdMnoZm4e2-dbkTs6dW2XPY,666
33
24
  corelp/modules/selfkwargs_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
25
  corelp/modules/selfkwargs_LP/selfkwargs.py,sha256=7yWkpVQRwb2VnDPUGxREXjyVIn0n8RpptlP3lnP57wU,1203
35
26
  corelp/modules/selfkwargs_LP/test_selfkwargs.py,sha256=Ex-y60W9oD1OUtDlRvyxn-Wlq1l3JmIJY0Iot2uGQbY,826
36
27
  corelp/modules/test_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
28
  corelp/modules/test_LP/test.py,sha256=KwAeIZWQ7pZX85J50q6qfB4HuJUgREu1ieB_byPqI2g,1361
38
29
  corelp/modules/test_LP/test_test.py,sha256=sXI6G4OBnWXSqrfMy9QdabkYIvcD80EmnNcnQiXxcRI,605
39
- corelp/modules/user_inputs_LP/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- corelp/modules/user_inputs_LP/test_user_inputs.py,sha256=-6YzBomJxIZSfnfbJPvnfgon3K5RTC3WQKNT4enpVH0,947
41
- corelp/modules/user_inputs_LP/user_inputs.py,sha256=ochRTY892XwYANBRyP-kwR_-7S96AVF4fIhfe6P4t1c,2472
42
- corelp/modules.json,sha256=ZQ8BMU8X2u71wfVj_AufwOoJUPdaxU_AHn7Ohbkp-Pc,3122
30
+ corelp/modules.json,sha256=3zREFn0--UhICcQJQG8syet8Ni1HdsG_pN0K86SQiXo,2230
43
31
  corelp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
32
  corelp/pythonLP.png:Zone.Identifier,sha256=uVLSTPcBvE56fSHhroXGEYJOgFeaiYNsDLSbGTRGzP4,25
45
33
  corelp/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
34
  corelp/scripts.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
47
- corelp-1.0.37.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
48
- corelp-1.0.37.dist-info/METADATA,sha256=ML8PH0xON63YuMqExPNOgloYPM3jESPiLiaBiS8OWoQ,1823
49
- corelp-1.0.37.dist-info/RECORD,,
35
+ corelp-1.0.38.dist-info/WHEEL,sha256=eycQt0QpYmJMLKpE3X9iDk8R04v2ZF0x82ogq-zP6bQ,79
36
+ corelp-1.0.38.dist-info/METADATA,sha256=I0RpZ1oe4qOcO5R7WAHrjHSYNmmqKVbhFEBy7bgufpA,1656
37
+ corelp-1.0.38.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.8.24
2
+ Generator: uv 0.9.24
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,176 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # Date : 2025-08-28
4
- # Author : Lancelot PINCET
5
- # GitHub : https://github.com/LancelotPincet
6
- # Library : coreLP
7
- # Module : Section
8
-
9
- """
10
- This class defines decorator instances allowing to create section functions.
11
- """
12
-
13
-
14
-
15
- # %% Libraries
16
- from corelp import print, folder, selfkwargs, kwargsself
17
- from dataclasses import dataclass
18
- import pickle
19
- import joblib
20
- from pathlib import Path
21
- from functools import wraps
22
- import hashlib
23
- import inspect
24
- import os
25
-
26
-
27
-
28
- # %% Class
29
- @dataclass(slots=True, kw_only=True)
30
- class Section() :
31
- '''
32
- This class defines decorator instances allowing to create section functions.
33
- Cache results into a folder, if another call occurs, can load-back the precalculated data.
34
-
35
- Parameters
36
- ----------
37
- path : str or Path
38
- path where to save section folder results.
39
- new : bool
40
- True to ignore pre-calculated data and crush them.
41
- num : int
42
- Index of section, after one call, adds 1 for next call.
43
- parent_path : str or Path
44
- Path to the parent folder if bulk processing.
45
-
46
- Examples
47
- --------
48
- >>> from corelp import Section
49
- ...
50
- >>> section = Section(path=export_path)
51
- ...
52
- >>> @section()
53
- ... def add(a, b=0) :
54
- ... testfunc.print('Hello World')
55
- ... return a + b
56
- ...
57
- >>> testfunc.print('3+0=', add(3)) # First call calculates and save result
58
- >>> testfunc.print('3+0=', add(3, 0)) # Second call loads back precalculated results
59
- >>> testfunc.print('1+3=', add(1, 3)) # New call with other parameters : crushed previous results with new ones
60
- >>> testfunc.print('1+3=', add(1, b=3)) # Second call with these parameters : loads precalculated results
61
- ...
62
- >>> @section(cache=False) # Creates an index of 2, does no caching
63
- ... def sub(a, b=0) :
64
- ... return a - b
65
- ...
66
- >>> @section(num=10) # Creates an index of 10
67
- ... def mul(a, b=0) :
68
- ... return a * b
69
- ...
70
- >>> @section(new=True) # Creates an index of 11, always creates new cache
71
- ... def div(a, b) :
72
- ... return a / b
73
- '''
74
-
75
- # Attributes
76
- path : Path | str = None
77
- new :bool = False
78
- num :int = 0
79
- parent_path : Path | str = None
80
-
81
- # Init
82
- def __post_init__(self) :
83
- if self.path is not None :
84
- self.path = Path(self.path)
85
- if self.parent_path is not None :
86
- self.parent_path = Path(self.parent_path)
87
-
88
- # Decorator
89
- def __call__(self, *, new=None, num=None, symlink=None, cache=True):
90
- if new is None :
91
- new = self.new
92
- if num is None :
93
- num = self.num
94
- self.num = num+1
95
-
96
- def decorator(func) :
97
- name = func.__name__
98
-
99
- @wraps(func)
100
- def wrapper(*args, **kwargs):
101
- wrapper.path = self.path / f"{num:03}_{name}"
102
- print(f'\n#### **{num}. {name.replace("_"," ")} section**\n')
103
-
104
- # Creating hash
105
- if cache :
106
- print('**Call hash:**', do_stdout=False)
107
- bound = inspect.signature(func).bind(*args, **kwargs)
108
- bound.apply_defaults()
109
- serialized = pickle.dumps(bound.arguments)
110
- args_hash = hashlib.md5(serialized).hexdigest()
111
- result_file = wrapper.path / f'{args_hash}.pkl'
112
- print(f'*{args_hash}*\n', do_stdout=False)
113
-
114
- # Checking already calculated exists
115
- if result_file.exists() and not new :
116
- print('**Loading from *precalculated* results...**')
117
- with open(result_file, 'rb') as f:
118
- result = joblib.load(f)
119
- print('...loaded\n')
120
- return result
121
-
122
- # Calculations
123
- folder(wrapper.path, warning=False)
124
- print('**Calculating results:**')
125
- print_status = kwargsself(print)
126
- print.file = wrapper.path / f'{name}_log.md'
127
- result = func(*args, **kwargs)
128
- selfkwargs(print, print_status)
129
- print('...calculated\n')
130
-
131
- # Caching
132
- if cache :
133
- print('**Saving results:**')
134
- with open(result_file, 'wb') as f:
135
- joblib.dump(result, f)
136
- print('...saved\n')
137
-
138
- # Create symlink
139
- if symlink is not None :
140
- print('**Creating symlinks:**')
141
- for link in symlink :
142
- print(f"- {link}")
143
- link_path = Path(link)
144
- link_folder = self.parent_path.parent / f'_outputs/{link_path.stem}'
145
- new_stem = str(self.subfolder.as_posix()).replace('/', '--').replace(' ', '_')
146
- if not link_folder.exists() :
147
- folder(link_folder, warning=False)
148
- link_from = wrapper.path / link_path
149
- link_to = link_folder / f"{new_stem}{link_path.suffix}"
150
- if link_to.exists() or link_to.is_symlink():
151
- link_to.unlink()
152
- if os.name == "nt":
153
- try :
154
- link_to.symlink_to(link_from, link_from.is_dir())
155
- except OSError :
156
- print("Windows does not allow to create symlink, aborting. Consider using Windows in Developper mode.")
157
- break
158
- else:
159
- link_to.symlink_to(link_from)
160
- print('...created\n')
161
-
162
-
163
- return result
164
- return wrapper
165
- return decorator
166
-
167
- @property
168
- def subfolder(self) :
169
- return self.path.relative_to(self.parent_path)
170
-
171
-
172
-
173
- # %% Test function run
174
- if __name__ == "__main__":
175
- from corelp import test
176
- test(__file__)
File without changes
@@ -1,43 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # Date : 2025-08-28
4
- # Author : Lancelot PINCET
5
- # GitHub : https://github.com/LancelotPincet
6
- # Library : coreLP
7
- # Module : Section
8
-
9
- """
10
- This file allows to test Section
11
-
12
- Section : This class defines decorator instances allowing to create section functions.
13
- """
14
-
15
-
16
-
17
- # %% Libraries
18
- from corelp import debug, Section
19
- debug_folder = debug(__file__)
20
-
21
-
22
-
23
- # %% Function test
24
- def test_function() :
25
- '''
26
- Test Section function
27
- '''
28
- section = Section(path=debug_folder)
29
-
30
- @section()
31
- def add(a, b=0) :
32
- return a+b
33
- assert add(3) == 3
34
- assert add(3, 0) == 3
35
- assert add(1, 3) == 4
36
- assert add(1, b=3) == 4
37
-
38
-
39
-
40
- # %% Test function run
41
- if __name__ == "__main__":
42
- from corelp import test
43
- test(__file__)
File without changes
@@ -1,251 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # Date : 2025-08-28
4
- # Author : Lancelot PINCET
5
- # GitHub : https://github.com/LancelotPincet
6
- # Library : coreLP
7
- # Module : main
8
-
9
- """
10
- This function can decorate the main function of a script.
11
- """
12
-
13
-
14
-
15
- # %% Libraries
16
- from corelp import print, Section, folder, selfkwargs, kwargsself, icon, Path
17
- import time
18
- import functools
19
- import os
20
- from datetime import datetime
21
- import tkinter as tk
22
- from tkinter import filedialog
23
- import types
24
-
25
-
26
-
27
- # %% Function
28
- def main() :
29
- '''
30
- This function can decorate the main function of a script.
31
- User inputs parameters shoud be put in the beginning of the main file, and the decorated function will recognize them.
32
- Decorated function can change the values of these parameters with keyword arguments when called.
33
- Section can be created bellow the mainfunction.
34
-
35
- Global parameters
36
- -----------------
37
- import_path : Path or str or None
38
- Path where to import script data to process.
39
- If None, will manually ask user to select it.
40
- If not existent, will be ignored.
41
- export_path : Path or str or None
42
- Path where to export script data to process.
43
- A new folder will be created inside at the call time as name.
44
- If None, will save in import_path. If not existent, will be ignored.
45
- If a previous call was already made in this same folder, and new is False, will try to reload data from this last folder.
46
- new : bool
47
- Overrides Decorator new parameter.
48
- bulk : function
49
- function(import_path) that returns a dictionnary of {import_subfolder:export_subfolder} for multiple decorated function run.
50
- If bulk is not None, the decorated function will run with import_subfolder, export_subfolder instead of import_path, export_path (see below).
51
- The import_subfolders and export_subfolder are defined from import_path and export_path respectively (they are not absolute from root path).
52
- overnight : bool
53
- If True and exception occurs, will skip and pass to the next run in bulk processing. To use for example for overnight bulk processing.
54
-
55
- Examples
56
- --------
57
- >>> from corelp import main
58
- ...
59
- >>> import_path = None # will be asked via a GUI
60
- >>> export_path = None # will create inside import_path
61
- >>> new = False # True to create a new export folder, False to reload precalculated data
62
- >>> bulk = None # function(import_path) that returns a dictionnary of {import_subfolder:export_subfolder} for multiple decorated function run.
63
- >>> overnight= False # If True and exception occurs, will skip and pass to the next run in bulk processing.
64
- >>> main_string = "Hello from main!" # User input parameter
65
- ...
66
- >>> @main(new=True) # if previous new is not defined, new is defined here
67
- ... def myscript() :
68
- ... print(main_string) # By default prints "Hello from main!"
69
- ... result = mysection() # Section defined bellow, result can be reloaded from previous run
70
- ... return result
71
- ...
72
- ... @main.section()
73
- ... def mysection() :
74
- ... print("Hello from section!")
75
- ... return True # Will be saved into export_path and can be reuploaded at next run with same inputs
76
- ...
77
- >>> # Launch
78
- >>> if __name__ == "__main__" :
79
- ... myscript() # prints "Hello from main!"
80
- ... myscript(main_string = "Hello changed!!") # prints "Hello changed!!" and loads section result from first run
81
- '''
82
-
83
-
84
-
85
- def decorator(func) :
86
- name = func.__name__
87
-
88
- # Get globals around function definition
89
- definition_globals = func.__globals__
90
-
91
- @functools.wraps(func)
92
- def wrapper(**overrides) -> None :
93
-
94
- # Creates new globals
95
- exec_globals = definition_globals.copy()
96
- exec_globals.update(overrides)
97
- _new = exec_globals.get("new", False)
98
- _bulk = exec_globals.get("bulk", None)
99
- _overnight = exec_globals.get("overnight", False)
100
-
101
- # Creates new function
102
- new_func = types.FunctionType(
103
- func.__code__,
104
- exec_globals,
105
- name=name,
106
- argdefs=func.__defaults__,
107
- closure=func.__closure__,
108
- )
109
-
110
- # Getting paths
111
- ipath = exec_globals.get('import_path', "None")
112
- if ipath is None :
113
- root = tk.Tk()
114
- root.title("Select import path")
115
- img = tk.PhotoImage(file=icon)
116
- root.iconphoto(True, img)
117
- root._icon_img = img # keep reference
118
- root.withdraw()
119
- root.update_idletasks()
120
- root.attributes("-topmost", True)
121
- root.update()
122
- root.focus_force()
123
- ipath = filedialog.askdirectory(title=f'Select import path for {name}')
124
- root.destroy()
125
- if not ipath :
126
- print('Searching for import_path was cancelled', style='red')
127
- raise ValueError('Searching for import_path was cancelled')
128
- epath = exec_globals.get('export_path', "None")
129
- if ipath != "None" :
130
- ipath = Path(ipath)
131
- if epath != "None" :
132
- epath = ipath.parent if epath is None else Path(epath)
133
-
134
- # Creating new export path
135
- prefix = name.replace('.', '_')
136
- if epath != "None" :
137
- if _new :
138
- base_path = folder(epath / (f'{prefix}_' + datetime.now().strftime("%Y-%m-%d-%Hh%Mmin%Ss")), warning=False)
139
- else :
140
- #Searching for newest old folder
141
- base_folder = None
142
- _date = None
143
- for f in epath.iterdir() :
144
- if (not f.is_dir()) or (not f.name.startswith(f'{prefix}_')) :
145
- continue
146
- date_str = f.name.split('_')[-1]
147
- date = datetime.strptime(date_str, "%Y-%m-%d-%Hh%Mmin%Ss")
148
- if _date is None or date > _date :
149
- _date, base_folder = date, f
150
- base_path = base_folder if base_folder is not None else epath / (f'{prefix}_' + datetime.now().strftime("%Y-%m-%d-%Hh%Mmin%Ss"))
151
- epath = base_path / 'export_folder'
152
- if not epath.exists():
153
- os.makedirs(epath) #creates folders until end
154
- if ipath != "None" :
155
- ilink = base_path / 'import_folder'
156
- if ilink.exists() or ilink.is_symlink():
157
- ilink.unlink()
158
- if os.name == "nt":
159
- try :
160
- ilink.symlink_to(ipath, ipath.is_dir())
161
- except OSError :
162
- print("Windows does not allow to create symlink, aborting. Consider using Windows in Developper mode.")
163
- else:
164
- ilink.symlink_to(ipath)
165
- md_file = epath / (name+'_log.md')
166
- html_file = epath / (name+'_log.html')
167
- else :
168
- md_file = None
169
- html_file = None
170
-
171
- # Defining bulk processing
172
- if _bulk is None :
173
- subfolders = {"" : ""}
174
- else :
175
- subfolders = _bulk(ipath)
176
-
177
- #Begining prints
178
- print_status = kwargsself(print)
179
- print.console = None
180
- print.file = md_file
181
- print(f'\n\n\n# **BEGIN {name}**\n')
182
- print(f"{time.ctime()}")
183
- if ipath != "None" :
184
- print(f'import_path : {ipath}\n')
185
- if epath != "None" :
186
- print(f'export_path : {epath}\n')
187
-
188
- # Bulk processing
189
- results = {} # {export_subfolder : fucntion result}
190
- for import_subfolder, export_subfolder in subfolders.items() :
191
- if ipath != "None" :
192
- impath = ipath / import_subfolder
193
- exec_globals["import_path"] = impath
194
- if epath != "None" :
195
- expath = epath / export_subfolder
196
- exec_globals["export_path"] = expath
197
-
198
- # Create export subfolder
199
- if not expath.exists() :
200
- os.mkdir(expath)
201
-
202
- # Updating sections
203
- wrapper.section.parent_path = epath
204
- wrapper.section.path = expath
205
- wrapper.section.new = _new
206
-
207
- #Applying function
208
- print("\n---\n")
209
- subfolder_string = f"{export_subfolder}" if export_subfolder != "" else ""
210
- print(f'## **Launched script {subfolder_string}**\n')
211
- tic = time.perf_counter()
212
- try :
213
- results[export_subfolder] = new_func()
214
-
215
- # Errors
216
- except Exception as e :
217
- toc = time.perf_counter()
218
- print.error()
219
- print(f'\n## **{subfolder_string} took {toc-tic:.2f}s**')
220
- print("\n---\n")
221
- if not _overnight :
222
- raise e
223
-
224
- # No error
225
- else :
226
- toc = time.perf_counter()
227
- print(f'\n## **{subfolder_string} took {toc-tic:.2f}s**')
228
- print("\n---\n")
229
-
230
- # END
231
- print(time.ctime())
232
- print(f'# **END {name}**\n\n')
233
- print.export_html(html_file)
234
- selfkwargs(print, print_status)
235
- if _bulk is None :
236
- results = results[""]
237
- return results
238
-
239
- # Making sections
240
- section = Section()
241
- wrapper.section = section
242
-
243
- return wrapper
244
- return decorator
245
-
246
-
247
-
248
- # %% Test function run
249
- if __name__ == "__main__":
250
- from corelp import test
251
- test(__file__)
@@ -1,74 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # Date : 2025-08-28
4
- # Author : Lancelot PINCET
5
- # GitHub : https://github.com/LancelotPincet
6
- # Library : coreLP
7
- # Module : main
8
-
9
- """
10
- This file allows to test main
11
-
12
- main : This function can decorate the main function of a script.
13
- """
14
-
15
-
16
-
17
- # %% Libraries
18
- from corelp import print, debug, main
19
- from time import sleep
20
- import pytest
21
- debug_folder = debug(__file__)
22
-
23
-
24
-
25
- # %% User inputs
26
- search = False # True to apply manual search
27
- global import_path
28
- global export_path
29
- import_path = None if search else debug_folder # Path to the imported data
30
- export_path = import_path # Path to the exported data
31
- new = False # True to create new result folder at each run
32
- bulk = None # function(import_path) that returns a dictionnary of {import_subfolder:export_subfolder} for multiple decorated function run.
33
- overnight= False # If True and exception occurs, will skip and pass to the next run in bulk processing.
34
- myparam = "Hello from main!"
35
- apply_error = False
36
-
37
-
38
-
39
- @main()
40
- def mainfunc() :
41
- if apply_error :
42
- 1/0
43
- print(myparam)
44
- result = section_1()
45
- print(f"import_path = {import_path}")
46
- return result
47
-
48
- @mainfunc.section()
49
- def section_1() :
50
- print('> Hello from section!')
51
- return True
52
-
53
-
54
-
55
- # %% Function test
56
- def test_function() :
57
- '''
58
- Test main function
59
- '''
60
- mainfunc()
61
- sleep(2) # Ensure new folder
62
- mainfunc(myparam="Hello changed!!")
63
- sleep(2) # Ensure new folder
64
- mainfunc(new=True)
65
- sleep(2) # Ensure new folder
66
- with pytest.raises(ZeroDivisionError, match="division by zero") :
67
- mainfunc(apply_error=True)
68
-
69
-
70
-
71
- # %% Test function run
72
- if __name__ == "__main__":
73
- from corelp import test
74
- test(__file__)
File without changes
@@ -1,326 +0,0 @@
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 numpy._core.numeric import True_
24
- from rich import print as richprint
25
- from rich.console import Console
26
- from rich.theme import Theme
27
- from rich.markdown import Markdown
28
- from rich.traceback import Traceback
29
- from rich.progress import (
30
- Progress,
31
- BarColumn,
32
- TextColumn,
33
- TaskProgressColumn,
34
- TimeElapsedColumn,
35
- TimeRemainingColumn,
36
- ProgressColumn,
37
- )
38
- import traceback as tb_module
39
- from time import perf_counter
40
- from pathlib import Path
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.apply_markdown 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
- # Return
162
- if return_string :
163
- return string
164
-
165
-
166
- # MUTING
167
- verbose : bool = True # True to print
168
-
169
-
170
-
171
- # PRINT
172
-
173
- @property
174
- def print(self) :
175
- return self.console.print
176
- @property
177
- def log(self) :
178
- return self.console.log
179
- pyprint = pyprint # python print
180
- richprint = richprint # rich prints
181
- apply_markdown : bool = field(default=True, repr=False) # True to apply rich markdown formatting in prints
182
-
183
-
184
-
185
- # LOGGING
186
-
187
- _file : Path = None
188
- @property
189
- def file(self) :
190
- return self._file
191
- @file.setter
192
- def file(self, value) :
193
- self._file = Path(value)
194
-
195
-
196
-
197
- # CONSOLE
198
-
199
- _theme = {}
200
- @property
201
- def theme(self) :
202
- return self._theme
203
- @theme.setter
204
- def theme(self, value) :
205
- self._theme.update(value)
206
- self._console = None
207
-
208
- _console : Console = field(default=None, repr=False)
209
- @prop(cache=True)
210
- def console(self) :
211
- theme = Theme(self.theme)
212
- return Console(theme=theme, record=True)
213
-
214
- def error(self) :
215
- rich_tb = Traceback.from_exception(*tb_module.sys.exc_info())
216
- self.console.print(rich_tb)
217
-
218
- def print_locals(self) :
219
- self.console.log(log_locals=True)
220
-
221
- def export_html(self, path) :
222
- if path is None :
223
- return
224
- path = Path(path)
225
- html_content = self.console.export_html(inline_styles=True)
226
- with open(path, "w", encoding="utf-8") as file:
227
- file.write(html_content)
228
-
229
-
230
-
231
- # CLOCK
232
-
233
- def clock(self, iterable, title="Working...") :
234
-
235
- # Get iterable
236
- iterable = range(iterable) if isinstance(iterable, int) else iterable
237
- iterable = list(iterable)
238
-
239
- # Detect if progressbar already exists
240
- first_bar = getattr(self, "_progress", None) is None
241
- progress = self.progress
242
- bars = self.bars
243
-
244
- # Opens progress
245
- if first_bar :
246
- verbose = self.verbose
247
- self.verbose = False
248
-
249
- # Write to file
250
- if self.file is not None :
251
- with open(Path(self.file), "a") as file :
252
- file.write(f'{title}...\n')
253
- progress.start()
254
-
255
- # Create new task
256
- task = bars.get(title, None)
257
- if task is None : # No bar with this name exists
258
- task = progress.add_task(title, total=len(iterable), avg_time=0.0)
259
- bars[title] = task # store it
260
- else :
261
- progress.reset(task)
262
-
263
- # Loop
264
- loop_counter = 0
265
- start = perf_counter()
266
- for item in iterable :
267
- yield item
268
- loop_counter += 1
269
- elapsed = perf_counter() - start
270
- avg_time = elapsed / loop_counter if loop_counter else 0
271
- progress.update(task, advance=1, avg_time=avg_time)
272
-
273
- # Clean up
274
- if first_bar :
275
- progress.stop()
276
- del(self.bars)
277
- del(self.progress)
278
- self.verbose = verbose
279
-
280
- _progress : Progress = field(default=None, repr=False)
281
- @prop(cache=True)
282
- def progress(self) :
283
- return Progress(
284
- TextColumn("{task.description}"),
285
- BarColumn(),
286
- TaskProgressColumn(),
287
- TextColumn("[magenta]/{task.total}[/]"),
288
- TimeElapsedColumn(),
289
- AvgLoopTimeColumn(),
290
- TimeRemainingColumn(),
291
- EndTimeColumn(),
292
- transient=False,
293
- console=self.console
294
- )
295
-
296
- _bars : dict = field(default=None, repr=False)
297
- @prop(cache=True)
298
- def bars(self) :
299
- return {}
300
-
301
-
302
-
303
- # Get instance
304
- print = Print() # Instance to use everywhere
305
-
306
- # Custom Progress bar columns
307
- class AvgLoopTimeColumn(ProgressColumn):
308
- def render(self, task):
309
- avg_time = task.fields.get("avg_time", None)
310
- if avg_time is not None and task.completed > 0:
311
- string = f"[yellow]↻ {avg_time:.2f}s[/]" if avg_time > 1 else f"[yellow]↻ {avg_time*1000:.2f}ms[/]"
312
- return string
313
- return ""
314
- class EndTimeColumn(ProgressColumn):
315
- def render(self, task):
316
- if task.time_remaining is not None:
317
- end_time = datetime.now() + timedelta(seconds=task.time_remaining)
318
- return f"[cyan]{end_time:%m-%d %H:%M:%S}[/] "
319
- return ""
320
-
321
-
322
-
323
- # %% Test function run
324
- if __name__ == "__main__":
325
- from corelp import test
326
- test(__file__)
@@ -1,108 +0,0 @@
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 corelp import print, debug
19
- import pytest
20
- from time import sleep
21
- debug_folder = debug(__file__)
22
-
23
-
24
- # %% test prints
25
- def test_print() :
26
- '''
27
- Test print
28
- '''
29
- string = "# TEST\nHello *world*!\n\nThis is 1 print **example**"
30
- print(string, style="magenta")
31
- print.print(string, style="magenta")
32
- print.log(string, style="magenta")
33
- print.pyprint(string)
34
- print.richprint(string)
35
-
36
-
37
-
38
- # %% test verbose
39
- def test_verbose() :
40
- '''
41
- Test verbose
42
- '''
43
- print.verbose = False # Muting
44
- print("Should not print") # Does not print
45
- print("Should print", verbose=True) # Does print
46
- print("Should not print") # Does not print
47
- print.verbose = True # Unmuting
48
- print("Should print") # Does print
49
- print("Should not print", verbose=False) # Does not print
50
- print("Should print") # Does print
51
-
52
-
53
-
54
- # %% test logging
55
- def test_logging() :
56
- '''
57
- Test logging
58
- '''
59
- print.theme = {"success" : "green"}
60
- string = "# TEST\nHello *world*!\n\nThis is 1 print **example**"
61
- print(string, style="success")
62
- print.print_locals()
63
- try :
64
- 1/0
65
- except Exception :
66
- print.error()
67
- file = debug_folder / "log.html"
68
- print.export_html(file)
69
-
70
-
71
-
72
- # %% test console
73
- def test_console() :
74
- '''
75
- Test console
76
- '''
77
- file = debug_folder / 'log.md'
78
- print.file = file
79
- string = "# TEST\nHello *world*!\n\nThis is 1 print **example**"
80
- print(string, style="magenta")
81
- assert file.exists()
82
-
83
-
84
-
85
- # %% test clock
86
- def test_clock() :
87
- '''
88
- Test clock
89
- '''
90
- for i in print.clock(5, "Outer loop") :
91
- print("Should not print")
92
- for j in print.clock(5, "Inner loop") :
93
- sleep(1)
94
- print("Should not print")
95
-
96
- for i in print.clock(10, "Other loop") :
97
- sleep(1)
98
- print("Should not print")
99
-
100
-
101
-
102
-
103
-
104
-
105
- # %% Test function run
106
- if __name__ == "__main__":
107
- from corelp import test
108
- test(__file__)
File without changes
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # Date : 2025-11-30
4
- # Author : Lancelot PINCET
5
- # GitHub : https://github.com/LancelotPincet
6
- # Library : coreLP
7
- # Module : user_inputs
8
-
9
- """
10
- This file allows to test user_inputs
11
-
12
- user_inputs : Gets last user inputs dictionnary from global variables.
13
- """
14
-
15
-
16
-
17
- # %% Libraries
18
- from corelp import print, debug
19
- import pytest
20
- from corelp import user_inputs
21
- debug_folder = debug(__file__)
22
-
23
-
24
-
25
- # %% Function test
26
- def test_user_inputs() :
27
- '''
28
- Test user_inputs function
29
- '''
30
- user_inputs() #init
31
- a = 1
32
- inputs = user_inputs()
33
- if inputs != {'a': 1} :
34
- raise ValueError(f'{inputs} should be dict(a=1)')
35
- user_inputs() #init
36
- b = 2
37
- inputs = user_inputs()
38
- if inputs != {'b': 2} :
39
- raise ValueError(f'{inputs} should be dict(b=2)')
40
-
41
-
42
-
43
- # %% Test function run
44
- if __name__ == "__main__":
45
- from corelp import test
46
- test(__file__)
@@ -1,87 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # Date : 2025-11-30
4
- # Author : Lancelot PINCET
5
- # GitHub : https://github.com/LancelotPincet
6
- # Library : coreLP
7
- # Module : user_inputs
8
-
9
- """
10
- Gets last user inputs dictionnary from global variables.
11
- """
12
-
13
-
14
-
15
- # %% Libraries
16
- import inspect
17
-
18
-
19
-
20
- # %% Function
21
-
22
- def user_inputs(reset=False) :
23
- r"""
24
- Return a dictionary of variables defined by the user in the interactive environment.
25
-
26
- Parameters
27
- ----------
28
- reset : bool or str
29
- True to set as first call, String value to define a group of parameters
30
-
31
- Returns
32
- -------
33
- dict
34
- A dictionary containing the user's currently defined variables.
35
-
36
- Examples
37
- --------
38
- >>> from corelp import user_inputs
39
- >>> user_inputs(True) # First call (initializes and clears import-related variables)
40
- None
41
- >>> a = 1 # User defines a variable
42
- >>> user_inputs() # Now returns: {'a': 1}
43
- {'a': 1}
44
- """
45
- frame = inspect.currentframe().f_back
46
- ns = {**frame.f_globals, **frame.f_locals}
47
-
48
- # ---- Filter user variables (ignore internals starting with "_") ----
49
- ns = {key: value for key, value in ns.items() if not key.startswith("_")}
50
-
51
- # Validate status
52
- if reset :
53
- user_inputs.current_group = reset if isinstance(reset, str) else None
54
- user_inputs.cache = None
55
-
56
- # Case when user_inputs is on top : cache = None
57
- if user_inputs.cache is None :
58
- user_inputs.cache = ns
59
- return
60
-
61
- # Case when user_inputs is at bottom : cache = dict
62
- else :
63
- updated = { key: value for key, value in ns.items() if key not in user_inputs.cache or user_inputs.cache[key] is not value}
64
- values = {key: value for key, value in updated.items() if not key.endswith('_')}
65
- comments = {key: value for key, value in updated.items() if key.endswith('_')}
66
-
67
- # Group values
68
- if user_inputs.current_group is not None :
69
- user_inputs.groups_values[user_inputs.current_group] = values
70
- user_inputs.groups_comments[user_inputs.current_group] = comments
71
-
72
- # End
73
- user_inputs.current_group = None
74
- user_inputs.cache = None
75
- return values
76
-
77
- user_inputs.cache = None
78
- user_inputs.current_group = None
79
- user_inputs.groups_values = {}
80
- user_inputs.groups_comments = {}
81
-
82
-
83
-
84
- # %% Test function run
85
- if __name__ == "__main__":
86
- from corelp import test
87
- test(__file__)