pyrandyos 1.1.0a1__tar.gz
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.
- pyrandyos-1.1.0a1/.gitignore +15 -0
- pyrandyos-1.1.0a1/LICENSE +21 -0
- pyrandyos-1.1.0a1/PKG-INFO +32 -0
- pyrandyos-1.1.0a1/pyproject.toml +81 -0
- pyrandyos-1.1.0a1/pyrandyos/__init__.py +2 -0
- pyrandyos-1.1.0a1/pyrandyos/__main__.py +14 -0
- pyrandyos-1.1.0a1/pyrandyos/_testing/__init__.py +4 -0
- pyrandyos-1.1.0a1/pyrandyos/_testing/_pyrandyos_testing.py +13 -0
- pyrandyos-1.1.0a1/pyrandyos/_testing/debug.py +35 -0
- pyrandyos-1.1.0a1/pyrandyos/app.py +187 -0
- pyrandyos-1.1.0a1/pyrandyos/config/__init__.py +2 -0
- pyrandyos-1.1.0a1/pyrandyos/config/appconfig.py +270 -0
- pyrandyos-1.1.0a1/pyrandyos/config/defaults.py +49 -0
- pyrandyos-1.1.0a1/pyrandyos/config/expandutils.py +59 -0
- pyrandyos-1.1.0a1/pyrandyos/config/keys.py +58 -0
- pyrandyos-1.1.0a1/pyrandyos/config/local.py +112 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/__init__.py +144 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/callback.py +25 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/dialogs/__init__.py +22 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/dialogs/config/__init__.py +1 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/dialogs/config/icons.py +20 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/dialogs/config/pres.py +56 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/dialogs/config/view.py +90 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/dialogs/log/__init__.py +1 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/dialogs/log/mixin.py +29 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/dialogs/log/pres.py +115 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/dialogs/log/view.py +170 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/gui_app.py +170 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/__init__.py +5 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/.gitattributes +1 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Codicons/codicon.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Codicons/json/LICENSE +395 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Codicons/json/LICENSE-CODE +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Codicons/mapping.json +602 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Codicons/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/ElusiveIcons/elusiveicons-webfont-2.0.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/ElusiveIcons/elusiveicons-webfont-charmap-2.0.json +306 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/ElusiveIcons/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/ElusiveIcons/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5/fontawesome5-regular-webfont-5.15.4.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5/fontawesome5-regular-webfont-charmap-5.15.4.json +154 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5_Brands/fontawesome5-brands-webfont-5.15.4.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5_Brands/fontawesome5-brands-webfont-charmap-5.15.4.json +459 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5_Brands/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5_Brands/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5_Solid/fontawesome5-solid-webfont-5.15.4.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5_Solid/fontawesome5-solid-webfont-charmap-5.15.4.json +1004 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5_Solid/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa5_Solid/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6/fontawesome6-regular-webfont-6.7.2.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6/fontawesome6-regular-webfont-charmap-6.7.2.json +165 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6_Brands/fontawesome6-brands-webfont-6.7.2.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6_Brands/fontawesome6-brands-webfont-charmap-6.7.2.json +497 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6_Brands/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6_Brands/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6_Solid/fontawesome6-solid-webfont-6.7.2.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6_Solid/fontawesome6-solid-webfont-charmap-6.7.2.json +1404 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6_Solid/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Fa6_Solid/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI/FluentSystemIcons-Regular.json +8949 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI/FluentSystemIcons-Regular.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI/json/LICENSE +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI/ttf/LICENSE +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Filled/FluentSystemIcons-Filled.json +9074 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Filled/FluentSystemIcons-Filled.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Filled/json/LICENSE +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Filled/ttf/LICENSE +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Light/FluentSystemIcons-Light.json +191 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Light/FluentSystemIcons-Light.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Light/json/LICENSE +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Light/ttf/LICENSE +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Resize/FluentSystemIcons-Resizable.json +5344 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Resize/FluentSystemIcons-Resizable.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Resize/json/LICENSE +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/FluentUI_Resize/ttf/LICENSE +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Material5/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Material5/materialdesignicons5-webfont-5.9.55.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Material5/materialdesignicons5-webfont-charmap-5.9.55.json +5960 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Material5/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Material6/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Material6/materialdesignicons6-webfont-6.9.96.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Material6/materialdesignicons6-webfont-charmap-6.9.96.json +7369 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Material6/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Phosphor/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Phosphor/phosphor-1.3.0.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Phosphor/phosphor-charmap-1.3.0.json +4472 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/Phosphor/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/RemixIcon/json/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/RemixIcon/remixicon-2.5.0.ttf +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/RemixIcon/remixicon-charmap-2.5.0.json +2273 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/assets/RemixIcon/ttf/LICENSE.txt +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/iconfont/__init__.py +18 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/iconfont/animation.py +129 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/iconfont/font.py +142 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/iconfont/fontspec.py +136 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/iconfont/icon.py +420 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/iconfont/sources.py +102 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/init_template.py +5 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/__init__.py +0 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/codicons/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/codicons/names.py +602 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/elusiveicons/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/elusiveicons/names.py +306 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa5/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa5/brands/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa5/brands/names.py +459 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa5/names.py +154 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa5/solid/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa5/solid/names.py +1004 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa6/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa6/brands/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa6/brands/names.py +497 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa6/names.py +165 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa6/solid/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fa6/solid/names.py +1404 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fluentui/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fluentui/filled/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fluentui/filled/names.py +9074 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fluentui/light/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fluentui/light/names.py +191 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fluentui/names.py +8949 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fluentui/resize/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/fluentui/resize/names.py +5344 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/material5/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/material5/names.py +5960 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/material6/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/material6/names.py +7369 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/phosphor/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/phosphor/names.py +4472 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/remixicon/__init__.py +7 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/thirdparty/remixicon/names.py +2273 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/update_from_spec.py +70 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/icons/utils.py +18 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/loadstatus.py +95 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/qrc.py +78 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/qt.py +92 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/splash.py +146 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/__init__.py +64 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/arrow-down.svg +3 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/arrow-left.svg +3 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/arrow-right.svg +3 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/arrow-up.svg +3 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/branch-closed-end.svg +6 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/branch-closed-more.svg +6 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/branch-closed.svg +3 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/branch-end.svg +5 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/branch-more.svg +5 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/branch-open-end.svg +6 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/branch-open-more.svg +6 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/branch-open.svg +3 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/checkbox-checked.svg +4 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/checkbox-unchecked.svg +3 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/radio-checked.svg +4 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/radio-unchecked.svg +3 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/vibedark.qrc +21 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/vibedark.qss +602 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/assets/vline.svg +4 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/qss.py +75 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/themes/__init__.py +3 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/themes/light.py +81 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/themes/qdarkstyle.py +99 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/styles/themes/vibedark.py +62 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/utils.py +362 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/widgets/__init__.py +117 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/widgets/graphview.py +20 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/widgets/viewbase.py +45 -0
- pyrandyos-1.1.0a1/pyrandyos/gui/window.py +45 -0
- pyrandyos-1.1.0a1/pyrandyos/logging.py +359 -0
- pyrandyos-1.1.0a1/pyrandyos/tools/add_log_func_call_decorators.py +298 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/__init__.py +0 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/casesafe.py +69 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/cfgdict.py +96 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/classproperty.py +34 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/cli.py +128 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/constants/__init__.py +15 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/constants/cli.py +60 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/constants/windows.py +46 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/encoding.py +9 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/expandvars.py +166 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/filemeta.py +272 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/git.py +90 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/hash.py +11 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/json.py +109 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/log.py +232 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/main.py +78 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/math.py +18 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/net.py +39 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/notebook.py +30 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/paths.py +225 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/signature_wrapper.py +156 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/sqlite.py +82 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/stack.py +517 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/string.py +55 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/system.py +171 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/tqdm.py +112 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/windows/__init__.py +1 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/windows/ctypes.py +50 -0
- pyrandyos-1.1.0a1/pyrandyos/utils/windows/funcs.py +106 -0
- pyrandyos-1.1.0a1/pyrandyos/version.py +4 -0
- pyrandyos-1.1.0a1/readme.md +9 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Randy Eckman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyrandyos
|
|
3
|
+
Version: 1.1.0a1
|
|
4
|
+
Summary: Library of common functions for Python applications
|
|
5
|
+
Project-URL: Homepage, https://github.com/emanspeaks/pyrandyos
|
|
6
|
+
Project-URL: Issues, https://github.com/emanspeaks/pyrandyos/issues
|
|
7
|
+
Author-email: Randy Eckman <emanspeaks@gmail.com>
|
|
8
|
+
Maintainer-email: Randy Eckman <emanspeaks@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: pandas
|
|
16
|
+
Requires-Dist: pyside2
|
|
17
|
+
Requires-Dist: pywin32
|
|
18
|
+
Requires-Dist: tqdm
|
|
19
|
+
Requires-Dist: types-pyside2
|
|
20
|
+
Provides-Extra: qdarkstyle
|
|
21
|
+
Requires-Dist: qdarkstyle; extra == 'qdarkstyle'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# PyRandyOS
|
|
25
|
+
|
|
26
|
+
Library of common functions for Python applications
|
|
27
|
+
|
|
28
|
+
## Licenses
|
|
29
|
+
|
|
30
|
+
### Icons
|
|
31
|
+
|
|
32
|
+
This repo uses icons taken from other open source icon libraries. Links to their licensing info are maintained alongside the URLs for the icon fonts in [`pyrandyos/gui/icons/iconfont/sources.py`](pyrandyos/gui/icons/iconfont/sources.py), where the paths to the license files are with respect to the repo root at the given URL.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyrandyos"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Library of common functions for Python applications"
|
|
9
|
+
readme = "readme.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{name = "Randy Eckman", email = "emanspeaks@gmail.com"},
|
|
12
|
+
]
|
|
13
|
+
maintainers = [
|
|
14
|
+
{name = "Randy Eckman", email = "emanspeaks@gmail.com"},
|
|
15
|
+
]
|
|
16
|
+
dependencies = [
|
|
17
|
+
"pywin32",
|
|
18
|
+
"tqdm",
|
|
19
|
+
"pyside2",
|
|
20
|
+
"types-pyside2",
|
|
21
|
+
"pandas",
|
|
22
|
+
"numpy",
|
|
23
|
+
]
|
|
24
|
+
license = "MIT"
|
|
25
|
+
license-files = ["LICENSE"]
|
|
26
|
+
requires-python = ">=3.10"
|
|
27
|
+
classifiers = [
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
qdarkstyle = ["qdarkstyle"]
|
|
34
|
+
|
|
35
|
+
[tool.hatch.version]
|
|
36
|
+
path = "pyrandyos/version.py"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build]
|
|
39
|
+
include = [
|
|
40
|
+
"pyrandyos/**/*.py",
|
|
41
|
+
"pyrandyos/gui/styles/assets",
|
|
42
|
+
"pyrandyos/gui/icons/assets",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://github.com/emanspeaks/pyrandyos"
|
|
47
|
+
Issues = "https://github.com/emanspeaks/pyrandyos/issues"
|
|
48
|
+
|
|
49
|
+
[tool.coverage.report]
|
|
50
|
+
# ignore_errors = true
|
|
51
|
+
|
|
52
|
+
# Regexes for lines to exclude from consideration
|
|
53
|
+
exclude_also = [
|
|
54
|
+
# Don't complain about missing debug-only code:
|
|
55
|
+
# "def __repr__",
|
|
56
|
+
# "if self\\.debug",
|
|
57
|
+
|
|
58
|
+
# Don't complain if tests don't hit defensive assertion code:
|
|
59
|
+
"raise AssertionError",
|
|
60
|
+
"raise NotImplementedError",
|
|
61
|
+
|
|
62
|
+
# Don't complain if non-runnable code isn't run:
|
|
63
|
+
# "if 0:",
|
|
64
|
+
"if __name__ == .__main__.:",
|
|
65
|
+
|
|
66
|
+
# Don't complain about abstract methods, they aren't run:
|
|
67
|
+
"@(abc\\.)?abstractmethod",
|
|
68
|
+
|
|
69
|
+
'if TYPE_CHECKING:',
|
|
70
|
+
'if IS_WIN_CTYPES:'
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[tool.coverage.run]
|
|
74
|
+
branch = true
|
|
75
|
+
# https://github.com/nedbat/coveragepy/issues/1392#issuecomment-1149310741
|
|
76
|
+
omit = [
|
|
77
|
+
"shibokensupport/*",
|
|
78
|
+
"pyscript",
|
|
79
|
+
"signature_bootstrap.py",
|
|
80
|
+
"tests/*",
|
|
81
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
args = sys.argv[1:]
|
|
5
|
+
if args[0] == 'log_func_call':
|
|
6
|
+
from .tools.add_log_func_call_decorators import main as main_log_func_call
|
|
7
|
+
if len(args) > 1:
|
|
8
|
+
config_path = args[1]
|
|
9
|
+
main_log_func_call(['.'], config_path=config_path)
|
|
10
|
+
else:
|
|
11
|
+
main_log_func_call(['.'])
|
|
12
|
+
|
|
13
|
+
else:
|
|
14
|
+
raise ValueError(f"Unknown command: {args[0]}")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from os import environ
|
|
2
|
+
|
|
3
|
+
# these constants are only relevant to testing, do not add to constants
|
|
4
|
+
ENV_CI_RUNNING = 'CI'
|
|
5
|
+
ENV_PYRANDYOS_UNITTEST_ACTIVE = 'PYRANDYOS_UNITTEST_ACTIVE'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _is_pyrandyos_unittest():
|
|
9
|
+
return bool(environ.get(ENV_PYRANDYOS_UNITTEST_ACTIVE))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _is_running_in_ci():
|
|
13
|
+
return bool(environ.get(ENV_CI_RUNNING))
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def is_debug_enabled():
|
|
5
|
+
# see https://stackoverflow.com/a/77627047/13230486
|
|
6
|
+
try:
|
|
7
|
+
if sys.gettrace() is not None:
|
|
8
|
+
return True
|
|
9
|
+
except AttributeError:
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
if sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) is not None:
|
|
14
|
+
return True
|
|
15
|
+
except AttributeError:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def hide_splash():
|
|
22
|
+
from pyrandyos.gui.gui_app import get_gui_app
|
|
23
|
+
gui_app = get_gui_app()
|
|
24
|
+
if gui_app and gui_app.splash:
|
|
25
|
+
splash = gui_app.splash.gui_view.qtobj
|
|
26
|
+
if splash.isVisible():
|
|
27
|
+
splash.hide()
|
|
28
|
+
if is_debug_enabled():
|
|
29
|
+
print("Splash screen hidden, debugger is enabled")
|
|
30
|
+
else:
|
|
31
|
+
print("Splash screen hidden")
|
|
32
|
+
else:
|
|
33
|
+
print("Splash screen is already hidden")
|
|
34
|
+
else:
|
|
35
|
+
print("No splash screen to hide")
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from tempfile import gettempdir
|
|
4
|
+
|
|
5
|
+
from .logging import (
|
|
6
|
+
log_info, Logger, log_func_call, DEBUGLOW2, set_trace_logging,
|
|
7
|
+
set_func_call_logging,
|
|
8
|
+
)
|
|
9
|
+
from .config import AppConfig
|
|
10
|
+
from .config.keys import (
|
|
11
|
+
BASE_LOG_PATH_KEY, APP_NAME_KEY, get_log_dir_keys, APP_PKG_DIR_KEY,
|
|
12
|
+
APP_ASSETS_DIR_KEY, APP_PKG_VERSION_KEY, TMP_DIR_KEY, LOCAL_CFG_KEY
|
|
13
|
+
)
|
|
14
|
+
from .config.local import process_local_config
|
|
15
|
+
|
|
16
|
+
from .utils.constants import DEFAULT_GROUP, DEFAULT_DIR_MODE
|
|
17
|
+
from .utils.log import setup_logging, create_log_file
|
|
18
|
+
from .utils.main import MainContext
|
|
19
|
+
from .utils.system import mkdir_chgrp
|
|
20
|
+
from .utils.stack import (
|
|
21
|
+
top_package_dir_path, top_module_and_name, set_show_traceback_locals
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PyRandyOSApp(AppConfig):
|
|
26
|
+
# abstract class attributes
|
|
27
|
+
APP_NAME: str
|
|
28
|
+
APP_LOG_PREFIX: str
|
|
29
|
+
|
|
30
|
+
# class attributes with fallback defaults
|
|
31
|
+
APP_PATH_KEYS: tuple[str] = ()
|
|
32
|
+
APP_LOG_DIR_KEYS: tuple[str] = ()
|
|
33
|
+
APP_GLOBAL_DEFAULTS = {}
|
|
34
|
+
APP_LOCAL_DEFAULTS = {}
|
|
35
|
+
APP_ASSETS_DIR: str | Path = None
|
|
36
|
+
"path to be resolved when config is processed; may use variable expansion"
|
|
37
|
+
|
|
38
|
+
use_local_config: bool = None
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
@log_func_call
|
|
42
|
+
def main(cls, *args, **kwargs):
|
|
43
|
+
raise NotImplementedError
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
@log_func_call
|
|
47
|
+
def init_main(cls, config: dict | str | Path = None,
|
|
48
|
+
setup_log: bool = False, logfile: Path = None,
|
|
49
|
+
logger: Logger = None, **kwargs):
|
|
50
|
+
"returns True if a local config is present and loaded, else False"
|
|
51
|
+
# log = logger or get_logger()
|
|
52
|
+
cls.set_logger(logger)
|
|
53
|
+
cls.init_parse_config(config, kwargs)
|
|
54
|
+
|
|
55
|
+
# setup logging first if necessary:
|
|
56
|
+
if setup_log:
|
|
57
|
+
(
|
|
58
|
+
logdir,
|
|
59
|
+
ts_name,
|
|
60
|
+
append,
|
|
61
|
+
cli_log_level,
|
|
62
|
+
file_log_level,
|
|
63
|
+
log_trace_enabled,
|
|
64
|
+
tb_locals_enabled,
|
|
65
|
+
log_func_call_enabled,
|
|
66
|
+
) = cls.expand_log_config()
|
|
67
|
+
logfile = logfile or create_log_file(logdir, ts_name, append,
|
|
68
|
+
cls.APP_LOG_PREFIX)
|
|
69
|
+
|
|
70
|
+
set_trace_logging(log_trace_enabled)
|
|
71
|
+
set_func_call_logging(log_func_call_enabled)
|
|
72
|
+
set_show_traceback_locals(tb_locals_enabled)
|
|
73
|
+
setup_logging(logfile, cli_log_level, file_log_level)
|
|
74
|
+
|
|
75
|
+
# start logging and process the rest of the configuration data
|
|
76
|
+
cls.set(BASE_LOG_PATH_KEY, logfile)
|
|
77
|
+
cls.set(APP_PKG_VERSION_KEY, cls.get_package_version())
|
|
78
|
+
pkgdir = cls.get_package_dir()
|
|
79
|
+
cls.set(APP_PKG_DIR_KEY, pkgdir)
|
|
80
|
+
assets_dir = cls.get_assets_dir()
|
|
81
|
+
if assets_dir:
|
|
82
|
+
cls.set(APP_ASSETS_DIR_KEY, assets_dir)
|
|
83
|
+
|
|
84
|
+
appname = cls.APP_NAME
|
|
85
|
+
cls.set(APP_NAME_KEY, appname)
|
|
86
|
+
log_info(f"Starting {appname}")
|
|
87
|
+
cls.process_config()
|
|
88
|
+
|
|
89
|
+
use_local_config = process_local_config(app_path_keys=cls.APP_PATH_KEYS) # noqa: E501
|
|
90
|
+
cls.use_local_config = use_local_config
|
|
91
|
+
return use_local_config
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
@log_func_call
|
|
95
|
+
def process_config(cls, skip_expansion: str | list[str] = 'skip_expand',
|
|
96
|
+
config: dict = None):
|
|
97
|
+
return super().process_config(skip_expansion, config,
|
|
98
|
+
app_path_keys=cls.APP_PATH_KEYS)
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
@log_func_call
|
|
102
|
+
def init_parse_config(cls, indata: dict | str | Path = None,
|
|
103
|
+
overrides: dict = None, defaults: dict = None):
|
|
104
|
+
global_defaults = dict()
|
|
105
|
+
global_defaults.update(cls.APP_GLOBAL_DEFAULTS)
|
|
106
|
+
return super().init_parse_config(indata, overrides, defaults,
|
|
107
|
+
global_defaults,
|
|
108
|
+
cls.APP_LOCAL_DEFAULTS)
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
@log_func_call
|
|
112
|
+
def run_cmdline(cls, args: list[str] = None):
|
|
113
|
+
if args is None:
|
|
114
|
+
args = sys.argv[1:]
|
|
115
|
+
|
|
116
|
+
with MainContext(cls.APP_NAME):
|
|
117
|
+
sys.exit(cls.main(*cls.preprocess_args(args)))
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
@log_func_call
|
|
121
|
+
def preprocess_args(cls, args: list[str]):
|
|
122
|
+
if len(args) > 0:
|
|
123
|
+
raise ValueError('too many command line arguments')
|
|
124
|
+
|
|
125
|
+
return args
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
@log_func_call
|
|
129
|
+
def create_log_dirs(cls, group: str = DEFAULT_GROUP,
|
|
130
|
+
mode: int = DEFAULT_DIR_MODE):
|
|
131
|
+
for key in get_log_dir_keys(cls.APP_LOG_DIR_KEYS):
|
|
132
|
+
p: Path = cls.get(key)
|
|
133
|
+
mkdir_chgrp(p, group, mode)
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
@log_func_call(DEBUGLOW2, trace_only=True)
|
|
137
|
+
def mkdir_temp(cls, name: str = None, group: str = DEFAULT_GROUP,
|
|
138
|
+
mode: int = DEFAULT_DIR_MODE):
|
|
139
|
+
tmp_dir: Path | None = cls.get(TMP_DIR_KEY)
|
|
140
|
+
if not tmp_dir:
|
|
141
|
+
try:
|
|
142
|
+
appname = cls.APP_NAME
|
|
143
|
+
except AttributeError:
|
|
144
|
+
appname = 'PyRandyOSApp'
|
|
145
|
+
|
|
146
|
+
tmp_dir = Path(gettempdir())/appname
|
|
147
|
+
if cls.global_config:
|
|
148
|
+
cls.set(TMP_DIR_KEY, tmp_dir)
|
|
149
|
+
|
|
150
|
+
if name:
|
|
151
|
+
tmp_dir = tmp_dir/name
|
|
152
|
+
|
|
153
|
+
mkdir_chgrp(tmp_dir, group, mode)
|
|
154
|
+
return tmp_dir
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
@log_func_call
|
|
158
|
+
def get_package_version(cls):
|
|
159
|
+
return getattr(top_module_and_name(cls)[0], '__version__')
|
|
160
|
+
|
|
161
|
+
@classmethod
|
|
162
|
+
@log_func_call
|
|
163
|
+
def get_package_dir(cls):
|
|
164
|
+
return top_package_dir_path(cls)
|
|
165
|
+
|
|
166
|
+
@classmethod
|
|
167
|
+
@log_func_call
|
|
168
|
+
def get_assets_dir(cls):
|
|
169
|
+
assetsdir = cls.get(APP_ASSETS_DIR_KEY, cls.APP_ASSETS_DIR)
|
|
170
|
+
if assetsdir:
|
|
171
|
+
return Path(assetsdir)
|
|
172
|
+
|
|
173
|
+
@classmethod
|
|
174
|
+
@log_func_call(DEBUGLOW2)
|
|
175
|
+
def get_default_win_size(cls):
|
|
176
|
+
return (cls[f'{LOCAL_CFG_KEY}.default_width'],
|
|
177
|
+
cls[f'{LOCAL_CFG_KEY}.default_height'])
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
@log_func_call(DEBUGLOW2)
|
|
181
|
+
def get_local_config(cls, case_insensitive: bool = None):
|
|
182
|
+
return super().get_local_config(case_insensitive=case_insensitive)
|
|
183
|
+
|
|
184
|
+
@classmethod
|
|
185
|
+
@log_func_call
|
|
186
|
+
def save_local_config(cls, case_insensitive: bool = None):
|
|
187
|
+
return super().save_local_config(cls.APP_PATH_KEYS, case_insensitive)
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
from logging import Logger
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
|
|
5
|
+
from ..logging import (
|
|
6
|
+
DEBUGLOW2, log_func_call, set_trace_logging, set_func_call_logging,
|
|
7
|
+
set_global_logger, get_global_logger
|
|
8
|
+
)
|
|
9
|
+
from ..utils.classproperty import classproperty
|
|
10
|
+
from ..utils.paths import (
|
|
11
|
+
get_expanded_pathobj, get_expanded_pureposixpath, pureposixpath_to_pathobj
|
|
12
|
+
)
|
|
13
|
+
from ..utils.json import load_jsonc
|
|
14
|
+
from ..utils.cfgdict import (
|
|
15
|
+
config_dict_get, config_dict_set, config_dict_update,
|
|
16
|
+
)
|
|
17
|
+
from ..utils.constants import NODEFAULT, IS_WIN32
|
|
18
|
+
from ..utils.system import build_cmd_arg_list
|
|
19
|
+
from ..utils.casesafe import casesafe_is_equal, casesafe_value_in_container
|
|
20
|
+
from ..utils.stack import set_show_traceback_locals
|
|
21
|
+
|
|
22
|
+
from .defaults import get_defaults
|
|
23
|
+
from .keys import (
|
|
24
|
+
BASE_LOG_DIR_KEY, BASE_PATH_KEY, LOG_TIMESTAMP_KEY,
|
|
25
|
+
APPEND_LOG_KEY, CLI_LOG_LEVEL_KEY, get_path_keys, BASE_LOG_PATH_KEY,
|
|
26
|
+
FILE_LOG_LEVEL_KEY, ABS_BASE_PATH_KEY, LOG_TRACE_ENABLED_KEY,
|
|
27
|
+
SHOW_TRACEBACK_LOCALS_KEY, LOG_FUNC_CALL_ENABLED_KEY,
|
|
28
|
+
)
|
|
29
|
+
from .expandutils import expand_key_recursively
|
|
30
|
+
|
|
31
|
+
_GLOBAL_CFG: dict = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AppConfigType(type):
|
|
35
|
+
# this is really just here for VSCode syntax highlighting because the real
|
|
36
|
+
# class won't necessarily have access to this as a metaclass.
|
|
37
|
+
# Don't overthink this. Just make sure it's set in AppConfig and
|
|
38
|
+
# its subclasses.
|
|
39
|
+
CONFIG_CASE_INSENSITIVE: bool
|
|
40
|
+
|
|
41
|
+
@log_func_call(DEBUGLOW2, trace_only=True)
|
|
42
|
+
def get_case(cls, case_insensitive: bool = None):
|
|
43
|
+
return (cls.CONFIG_CASE_INSENSITIVE if case_insensitive is None
|
|
44
|
+
else case_insensitive)
|
|
45
|
+
|
|
46
|
+
@log_func_call(DEBUGLOW2, trace_only=True)
|
|
47
|
+
def __contains__(cls, item):
|
|
48
|
+
return casesafe_value_in_container(_GLOBAL_CFG, item, cls.get_case())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AppConfig(metaclass=AppConfigType):
|
|
52
|
+
CONFIG_CASE_INSENSITIVE: bool = IS_WIN32
|
|
53
|
+
|
|
54
|
+
@log_func_call
|
|
55
|
+
def __init__(self):
|
|
56
|
+
# Class is intended as a singleton
|
|
57
|
+
if type(self) is AppConfig: # pragma: no cover
|
|
58
|
+
raise NotImplementedError('Singleton class should not be '
|
|
59
|
+
'instantiated')
|
|
60
|
+
|
|
61
|
+
@classproperty
|
|
62
|
+
@log_func_call(DEBUGLOW2, trace_only=True)
|
|
63
|
+
def log(cls):
|
|
64
|
+
return get_global_logger()
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
@log_func_call
|
|
68
|
+
def set_logger(cls, log: Logger):
|
|
69
|
+
set_global_logger(log)
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
@log_func_call
|
|
73
|
+
def set(cls, key: str, value, case_insensitive: bool = None):
|
|
74
|
+
config_dict_set(_GLOBAL_CFG, key, value,
|
|
75
|
+
cls.get_case(case_insensitive))
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
@log_func_call(DEBUGLOW2, trace_only=True)
|
|
79
|
+
def __class_getitem__(cls, key: str):
|
|
80
|
+
return config_dict_get(_GLOBAL_CFG, key,
|
|
81
|
+
case_insensitive=cls.get_case())
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
@log_func_call
|
|
85
|
+
def set_global_config(cls, config: dict):
|
|
86
|
+
log = get_global_logger()
|
|
87
|
+
log.debug('setting global config')
|
|
88
|
+
global _GLOBAL_CFG
|
|
89
|
+
_GLOBAL_CFG = config
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
@log_func_call(DEBUGLOW2)
|
|
93
|
+
def get_global_config(cls):
|
|
94
|
+
return _GLOBAL_CFG
|
|
95
|
+
|
|
96
|
+
@classproperty
|
|
97
|
+
@log_func_call(DEBUGLOW2)
|
|
98
|
+
def global_config(cls):
|
|
99
|
+
return cls.get_global_config()
|
|
100
|
+
|
|
101
|
+
@global_config.setter
|
|
102
|
+
@log_func_call
|
|
103
|
+
def global_config(cls, config: dict):
|
|
104
|
+
cls.set_global_config(config)
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
@log_func_call(DEBUGLOW2, trace_only=True)
|
|
108
|
+
def get(cls, key: str, default=NODEFAULT,
|
|
109
|
+
case_insensitive: bool = None):
|
|
110
|
+
"""
|
|
111
|
+
If the global app config has been set, returns the value for the
|
|
112
|
+
given key if present, else returns the default if provided.
|
|
113
|
+
When no global app config is set, returns None.
|
|
114
|
+
|
|
115
|
+
If the key contains '.', it assumes that the key is a dot-delimited
|
|
116
|
+
list of keys of nested dicts or lists, and will recursively drill down
|
|
117
|
+
through the config dict to return the ultimate value.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
key (str): key to get from dict or dot-delimited list of keys
|
|
121
|
+
default (Any, optional): Value to return if key not found.
|
|
122
|
+
Defaults to NODEFAULT, which raises a KeyError if key is
|
|
123
|
+
not found.
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
KeyError: key not found in `config` or its nested children
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Any: value for key or default value (if provided) or None if the
|
|
130
|
+
global app config is not set.
|
|
131
|
+
"""
|
|
132
|
+
if _GLOBAL_CFG:
|
|
133
|
+
return config_dict_get(_GLOBAL_CFG, key, default,
|
|
134
|
+
cls.get_case(case_insensitive))
|
|
135
|
+
|
|
136
|
+
@classmethod
|
|
137
|
+
@log_func_call
|
|
138
|
+
def update(cls, data: dict, case_insensitive: bool = None):
|
|
139
|
+
config_dict_update(_GLOBAL_CFG, data, cls.get_case(case_insensitive))
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
@log_func_call
|
|
143
|
+
def build_cmd_args_from_config(cls, key: str):
|
|
144
|
+
return build_cmd_arg_list(cls[key])
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
@log_func_call
|
|
148
|
+
def process_config(cls, skip_expansion: str | list[str] = 'skip_expand',
|
|
149
|
+
config: dict = None, app_path_keys: tuple[str] = (),
|
|
150
|
+
case_insensitive: bool = None):
|
|
151
|
+
config = config or _GLOBAL_CFG
|
|
152
|
+
case = cls.get_case(case_insensitive)
|
|
153
|
+
skip_expansion = skip_expansion or ()
|
|
154
|
+
if isinstance(skip_expansion, str):
|
|
155
|
+
skip_expansion = (skip_expansion,)
|
|
156
|
+
|
|
157
|
+
for k in config.keys():
|
|
158
|
+
# print(k)
|
|
159
|
+
expand_key_recursively(config, k, skip_expansion,
|
|
160
|
+
case_insensitive=case)
|
|
161
|
+
|
|
162
|
+
base_path = get_expanded_pathobj(config[BASE_PATH_KEY], config, case)
|
|
163
|
+
config_dict_set(config, BASE_PATH_KEY, base_path, case)
|
|
164
|
+
config_dict_set(config, ABS_BASE_PATH_KEY, base_path.absolute(), case)
|
|
165
|
+
for k in get_path_keys(app_path_keys):
|
|
166
|
+
if casesafe_is_equal(k, BASE_PATH_KEY):
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
v = config_dict_get(config, k, None, case)
|
|
170
|
+
if v:
|
|
171
|
+
config_dict_set(config, k, cls.handle_path(v, base_path))
|
|
172
|
+
|
|
173
|
+
set_trace_logging(config_dict_get(config, LOG_TRACE_ENABLED_KEY,
|
|
174
|
+
case_insensitive=case))
|
|
175
|
+
set_func_call_logging(config_dict_get(config,
|
|
176
|
+
LOG_FUNC_CALL_ENABLED_KEY,
|
|
177
|
+
case_insensitive=case))
|
|
178
|
+
set_show_traceback_locals(config_dict_get(config,
|
|
179
|
+
SHOW_TRACEBACK_LOCALS_KEY,
|
|
180
|
+
case_insensitive=case))
|
|
181
|
+
return config
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
@log_func_call
|
|
185
|
+
def get_base_logpath(cls):
|
|
186
|
+
return config_dict_get(_GLOBAL_CFG, BASE_LOG_PATH_KEY,
|
|
187
|
+
case_insensitive=cls.get_case())
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
@log_func_call
|
|
191
|
+
def init_parse_config(cls, indata: dict | str | Path = None,
|
|
192
|
+
overrides: dict = None, defaults: dict = None,
|
|
193
|
+
app_global_defaults: dict = {},
|
|
194
|
+
app_local_defaults: dict = {},
|
|
195
|
+
case_insensitive: bool = None):
|
|
196
|
+
defaults = defaults or get_defaults(cls, app_global_defaults,
|
|
197
|
+
app_local_defaults)
|
|
198
|
+
|
|
199
|
+
config = deepcopy(defaults)
|
|
200
|
+
if indata:
|
|
201
|
+
config_dict_update(config, indata if isinstance(indata, dict)
|
|
202
|
+
else load_jsonc(indata), case_insensitive)
|
|
203
|
+
|
|
204
|
+
if overrides:
|
|
205
|
+
config_dict_update(config, overrides, case_insensitive)
|
|
206
|
+
|
|
207
|
+
global _GLOBAL_CFG
|
|
208
|
+
_GLOBAL_CFG = config
|
|
209
|
+
|
|
210
|
+
@classmethod
|
|
211
|
+
@log_func_call
|
|
212
|
+
def expand_log_config(cls, skip_expansion: str | list[str] = None):
|
|
213
|
+
# An abbreviated function to get the log path quickly before further
|
|
214
|
+
# expansions so further error messages can be logged.
|
|
215
|
+
# All of these keys are hardcoded, so we don't need to worry about case
|
|
216
|
+
config = _GLOBAL_CFG
|
|
217
|
+
expand_key_recursively(config, BASE_PATH_KEY, skip_expansion)
|
|
218
|
+
expand_key_recursively(config, BASE_LOG_DIR_KEY, skip_expansion)
|
|
219
|
+
expand_key_recursively(config, LOG_TIMESTAMP_KEY, skip_expansion)
|
|
220
|
+
expand_key_recursively(config, APPEND_LOG_KEY, skip_expansion)
|
|
221
|
+
expand_key_recursively(config, CLI_LOG_LEVEL_KEY, skip_expansion)
|
|
222
|
+
expand_key_recursively(config, FILE_LOG_LEVEL_KEY, skip_expansion)
|
|
223
|
+
expand_key_recursively(config, LOG_TRACE_ENABLED_KEY, skip_expansion)
|
|
224
|
+
expand_key_recursively(config, LOG_FUNC_CALL_ENABLED_KEY,
|
|
225
|
+
skip_expansion)
|
|
226
|
+
expand_key_recursively(config, SHOW_TRACEBACK_LOCALS_KEY,
|
|
227
|
+
skip_expansion)
|
|
228
|
+
base_path = get_expanded_pathobj(config[BASE_PATH_KEY], config)
|
|
229
|
+
logdir = config_dict_get(config, BASE_LOG_DIR_KEY)
|
|
230
|
+
timestamp_name = config_dict_get(config, LOG_TIMESTAMP_KEY)
|
|
231
|
+
append_log = config_dict_get(config, APPEND_LOG_KEY)
|
|
232
|
+
cli_log_level = config_dict_get(config, CLI_LOG_LEVEL_KEY)
|
|
233
|
+
file_log_level = config_dict_get(config, FILE_LOG_LEVEL_KEY)
|
|
234
|
+
log_trace_enabled = config_dict_get(config, LOG_TRACE_ENABLED_KEY)
|
|
235
|
+
log_func_call_enabled = config_dict_get(config,
|
|
236
|
+
LOG_FUNC_CALL_ENABLED_KEY)
|
|
237
|
+
tb_locals_enabled = config_dict_get(config, SHOW_TRACEBACK_LOCALS_KEY)
|
|
238
|
+
return (cls.handle_path(logdir, base_path), timestamp_name,
|
|
239
|
+
append_log, cli_log_level, file_log_level, log_trace_enabled,
|
|
240
|
+
tb_locals_enabled, log_func_call_enabled)
|
|
241
|
+
|
|
242
|
+
@classmethod
|
|
243
|
+
@log_func_call
|
|
244
|
+
def handle_path(cls, x: str, base_path: Path,
|
|
245
|
+
case_insensitive: bool = None) -> Path:
|
|
246
|
+
newppp = get_expanded_pureposixpath(x, _GLOBAL_CFG, case_insensitive)
|
|
247
|
+
newpath = pureposixpath_to_pathobj(newppp)
|
|
248
|
+
|
|
249
|
+
# any relative paths are assumed to be wrt base path for now
|
|
250
|
+
newpathstr = str(newpath)
|
|
251
|
+
if not newpath.is_absolute() and newpathstr[0] != '$':
|
|
252
|
+
if x[:2] in ('./', '.\\'):
|
|
253
|
+
newpath = get_expanded_pathobj(newpath)
|
|
254
|
+
else:
|
|
255
|
+
newpath = base_path/newpath
|
|
256
|
+
|
|
257
|
+
return newpath
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
@log_func_call
|
|
261
|
+
def get_local_config(base: dict = None, case_insensitive: bool = None):
|
|
262
|
+
from .local import get_local_config as _getlocal
|
|
263
|
+
return _getlocal(base, case_insensitive)
|
|
264
|
+
|
|
265
|
+
@staticmethod
|
|
266
|
+
@log_func_call
|
|
267
|
+
def save_local_config(app_path_keys: tuple = (),
|
|
268
|
+
case_insensitive: bool = None):
|
|
269
|
+
from .local import save_local_config as _savelocal
|
|
270
|
+
_savelocal(app_path_keys, case_insensitive)
|