cdxcore 0.1.6__py3-none-any.whl → 0.1.10__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.
Potentially problematic release.
This version of cdxcore might be problematic. Click here for more details.
- cdxcore/__init__.py +1 -9
- cdxcore/config.py +1188 -521
- cdxcore/crman.py +95 -25
- cdxcore/err.py +371 -0
- cdxcore/pretty.py +468 -0
- cdxcore/pretty.py_bak.py +750 -0
- cdxcore/subdir.py +2238 -1339
- cdxcore/uniquehash.py +515 -363
- cdxcore/util.py +358 -417
- cdxcore/verbose.py +683 -248
- cdxcore/version.py +399 -140
- cdxcore-0.1.10.dist-info/METADATA +27 -0
- cdxcore-0.1.10.dist-info/RECORD +35 -0
- {cdxcore-0.1.6.dist-info → cdxcore-0.1.10.dist-info}/top_level.txt +2 -1
- docs/source/conf.py +123 -0
- tests/test_config.py +500 -0
- tests/test_crman.py +54 -0
- tests/test_err.py +86 -0
- tests/test_pretty.py +404 -0
- tests/test_subdir.py +289 -0
- tests/test_uniquehash.py +159 -144
- tests/test_util.py +122 -83
- tests/test_verbose.py +119 -0
- tests/test_version.py +153 -0
- up/git_message.py +2 -2
- cdxcore/logger.py +0 -319
- cdxcore/prettydict.py +0 -388
- cdxcore/prettyobject.py +0 -64
- cdxcore-0.1.6.dist-info/METADATA +0 -1418
- cdxcore-0.1.6.dist-info/RECORD +0 -30
- conda/conda_exists.py +0 -10
- conda/conda_modify_yaml.py +0 -42
- tests/_cdxbasics.py +0 -1086
- {cdxcore-0.1.6.dist-info → cdxcore-0.1.10.dist-info}/WHEEL +0 -0
- {cdxcore-0.1.6.dist-info → cdxcore-0.1.10.dist-info}/licenses/LICENSE +0 -0
- {cdxcore → tmp}/deferred.py +0 -0
- {cdxcore → tmp}/dynaplot.py +0 -0
- {cdxcore → tmp}/filelock.py +0 -0
- {cdxcore → tmp}/np.py +0 -0
- {cdxcore → tmp}/npio.py +0 -0
- {cdxcore → tmp}/sharedarray.py +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cdxcore
|
|
3
|
+
Version: 0.1.10
|
|
4
|
+
Summary: Basic Python Tools; upgraded cdxbasics
|
|
5
|
+
Author-email: Hans Buehler <github@buehler.london>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/hansbuehler/cdxcore
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: numpy
|
|
14
|
+
Requires-Dist: pandas
|
|
15
|
+
Requires-Dist: matplotlib
|
|
16
|
+
Requires-Dist: sortedcontainers
|
|
17
|
+
Requires-Dist: psutil
|
|
18
|
+
Requires-Dist: jsonpickle
|
|
19
|
+
Requires-Dist: numba
|
|
20
|
+
Requires-Dist: joblib
|
|
21
|
+
Requires-Dist: blosc
|
|
22
|
+
Requires-Dist: mkdocstrings[python]>=0.18
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# cdxcore
|
|
26
|
+
Under construction.
|
|
27
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
cdxcore/__init__.py,sha256=DzMRN-6SAKCjFPYC72fGXBql03834TWP2QnjtPSb_vc,127
|
|
2
|
+
cdxcore/config.py,sha256=YnIEJVFtMZ5EHlwzaB2JsSkCPhWPvEw9QSpe6mof_V4,98472
|
|
3
|
+
cdxcore/crman.py,sha256=jOw40Bh6PkmEiabA7OS9NsqF-9viam3CBiKzPwxVkVw,5693
|
|
4
|
+
cdxcore/err.py,sha256=SIJMBXKXYI_hiysv5iSPRy0w_BbxyTDPbNEs56Y94Rk,14541
|
|
5
|
+
cdxcore/jcpool.py,sha256=OzoXWBJKWaDgJm1OeUj9ERz9skvwslGOwNq0wbKtcFM,17222
|
|
6
|
+
cdxcore/pretty.py,sha256=iUpgUCwmI8yb5O-WZFJEk3QvNYcj_GIFHUgZ5lK8F2I,17082
|
|
7
|
+
cdxcore/pretty.py_bak.py,sha256=JgWr5044HzCNGG0wKSAWlWiPRs7-bNzkwiKH0T3n0to,28658
|
|
8
|
+
cdxcore/subdir.py,sha256=ceL-Od5NAlJX3f4mYgwki5EMzbs-LpG5M2cikC8eFIE,173868
|
|
9
|
+
cdxcore/uniquehash.py,sha256=g-D8pqPIppSdRq5QfdE5aP3paZ-NkXWHfnn-uNB7fmg,50648
|
|
10
|
+
cdxcore/util.py,sha256=0fp0EzeZvnje1Q7SUcgB_JtKpsYgGTfvlHVfq0mE_ug,31930
|
|
11
|
+
cdxcore/verbose.py,sha256=nKNoZQwl3eF1zBf-JZwPC-lL9d_o5mJsDsSUMixTMLw,29882
|
|
12
|
+
cdxcore/version.py,sha256=m30oI2Ortg44dKSim-sIoeh9PioD1FWsSfVEP5rubhk,27173
|
|
13
|
+
cdxcore-0.1.10.dist-info/licenses/LICENSE,sha256=M-cisgK9kb1bqVRJ7vrCxHcMQQfDxdY3c2YFJJWfNQg,1090
|
|
14
|
+
docs/source/conf.py,sha256=Owctibh5XcSpSNcrpOr3ROIDjoklmFVrMhu8cOSe50o,4180
|
|
15
|
+
tests/test_config.py,sha256=0U9vFIKDex0Il-7Vc_C4saAuXoHIsdQ8YhhS8AO7FQI,15950
|
|
16
|
+
tests/test_crman.py,sha256=jYDxqF__iq3fEjaZQoq66CNChWRoR79Ntyng5mr3sIA,1698
|
|
17
|
+
tests/test_err.py,sha256=VbVmbaB6o49G-n3t7yuJ4M0d9pyUQyJuVDqK-xRrLo8,3458
|
|
18
|
+
tests/test_pretty.py,sha256=5TmF7c1TRDSN-YR5yo04SiLJiW3bZaxpXHJ-4ZEO8hg,11952
|
|
19
|
+
tests/test_subdir.py,sha256=tO-zoOIKQtZEMpQM-tsrisyLRmMH8txCSOzh6jPRhYY,11721
|
|
20
|
+
tests/test_uniquehash.py,sha256=ldoQLT77R7odMAok4Yo3jmiUIH3VPHKoSiSLKbbM_mo,24907
|
|
21
|
+
tests/test_util.py,sha256=DZ6AlPFDNNlkqP5MlM1BUwmBJvEj4WCFqZcdh_Isflw,19955
|
|
22
|
+
tests/test_verbose.py,sha256=7JGCLKHU1HovO6UYSLLcJQxjaZxYJejS1fl8O3Sgk9w,5037
|
|
23
|
+
tests/test_version.py,sha256=eq7bNT0GefbUkwEzo658UUxgBCiR7CDhuIxAmnnI-qQ,4658
|
|
24
|
+
tmp/deferred.py,sha256=TirvzxYXWnZkOWuEMB7qsf6auilfy4VD_zPknYCjrnw,9401
|
|
25
|
+
tmp/dynaplot.py,sha256=kwrH_WccpJcfS7n_gzdAr4QxQobvIZZrrxgdsKLKfj0,48552
|
|
26
|
+
tmp/filelock.py,sha256=HqnHZhSCESaOA3ClrdWPW_GZpyo7c3VRSEofAV-khKM,18137
|
|
27
|
+
tmp/np.py,sha256=2MynhiaTfmx984Gz7TwfZH3t7GCmCAQiyeWzDDCL6_k,47686
|
|
28
|
+
tmp/npio.py,sha256=4Kwp5H4MgKHkOEhu4UJ5CcwpM7Pm8UFkaoL5FvOEFRI,10310
|
|
29
|
+
tmp/sharedarray.py,sha256=JuHuSlxA0evD0a-bEZgTFrfdlVPMgzfQNgfSjr1212w,11484
|
|
30
|
+
up/git_message.py,sha256=EfSH7Pit3ZoCiRqSMwRCUN_QyuwreU4LTIyGSutBlm4,123
|
|
31
|
+
up/pip_modify_setup.py,sha256=Esaml4yA9tFsqxLhk5bWSwvKCURONjQqfyChgFV2TSY,1584
|
|
32
|
+
cdxcore-0.1.10.dist-info/METADATA,sha256=CR4ZGaCL36hx08u3KnBdB5TWKM8Sknk2W_KsCptAv4g,754
|
|
33
|
+
cdxcore-0.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
34
|
+
cdxcore-0.1.10.dist-info/top_level.txt,sha256=phNSwCyJFe7UP2YMoi8o6ykhotatlIbJHjTp9EHM51k,26
|
|
35
|
+
cdxcore-0.1.10.dist-info/RECORD,,
|
docs/source/conf.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
#
|
|
3
|
+
# For the full list of built-in configuration values, see the documentation:
|
|
4
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
5
|
+
|
|
6
|
+
# -- Project information -----------------------------------------------------
|
|
7
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
8
|
+
|
|
9
|
+
# conf.py
|
|
10
|
+
def set_path(source = "cdxcore"):
|
|
11
|
+
import os, sys
|
|
12
|
+
root_path = os.path.split(
|
|
13
|
+
os.path.split(
|
|
14
|
+
os.path.split( __file__ )[0] # 'source
|
|
15
|
+
)[0] # 'docs'
|
|
16
|
+
)[0] # 'packag
|
|
17
|
+
assert root_path[-len(source):] == source, f"Conf.py '{__file__}': invalid source path '{root_path}'. Call 'make html' from the docs directory"
|
|
18
|
+
sys.path.insert(0, root_path) # so your package is importable
|
|
19
|
+
|
|
20
|
+
project = 'cdxcore'
|
|
21
|
+
copyright = '2025, Hans Buehler'
|
|
22
|
+
author = 'Hans Buehler'
|
|
23
|
+
|
|
24
|
+
set_path(project)
|
|
25
|
+
|
|
26
|
+
# -- General configuration ---------------------------------------------------
|
|
27
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
28
|
+
|
|
29
|
+
extensions = [
|
|
30
|
+
"sphinx.ext.autodoc",
|
|
31
|
+
"sphinx.ext.autosummary",
|
|
32
|
+
"sphinx.ext.intersphinx",
|
|
33
|
+
"sphinx.ext.viewcode",
|
|
34
|
+
#"sphinx_autodoc_typehints",
|
|
35
|
+
"numpydoc",
|
|
36
|
+
"sphinx_automodapi.automodapi",
|
|
37
|
+
"sphinx_copybutton",
|
|
38
|
+
"sphinx_design",
|
|
39
|
+
"myst_parser", # pip install myst-parser
|
|
40
|
+
]
|
|
41
|
+
templates_path = ['_templates']
|
|
42
|
+
exclude_patterns = []
|
|
43
|
+
|
|
44
|
+
# -- Options for HTML output -------------------------------------------------
|
|
45
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
46
|
+
|
|
47
|
+
html_theme = "pydata_sphinx_theme"
|
|
48
|
+
html_theme_options = {
|
|
49
|
+
"navigation_depth": 4,
|
|
50
|
+
"show_prev_next": False,
|
|
51
|
+
"github_url": "https://github.com/hansbuehler/cdxcore", # optional
|
|
52
|
+
"show_toc_level": 2,
|
|
53
|
+
"secondary_sidebar_items": ["page-toc", "sourcelink"],
|
|
54
|
+
}
|
|
55
|
+
html_theme_options = {
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
html_static_path = ['_static']
|
|
59
|
+
html_css_files = ["custom.css"]
|
|
60
|
+
|
|
61
|
+
# Autodoc / autosummary: NumPy-like API pages
|
|
62
|
+
autosummary_generate = True
|
|
63
|
+
autodoc_default_options = {
|
|
64
|
+
"members": True,
|
|
65
|
+
"inherited-members": False,
|
|
66
|
+
"undoc-members": False,
|
|
67
|
+
"show-inheritance": False,
|
|
68
|
+
"special-members": "__call__"
|
|
69
|
+
}
|
|
70
|
+
autodoc_typehints = 'signature' # types shown in the doc body, like NumPy
|
|
71
|
+
#numpydoc_show_class_members = True
|
|
72
|
+
|
|
73
|
+
# numpydoc tweaks (keeps class doc at top, avoids member spam)
|
|
74
|
+
numpydoc_show_class_members = True
|
|
75
|
+
numpydoc_class_members_toctree = True
|
|
76
|
+
# Optional validation during build:
|
|
77
|
+
# numpydoc_validation_checks = {"all"} # or a subset like {"GL06","PR01",...}
|
|
78
|
+
|
|
79
|
+
# Cross-link to external projects (like NumPy, SciPy, pandas)
|
|
80
|
+
intersphinx_mapping = {
|
|
81
|
+
"python": ("https://docs.python.org/3", None),
|
|
82
|
+
"numpy": ("https://numpy.org/doc/stable/", None),
|
|
83
|
+
"scipy": ("https://docs.scipy.org/doc/scipy/", None),
|
|
84
|
+
"pandas": ("https://pandas.pydata.org/docs/", None),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
myst_enable_extensions = [
|
|
88
|
+
"colon_fence", # allow ::: fenced blocks
|
|
89
|
+
"deflist", # definition lists
|
|
90
|
+
"dollarmath", # $math$ and $$math$$
|
|
91
|
+
"amsmath", # AMS math environments
|
|
92
|
+
"linkify", # auto-detect bare URLs
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
source_suffix = {
|
|
96
|
+
".rst": "restructuredtext",
|
|
97
|
+
".md": "markdown",
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
from docutils import nodes
|
|
101
|
+
from sphinx.roles import XRefRole
|
|
102
|
+
|
|
103
|
+
def decorator_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
|
104
|
+
"""Custom role to display decorators with @ prefix."""
|
|
105
|
+
# Create a pending_xref node like other Sphinx cross-references
|
|
106
|
+
node = nodes.literal(text, f"@{text}", classes=["xref", "dec"])
|
|
107
|
+
node["reftype"] = "func" # decorators are functions
|
|
108
|
+
node["reftarget"] = text
|
|
109
|
+
node["refdomain"] = "py" # Python domain
|
|
110
|
+
return [node], []
|
|
111
|
+
|
|
112
|
+
from sphinx.domains.python import PyXRefRole
|
|
113
|
+
|
|
114
|
+
def setup(app):
|
|
115
|
+
# Make :dec: behave exactly like :py:func:
|
|
116
|
+
app.add_role_to_domain("py", "dec", PyXRefRole("func"))
|
|
117
|
+
return {"parallel_read_safe": True}
|
|
118
|
+
|
|
119
|
+
#def setup(app):
|
|
120
|
+
# app.add_role("dec", decorator_role)
|
|
121
|
+
# return {"parallel_read_safe": True}
|
|
122
|
+
|
|
123
|
+
|
tests/test_config.py
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Tue Apr 14 21:24:52 2020
|
|
4
|
+
@author: hansb
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import unittest as unittest
|
|
8
|
+
import dataclasses as dataclasses
|
|
9
|
+
import sys as sys
|
|
10
|
+
import os as os
|
|
11
|
+
import pickle as pickle
|
|
12
|
+
import tempfile as tempfile
|
|
13
|
+
import shutil as shutil
|
|
14
|
+
|
|
15
|
+
def import_local():
|
|
16
|
+
"""
|
|
17
|
+
In order to be able to run our tests manually from the 'tests' directory
|
|
18
|
+
we force import from the local package.
|
|
19
|
+
We also force reloading all modules to make sure we are not running old code.
|
|
20
|
+
"""
|
|
21
|
+
me = "cdxcore"
|
|
22
|
+
import os
|
|
23
|
+
import sys
|
|
24
|
+
cwd = os.getcwd()
|
|
25
|
+
if cwd[-len(me):] == me:
|
|
26
|
+
return
|
|
27
|
+
assert cwd[-5:] == "tests",("Expected current working directory to be in a 'tests' directory", cwd[-5:], "from", cwd)
|
|
28
|
+
assert cwd[-6] in ['/', '\\'],("Expected current working directory 'tests' to be lead by a '\\' or '/'", cwd[-6:], "from", cwd)
|
|
29
|
+
sys.path.insert( 0, cwd[:-6] )
|
|
30
|
+
|
|
31
|
+
# reload modules
|
|
32
|
+
import importlib as imp
|
|
33
|
+
modules = sys.modules.copy()
|
|
34
|
+
for name, mdata in modules.items():
|
|
35
|
+
if name[:len(me)] == me:
|
|
36
|
+
imp.reload(mdata)
|
|
37
|
+
print("Reloaded", name)
|
|
38
|
+
#import_local()
|
|
39
|
+
|
|
40
|
+
from cdxcore.config import Config, Int, Float
|
|
41
|
+
from cdxcore.pretty import PrettyObject as pdct
|
|
42
|
+
from cdxcore.uniquehash import unique_hash16
|
|
43
|
+
|
|
44
|
+
class Test(unittest.TestCase):
|
|
45
|
+
|
|
46
|
+
def test_config(self):
|
|
47
|
+
|
|
48
|
+
config = Config(x=0., z=-1.)
|
|
49
|
+
x = config("x", 10., float, "test x")
|
|
50
|
+
self.assertEqual( x, 0. )
|
|
51
|
+
y = config("y", 10., float, "test y")
|
|
52
|
+
self.assertEqual( y, 10. )
|
|
53
|
+
|
|
54
|
+
with self.assertRaises(Exception):
|
|
55
|
+
# 'z' was not read
|
|
56
|
+
config.done()
|
|
57
|
+
|
|
58
|
+
# calling twice with different values
|
|
59
|
+
config = Config(x=0.)
|
|
60
|
+
x = config("x", 1., float, "test x")
|
|
61
|
+
x = config("x", 1., float, "test x") # ok: same parameters
|
|
62
|
+
with self.assertRaises(Exception):
|
|
63
|
+
x = config("x", 1., Float<0.5, "test x") # not ok: Float condition
|
|
64
|
+
with self.assertRaises(Exception):
|
|
65
|
+
x = config("x", 2., float, "test x") # not ok: different default
|
|
66
|
+
config.done()
|
|
67
|
+
|
|
68
|
+
# test usage per access method:
|
|
69
|
+
# __call__('a')
|
|
70
|
+
# get('a')
|
|
71
|
+
# get_default('a', ...)
|
|
72
|
+
# all register usage;
|
|
73
|
+
# get_raw('a')
|
|
74
|
+
# ['a']
|
|
75
|
+
# do not.
|
|
76
|
+
config = Config(a=1)
|
|
77
|
+
_ = config.get("a")
|
|
78
|
+
self.assertTrue( not 'a' in config.not_done )
|
|
79
|
+
config = Config(a=1)
|
|
80
|
+
_ = config.get_default("a", 0)
|
|
81
|
+
self.assertTrue( not 'a' in config.not_done )
|
|
82
|
+
config = Config(a=1)
|
|
83
|
+
_ = config("a")
|
|
84
|
+
self.assertTrue( not 'a' in config.not_done )
|
|
85
|
+
config = Config(a=1)
|
|
86
|
+
_ = config("a", 0)
|
|
87
|
+
self.assertTrue( not 'a' in config.not_done )
|
|
88
|
+
|
|
89
|
+
config = Config(a=1)
|
|
90
|
+
_ = config.get_raw('a')
|
|
91
|
+
self.assertTrue( 'a' in config.not_done )
|
|
92
|
+
config = Config(a=1)
|
|
93
|
+
_ = config['a']
|
|
94
|
+
self.assertTrue( 'a' in config.not_done )
|
|
95
|
+
|
|
96
|
+
# test sub configs
|
|
97
|
+
config = Config()
|
|
98
|
+
config.x = 1
|
|
99
|
+
config.a = "a"
|
|
100
|
+
config.sub.x = 2.
|
|
101
|
+
|
|
102
|
+
self.assertEqual(1., config("x", 0., float, "x"))
|
|
103
|
+
self.assertEqual("a", config("a", None, str, "a"))
|
|
104
|
+
self.assertEqual(2, config.sub("x", 0, int, "x"))
|
|
105
|
+
self.assertTrue( isinstance( config.sub, Config ) )
|
|
106
|
+
config.done()
|
|
107
|
+
|
|
108
|
+
# test detach
|
|
109
|
+
config = Config()
|
|
110
|
+
config.sub.x = 1
|
|
111
|
+
with self.assertRaises(Exception):
|
|
112
|
+
config.done() # 'sub.x' not read
|
|
113
|
+
|
|
114
|
+
config = Config()
|
|
115
|
+
config.sub.x = 1
|
|
116
|
+
sub = config.sub.detach()
|
|
117
|
+
config.done() # ok
|
|
118
|
+
_ = sub("x", 1)
|
|
119
|
+
config.done() # fine now
|
|
120
|
+
|
|
121
|
+
# test list (_Enum)
|
|
122
|
+
config = Config(t="a", q="q")
|
|
123
|
+
_ = config("t", "b", ['a', 'b', 'c'] )
|
|
124
|
+
self.assertEqual(_, 'a')
|
|
125
|
+
with self.assertRaises(Exception):
|
|
126
|
+
_ = config("q", "b", ['a', 'b', 'c'] ) # exception: 'q' not in set
|
|
127
|
+
|
|
128
|
+
# test tuple (_Alt)
|
|
129
|
+
config = Config(t="a")
|
|
130
|
+
_ = config("t", "b", (None, str) )
|
|
131
|
+
self.assertEqual(_, 'a')
|
|
132
|
+
config = Config(t=None)
|
|
133
|
+
_ = config("t", "b", (None, str) )
|
|
134
|
+
self.assertEqual(_, None)
|
|
135
|
+
with self.assertRaises(Exception):
|
|
136
|
+
config = Config(t="a")
|
|
137
|
+
_ = config("t", 1, (None, int) )
|
|
138
|
+
self.assertEqual(_, None)
|
|
139
|
+
config = Config()
|
|
140
|
+
_ = config("t", "b", (None, ['a','b']) )
|
|
141
|
+
self.assertEqual(_, 'b')
|
|
142
|
+
config = Config(t=2)
|
|
143
|
+
_ = config("t", 1, (Int>=1, Int<=1) )
|
|
144
|
+
self.assertEqual(_, 2)
|
|
145
|
+
config = Config()
|
|
146
|
+
_ = config("t", 3, (Int>=1, Int<=1) )
|
|
147
|
+
self.assertEqual(_, 3)
|
|
148
|
+
with self.assertRaises(Exception):
|
|
149
|
+
config = Config()
|
|
150
|
+
_ = config("t", 0, (Int>=1, Int<=-1) )
|
|
151
|
+
with self.assertRaises(Exception):
|
|
152
|
+
config = Config(t=0)
|
|
153
|
+
_ = config("t", 3, (Int>=1, Int<=-1) )
|
|
154
|
+
|
|
155
|
+
# combined conditons
|
|
156
|
+
config = Config(x=1., y=1.)
|
|
157
|
+
|
|
158
|
+
x = config("x", 1., ( Float>=0.) & (Float<=1.), "test x")
|
|
159
|
+
with self.assertRaises(Exception):
|
|
160
|
+
# test that violated condition is caught
|
|
161
|
+
y = config("y", 1., ( Float>=0.) & (Float<1.), "test y")
|
|
162
|
+
|
|
163
|
+
config = Config(x=1., y=1.)
|
|
164
|
+
with self.assertRaises(NotImplementedError):
|
|
165
|
+
# left hand must be > or >=
|
|
166
|
+
y = config("y", 1., ( Float<=0.) & (Float<1.), "test x")
|
|
167
|
+
config = Config(x=1., y=1.)
|
|
168
|
+
with self.assertRaises(NotImplementedError):
|
|
169
|
+
# right hand must be < or <=
|
|
170
|
+
y = config("y", 1., ( Float>=0.) & (Float>1.), "test x")
|
|
171
|
+
|
|
172
|
+
# test int
|
|
173
|
+
config = Config(x=1)
|
|
174
|
+
x = config("x", 0, ( Int>=0 ) & ( Int<=1), "int test")
|
|
175
|
+
config = Config(x=1)
|
|
176
|
+
with self.assertRaises(NotImplementedError):
|
|
177
|
+
# cannot mix types
|
|
178
|
+
x = config("x", 1., ( Float>=0.) & (Int<=1), "test x")
|
|
179
|
+
|
|
180
|
+
# test deleting children
|
|
181
|
+
config = Config()
|
|
182
|
+
config.a.x = 1
|
|
183
|
+
config.b.x = 2
|
|
184
|
+
config.c.x = 3
|
|
185
|
+
config.delete_children( 'a' )
|
|
186
|
+
l = sorted( config.children )
|
|
187
|
+
self.assertEqual(l, ['b', 'c'])
|
|
188
|
+
config = Config()
|
|
189
|
+
config.a.x = 1
|
|
190
|
+
config.b.x = 2
|
|
191
|
+
config.c.x = 3
|
|
192
|
+
config.delete_children( ['a','b'] )
|
|
193
|
+
l = sorted( config.children )
|
|
194
|
+
self.assertEqual(l, ['c'])
|
|
195
|
+
|
|
196
|
+
# test conversion to dictionaries
|
|
197
|
+
config = Config()
|
|
198
|
+
config.x = 1
|
|
199
|
+
config.y = 2
|
|
200
|
+
config.sub.x = 10
|
|
201
|
+
config.sub.y = 20
|
|
202
|
+
inp_dict = config.input_dict()
|
|
203
|
+
|
|
204
|
+
test = pdct()
|
|
205
|
+
test.x = 1
|
|
206
|
+
test.y = 2
|
|
207
|
+
test.sub = pdct()
|
|
208
|
+
test.sub.x = 10
|
|
209
|
+
test.sub.y = 20
|
|
210
|
+
|
|
211
|
+
self.assertEqual( test, inp_dict)
|
|
212
|
+
|
|
213
|
+
# clean_copy
|
|
214
|
+
config = Config()
|
|
215
|
+
config.gym.user_version = 1
|
|
216
|
+
config.gym.world_character_id = 2
|
|
217
|
+
config.gym.vol_model.type = "decoder"
|
|
218
|
+
_ = config.clean_copy()
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
test = PrettyDict()
|
|
222
|
+
test.x = config("x", 1)
|
|
223
|
+
test.y = config("y", 22)
|
|
224
|
+
test.z = config("z", 33)
|
|
225
|
+
test.sub = PrettyDict()
|
|
226
|
+
test.sub.x = config.sub("x", 10)
|
|
227
|
+
test.sub.y = config.sub("y", 222)
|
|
228
|
+
test.sub.z = config.sub("z", 333)
|
|
229
|
+
usd_dict = config.usage_dict()
|
|
230
|
+
self.assertEqual( usd_dict, test )
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
# test keys()
|
|
234
|
+
|
|
235
|
+
config = Config()
|
|
236
|
+
config.a = 1
|
|
237
|
+
config.x.b = 2
|
|
238
|
+
keys = list(config)
|
|
239
|
+
sorted(keys)
|
|
240
|
+
self.assertEqual( keys, ['a'])
|
|
241
|
+
keys = list(config.keys())
|
|
242
|
+
sorted(keys)
|
|
243
|
+
self.assertEqual( keys, ['a'])
|
|
244
|
+
|
|
245
|
+
# test update
|
|
246
|
+
|
|
247
|
+
config = Config()
|
|
248
|
+
config.a = 1
|
|
249
|
+
config.x.a = 1
|
|
250
|
+
config.z.a =1
|
|
251
|
+
|
|
252
|
+
config2 = Config()
|
|
253
|
+
config2.b = 2
|
|
254
|
+
config2.x.a = 2
|
|
255
|
+
config2.x.b = 2
|
|
256
|
+
config2.y.b = 2
|
|
257
|
+
config2.z = 2
|
|
258
|
+
config.update( config2 )
|
|
259
|
+
ur1 = config.input_report()
|
|
260
|
+
|
|
261
|
+
econfig = Config()
|
|
262
|
+
econfig.a = 1
|
|
263
|
+
econfig.b = 2
|
|
264
|
+
econfig.x.a = 2
|
|
265
|
+
econfig.x.b = 2
|
|
266
|
+
econfig.y.b = 2
|
|
267
|
+
econfig.z = 2
|
|
268
|
+
ur2 = econfig.input_report()
|
|
269
|
+
self.assertEqual( ur1, ur2 )
|
|
270
|
+
|
|
271
|
+
config = Config()
|
|
272
|
+
config.a = 1
|
|
273
|
+
config.x.a = 1
|
|
274
|
+
|
|
275
|
+
d = dict(b=2,x=dict(a=2,b=2),y=dict(b=2),z=2)
|
|
276
|
+
config.update(d)
|
|
277
|
+
ur2 = econfig.input_report()
|
|
278
|
+
self.assertEqual( ur1, ur2 )
|
|
279
|
+
|
|
280
|
+
# test str and repr
|
|
281
|
+
|
|
282
|
+
config = Config()
|
|
283
|
+
config.x = 1
|
|
284
|
+
config.y = 2
|
|
285
|
+
config.sub.x = 10
|
|
286
|
+
config.sub.y = 20
|
|
287
|
+
|
|
288
|
+
self.assertEqual( str(config), "config{'x': 1, 'y': 2, 'sub': {'x': 10, 'y': 20}}")
|
|
289
|
+
self.assertEqual( repr(config), "Config( **{'x': 1, 'y': 2, 'sub': {'x': 10, 'y': 20}}, config_name='config' )")
|
|
290
|
+
|
|
291
|
+
# test recorded usage
|
|
292
|
+
|
|
293
|
+
config = Config()
|
|
294
|
+
config.x = 1
|
|
295
|
+
config.sub.a = 1
|
|
296
|
+
config.det.o = 1
|
|
297
|
+
|
|
298
|
+
_ = config("x", 11)
|
|
299
|
+
_ = config("y", 22)
|
|
300
|
+
_ = config.sub("a", 11)
|
|
301
|
+
_ = config.sub("b", 22)
|
|
302
|
+
det = config.det.detach() # shares the same recorder !
|
|
303
|
+
_ = det("o", 11)
|
|
304
|
+
_ = det("p", 22)
|
|
305
|
+
|
|
306
|
+
self.assertEqual( config.get_recorded("x"), 1)
|
|
307
|
+
self.assertEqual( config.get_recorded("y"), 22)
|
|
308
|
+
self.assertEqual( config.sub.get_recorded("a"), 1)
|
|
309
|
+
self.assertEqual( config.sub.get_recorded("b"), 22)
|
|
310
|
+
self.assertEqual( config.det.get_recorded("o"), 1)
|
|
311
|
+
self.assertEqual( config.det.get_recorded("p"), 22)
|
|
312
|
+
|
|
313
|
+
# unique ID
|
|
314
|
+
|
|
315
|
+
config = Config()
|
|
316
|
+
# world
|
|
317
|
+
config.world.samples = 10000
|
|
318
|
+
config.world.steps = 20
|
|
319
|
+
config.world.black_scholes = True
|
|
320
|
+
config.world.rvol = 0.2 # 20% volatility
|
|
321
|
+
config.world.drift = 0. # real life drift
|
|
322
|
+
config.world.cost_s = 0.
|
|
323
|
+
# gym
|
|
324
|
+
config.gym.objective.utility = "cvar"
|
|
325
|
+
config.gym.objective.lmbda = 1.
|
|
326
|
+
config.gym.agent.network.depth = 6
|
|
327
|
+
config.gym.agent.network.width = 40
|
|
328
|
+
config.gym.agent.network.activation = "softplus"
|
|
329
|
+
# trainer
|
|
330
|
+
config.trainer.train.optimizer = "adam"
|
|
331
|
+
config.trainer.train.batch_size = None
|
|
332
|
+
config.trainer.train.epochs = 400
|
|
333
|
+
config.trainer.caching.epoch_freq = 10
|
|
334
|
+
config.trainer.caching.mode = "on"
|
|
335
|
+
config.trainer.visual.epoch_refresh = 1
|
|
336
|
+
config.trainer.visual.time_refresh = 10
|
|
337
|
+
config.trainer.visual.confidence_pcnt_lo = 0.25
|
|
338
|
+
config.trainer.visual.confidence_pcnt_hi = 0.75
|
|
339
|
+
|
|
340
|
+
id1 = config.unique_hash()
|
|
341
|
+
|
|
342
|
+
config = Config()
|
|
343
|
+
# world
|
|
344
|
+
config.world.samples = 10000
|
|
345
|
+
config.world.steps = 20
|
|
346
|
+
config.world.black_scholes = True
|
|
347
|
+
config.world.rvol = 0.2 # 20% volatility
|
|
348
|
+
config.world.drift = 0. # real life drift
|
|
349
|
+
config.world.cost_s = 0.
|
|
350
|
+
# gym
|
|
351
|
+
config.gym.objective.utility = "cvar"
|
|
352
|
+
config.gym.objective.lmbda = 1.
|
|
353
|
+
config.gym.agent.network.depth = 5 # <====== changed this
|
|
354
|
+
config.gym.agent.network.width = 40
|
|
355
|
+
config.gym.agent.network.activation = "softplus"
|
|
356
|
+
# trainer
|
|
357
|
+
config.trainer.train.optimizer = "adam"
|
|
358
|
+
config.trainer.train.batch_size = None
|
|
359
|
+
config.trainer.train.epochs = 400
|
|
360
|
+
config.trainer.caching.epoch_freq = 10
|
|
361
|
+
config.trainer.caching.mode = "on"
|
|
362
|
+
config.trainer.visual.epoch_refresh = 1
|
|
363
|
+
config.trainer.visual.time_refresh = 10
|
|
364
|
+
config.trainer.visual.confidence_pcnt_lo = 0.25
|
|
365
|
+
config.trainer.visual.confidence_pcnt_hi = 0.75
|
|
366
|
+
|
|
367
|
+
id2 = config.unique_hash()
|
|
368
|
+
self.assertNotEqual(id1,id2)
|
|
369
|
+
self.assertEqual(id2,"cfef59b69770d0a973342ad68f38fba2")
|
|
370
|
+
|
|
371
|
+
_ = config.nothing("get_nothing", 0) # this triggered a new ID in old versions
|
|
372
|
+
|
|
373
|
+
id3 = config.unique_hash()
|
|
374
|
+
self.assertEqual(id2,id3)
|
|
375
|
+
|
|
376
|
+
idempty = Config().unique_hash()
|
|
377
|
+
self.assertEqual(idempty,"64550d6ffe2c0a01a14aba1eade0200c")
|
|
378
|
+
self.assertNotEqual(idempty,id3)
|
|
379
|
+
|
|
380
|
+
# pickle test
|
|
381
|
+
|
|
382
|
+
binary = pickle.dumps(config)
|
|
383
|
+
restored = pickle.loads(binary)
|
|
384
|
+
idrest = restored.unique_hash()
|
|
385
|
+
self.assertEqual(idrest,id2)
|
|
386
|
+
|
|
387
|
+
# unique ID test
|
|
388
|
+
|
|
389
|
+
config1 = Config()
|
|
390
|
+
config1.x = 1
|
|
391
|
+
config1.sub.y = 2
|
|
392
|
+
config2 = Config()
|
|
393
|
+
config2.x = 1
|
|
394
|
+
config2.sub.y = 3
|
|
395
|
+
self.assertNotEqual( unique_hash16(config1), unique_hash16(config2) )
|
|
396
|
+
|
|
397
|
+
config1 = Config()
|
|
398
|
+
config1.x = 1
|
|
399
|
+
config1.sub.y = 2
|
|
400
|
+
config2 = Config()
|
|
401
|
+
config2.x = 2
|
|
402
|
+
config2.sub.y = 2
|
|
403
|
+
self.assertNotEqual( unique_hash16(config1), unique_hash16(config2) )
|
|
404
|
+
|
|
405
|
+
config1 = Config()
|
|
406
|
+
config1.x = 1
|
|
407
|
+
config1.sub.y = 2
|
|
408
|
+
config2 = Config()
|
|
409
|
+
config2.x = 1
|
|
410
|
+
config2.sub.y = 2
|
|
411
|
+
self.assertEqual( unique_hash16(config1), unique_hash16(config2) )
|
|
412
|
+
|
|
413
|
+
# unique_hash16() ignores protected and private members
|
|
414
|
+
config1 = Config()
|
|
415
|
+
config1.x = 1
|
|
416
|
+
config1.sub._y = 2
|
|
417
|
+
config2 = Config()
|
|
418
|
+
config2.x = 1
|
|
419
|
+
config2.sub._y = 3
|
|
420
|
+
self.assertEqual( unique_hash16(config1), unique_hash16(config2) )
|
|
421
|
+
|
|
422
|
+
def test_detach(self):
|
|
423
|
+
""" testing detach/copy/clean_cooy """
|
|
424
|
+
|
|
425
|
+
config = Config(a=1,b=2)
|
|
426
|
+
config.child.x = 1
|
|
427
|
+
_ = config("a", 2)
|
|
428
|
+
c1 = config.detach()
|
|
429
|
+
with self.assertRaises(Exception):
|
|
430
|
+
_ = c1("a", 1) # different default
|
|
431
|
+
_ = c1("b", 3)
|
|
432
|
+
with self.assertRaises(Exception):
|
|
433
|
+
_ = config("b", 2) # different default
|
|
434
|
+
|
|
435
|
+
config = Config(a=1,b=2)
|
|
436
|
+
config.child.x = 1
|
|
437
|
+
_ = config("a", 2)
|
|
438
|
+
c1 = config.copy()
|
|
439
|
+
with self.assertRaises(Exception):
|
|
440
|
+
_ = c1("a", 1) # different default
|
|
441
|
+
_ = c1("b", 3)
|
|
442
|
+
_ = config("b", 2) # different default - ok
|
|
443
|
+
|
|
444
|
+
config = Config(a=1,b=2)
|
|
445
|
+
config.child.x = 1
|
|
446
|
+
_ = config("a", 2)
|
|
447
|
+
c1 = config.clean_copy()
|
|
448
|
+
_ = c1("a", 1) # different default - ok
|
|
449
|
+
_ = c1("b", 3)
|
|
450
|
+
_ = config("b", 2) # different default - ok
|
|
451
|
+
|
|
452
|
+
def test_dataclass(self):
|
|
453
|
+
|
|
454
|
+
@dataclasses.dataclass
|
|
455
|
+
class A:
|
|
456
|
+
i : int = 0
|
|
457
|
+
config : Config = Config().as_field()
|
|
458
|
+
|
|
459
|
+
def f(self):
|
|
460
|
+
return self.config("a", 1, int, "Test")
|
|
461
|
+
|
|
462
|
+
a = A()
|
|
463
|
+
self.assertEqual(a.f(),1)
|
|
464
|
+
c = Config()
|
|
465
|
+
a = A(i=2,config=Config(c))
|
|
466
|
+
self.assertEqual(a.f(),1)
|
|
467
|
+
c = Config(a=2)
|
|
468
|
+
a = A(i=2,config=Config(c))
|
|
469
|
+
self.assertEqual(a.f(),2)
|
|
470
|
+
a = A(i=2,config=Config(a=2))
|
|
471
|
+
self.assertEqual(a.f(),2)
|
|
472
|
+
|
|
473
|
+
def test_io(self):
|
|
474
|
+
|
|
475
|
+
config = Config(x=1)
|
|
476
|
+
config.child.y = 2
|
|
477
|
+
config._test = 33
|
|
478
|
+
|
|
479
|
+
try:
|
|
480
|
+
tmp_dir = tempfile.mkdtemp()
|
|
481
|
+
self.assertNotEqual(tmp_dir[-1],"/")
|
|
482
|
+
self.assertNotEqual(tmp_dir[-1],"\\")
|
|
483
|
+
tmp_file = tmp_dir + "/test_pretty_object.pck"
|
|
484
|
+
|
|
485
|
+
with open(tmp_file, "wb") as f:
|
|
486
|
+
pickle.dump(config,f)
|
|
487
|
+
|
|
488
|
+
with open(tmp_file, "rb") as f:
|
|
489
|
+
config2 = pickle.load(f)
|
|
490
|
+
self.assertEqual(config,config2)
|
|
491
|
+
|
|
492
|
+
os.remove(tmp_file)
|
|
493
|
+
finally:
|
|
494
|
+
shutil.rmtree(tmp_dir)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
if __name__ == '__main__':
|
|
498
|
+
unittest.main()
|
|
499
|
+
|
|
500
|
+
|