diff --git a/404.html b/404.html
index 4c1331bf..4ee55674 100644
--- a/404.html
+++ b/404.html
@@ -34,7 +34,7 @@
-
+
@@ -42,7 +42,7 @@
-
+
diff --git a/assets/stylesheets/main.046329b4.min.css b/assets/stylesheets/main.046329b4.min.css
deleted file mode 100644
index 77ac5aae..00000000
--- a/assets/stylesheets/main.046329b4.min.css
+++ /dev/null
@@ -1 +0,0 @@
-@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}@media (prefers-reduced-motion){*,:after,:before{transition:none!important}}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:initial;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:#0000;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-scheme=default]{color-scheme:light}[data-md-color-scheme=default] img[src$="#gh-dark-mode-only"],[data-md-color-scheme=default] img[src$="#only-dark"]{display:none}:root,[data-md-color-scheme=default]{--md-default-fg-color:#000000de;--md-default-fg-color--light:#0000008a;--md-default-fg-color--lighter:#00000052;--md-default-fg-color--lightest:#00000012;--md-default-bg-color:#fff;--md-default-bg-color--light:#ffffffb3;--md-default-bg-color--lighter:#ffffff4d;--md-default-bg-color--lightest:#ffffff1f;--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:#ffff0080;--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-mark-color:#ffff0080;--md-typeset-del-color:#f5503d26;--md-typeset-ins-color:#0bd57026;--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-table-color:#0000001f;--md-typeset-table-color--light:rgba(0,0,0,.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-warning-fg-color:#000000de;--md-warning-bg-color:#ff9;--md-footer-fg-color:#fff;--md-footer-fg-color--light:#ffffffb3;--md-footer-fg-color--lighter:#ffffff73;--md-footer-bg-color:#000000de;--md-footer-bg-color--dark:#00000052;--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #0000001a,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0003,0 0 0.05rem #00000059}.md-icon svg{fill:currentcolor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--md-text-font-family:var(--md-text-font,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;--md-code-font-family:var(--md-code-font,_),SFMono-Regular,Consolas,Menlo,monospace}aside,body,input{font-feature-settings:"kern","liga";color:var(--md-typeset-color);font-family:var(--md-text-font-family)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family)}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin-bottom:1em;margin-top:1em}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a:focus code,.md-typeset a:hover code{background-color:var(--md-accent-fg-color--transparent)}.md-typeset a code{color:currentcolor;transition:background-color 125ms}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr;font-variant-ligatures:none}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;outline-color:var(--md-accent-fg-color);overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help;text-decoration:none}@media (hover:none){.md-typeset abbr[title]:focus:after,.md-typeset abbr[title]:hover:after{background-color:var(--md-default-fg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z3);color:var(--md-default-bg-color);content:attr(title);font-size:.7rem;left:.8rem;margin-top:2em;padding:.2rem .3rem;position:absolute;right:.8rem}}.md-typeset small{opacity:.75}[dir=ltr] .md-typeset sub,[dir=ltr] .md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em}[dir=ltr] .md-typeset blockquote{padding-left:.6rem}[dir=rtl] .md-typeset blockquote{padding-right:.6rem}[dir=ltr] .md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{border-right:.2rem solid var(--md-default-fg-color--lighter)}.md-typeset blockquote{color:var(--md-default-fg-color--light);margin-left:0;margin-right:0}.md-typeset ul{list-style-type:disc}[dir=ltr] .md-typeset ol,[dir=ltr] .md-typeset ul{margin-left:.625em}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em}.md-typeset ol,.md-typeset ul{padding:0}.md-typeset ol:not([hidden]),.md-typeset ul:not([hidden]){display:flow-root}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}[dir=ltr] .md-typeset ol li,[dir=ltr] .md-typeset ul li{margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}[dir=ltr] .md-typeset ol li ol,[dir=ltr] .md-typeset ol li ul,[dir=ltr] .md-typeset ul li ol,[dir=ltr] .md-typeset ul li ul{margin-left:.625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin-bottom:.5em;margin-top:.5em}[dir=ltr] .md-typeset dd{margin-left:1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em}.md-typeset dd{margin-bottom:1.5em;margin-top:1em}.md-typeset img,.md-typeset svg,.md-typeset video{height:auto;max-width:100%}.md-typeset img[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:1em auto;max-width:100%;text-align:center;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block}.md-typeset figcaption{font-style:italic;margin:1em auto;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:var(--md-typeset-table-color--light);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}[dir=ltr] .md-typeset table th[role=columnheader]:after{margin-left:.5em}[dir=rtl] .md-typeset table th[role=columnheader]:after{margin-right:.5em}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}@media screen and (max-width:44.9375em){.md-content__inner>pre{margin:1em -.8rem}.md-content__inner>pre code{border-radius:0}}.md-typeset .md-author{display:block;flex-shrink:0;height:1.6rem;overflow:hidden;position:relative;transition:color 125ms,transform 125ms;width:1.6rem}.md-typeset .md-author img{border-radius:100%;display:block}.md-typeset .md-author--more{background:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--lighter);font-size:.6rem;font-weight:700;line-height:1.6rem;text-align:center}.md-typeset .md-author--long{height:2.4rem;width:2.4rem}.md-typeset a.md-author{transform:scale(1)}.md-typeset a.md-author img{filter:grayscale(100%) opacity(75%);transition:filter 125ms}.md-typeset a.md-author:focus,.md-typeset a.md-author:hover{transform:scale(1.1);z-index:1}.md-typeset a.md-author:focus img,.md-typeset a.md-author:hover img{filter:grayscale(0)}.md-banner{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color);overflow:auto}@media print{.md-banner{display:none}}.md-banner--warning{background-color:var(--md-warning-bg-color);color:var(--md-warning-fg-color)}.md-banner__inner{font-size:.7rem;margin:.6rem auto;padding:0 .8rem}[dir=ltr] .md-banner__button{float:right}[dir=rtl] .md-banner__button{float:left}.md-banner__button{color:inherit;cursor:pointer;transition:opacity .25s}.no-js .md-banner__button{display:none}.md-banner__button:hover{opacity:.7}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.9375em){body[data-md-scrolllock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;position:absolute;right:.5em;top:.5em;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}@keyframes consent{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes overlay{0%{opacity:0}to{opacity:1}}.md-consent__overlay{animation:overlay .25s both;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);background-color:#0000008a;height:100%;opacity:1;position:fixed;top:0;width:100%;z-index:5}.md-consent__inner{animation:consent .5s cubic-bezier(.1,.7,.1,1) both;background-color:var(--md-default-bg-color);border:0;border-radius:.1rem;bottom:0;box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;max-height:100%;overflow:auto;padding:0;position:fixed;width:100%;z-index:5}.md-consent__form{padding:.8rem}.md-consent__settings{display:none;margin:1em 0}input:checked+.md-consent__settings{display:block}.md-consent__controls{margin-bottom:.8rem}.md-typeset .md-consent__controls .md-button{display:inline}@media screen and (max-width:44.9375em){.md-typeset .md-consent__controls .md-button{display:block;margin-top:.4rem;text-align:center;width:100%}}.md-consent label{cursor:pointer}.md-content{flex-grow:1;min-width:0}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){[dir=ltr] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=ltr] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner,[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}[dir=ltr] .md-content__button{float:right}[dir=rtl] .md-content__button{float:left}[dir=ltr] .md-content__button{margin-left:.4rem}[dir=rtl] .md-content__button{margin-right:.4rem}.md-content__button{margin:.4rem 0;padding:0}@media print{.md-content__button{display:none}}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}[dir=ltr] .md-dialog{right:.8rem}[dir=rtl] .md-dialog{left:.8rem}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:var(--md-shadow-z3);min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:4}@media print{.md-dialog{display:none}}.md-dialog--active{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-feedback{margin:2em 0 1em;text-align:center}.md-feedback fieldset{border:none;margin:0;padding:0}.md-feedback__title{font-weight:700;margin:1em auto}.md-feedback__inner{position:relative}.md-feedback__list{align-content:baseline;display:flex;flex-wrap:wrap;justify-content:center;position:relative}.md-feedback__list:hover .md-icon:not(:disabled){color:var(--md-default-fg-color--lighter)}:disabled .md-feedback__list{min-height:1.8rem}.md-feedback__icon{color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;margin:0 .1rem;transition:color 125ms}.md-feedback__icon:not(:disabled).md-icon:hover{color:var(--md-accent-fg-color)}.md-feedback__icon:disabled{color:var(--md-default-fg-color--lightest);pointer-events:none}.md-feedback__note{opacity:0;position:relative;transform:translateY(.4rem);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-feedback__note>*{margin:0 auto;max-width:16rem}:disabled .md-feedback__note{opacity:1;transform:translateY(0)}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{justify-content:space-between;overflow:auto;padding:.2rem}.md-footer__inner:not([hidden]){display:flex}.md-footer__link{align-items:end;display:flex;flex-grow:0.01;margin-bottom:.4rem;margin-top:1rem;max-width:100%;outline-color:var(--md-accent-fg-color);overflow:hidden;transition:opacity .25s}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}[dir=rtl] .md-footer__link svg{transform:scaleX(-1)}@media screen and (max-width:44.9375em){.md-footer__link--prev{flex-shrink:0}.md-footer__link--prev .md-footer__title{display:none}}[dir=ltr] .md-footer__link--next{margin-left:auto}[dir=rtl] .md-footer__link--next{margin-right:auto}.md-footer__link--next{text-align:right}[dir=rtl] .md-footer__link--next{text-align:left}.md-footer__title{flex-grow:1;font-size:.9rem;margin-bottom:.7rem;max-width:calc(100% - 2.4rem);padding:0 1rem;white-space:nowrap}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-copyright{width:auto}}.md-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-social{display:inline-flex;gap:.2rem;margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-social{padding:.6rem 0}}.md-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-social__link:before{line-height:1.9}.md-social__link svg{fill:currentcolor;max-height:.8rem;vertical-align:-25%}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[dir=ltr] .md-typeset .md-input{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .md-input,[dir=rtl] .md-typeset .md-input{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .md-input{border-top-left-radius:.1rem}.md-typeset .md-input{border-bottom:.1rem solid var(--md-default-fg-color--lighter);box-shadow:var(--md-shadow-z1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:border .25s,box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{border-bottom-color:var(--md-accent-fg-color);box-shadow:var(--md-shadow-z2)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem #0000,0 .2rem .4rem #0000;color:var(--md-primary-bg-color);display:block;left:0;position:sticky;right:0;top:0;z-index:4}@media print{.md-header{display:none}}.md-header[hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header--shadow{box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentcolor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.1875em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentcolor;display:block;height:1.2rem;width:auto}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;white-space:nowrap}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__topic:first-child{font-weight:700}[dir=ltr] .md-header__title{margin-left:1rem}[dir=rtl] .md-header__title{margin-right:1rem}[dir=ltr] .md-header__title{margin-right:.4rem}[dir=rtl] .md-header__title{margin-left:.4rem}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem}.md-header__title--active .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title--active .md-header__topic{transform:translateX(1.25rem)}.md-header__title--active .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__option>input{bottom:0}.md-header__source{display:none}@media screen and (min-width:60em){[dir=ltr] .md-header__source{margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem}.md-header__source{display:block;max-width:11.7rem;width:11.7rem}}@media screen and (min-width:76.25em){[dir=ltr] .md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-meta{color:var(--md-default-fg-color--light);font-size:.7rem;line-height:1.3}.md-meta__list{display:inline-flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.md-meta__item:not(:last-child):after{content:"·";margin-left:.2rem;margin-right:.2rem}.md-meta__link{color:var(--md-typeset-a-color)}.md-meta__link:focus,.md-meta__link:hover{color:var(--md-accent-fg-color)}.md-draft{background-color:#ff1744;border-radius:.125em;color:#fff;display:inline-block;font-weight:700;padding-left:.5714285714em;padding-right:.5714285714em}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{color:var(--md-default-fg-color--light);display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentcolor;display:block;height:2.4rem;max-width:100%;object-fit:contain;width:auto}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__item{padding:0 .6rem}[dir=ltr] .md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-left:0}.md-nav__link{align-items:flex-start;display:flex;margin-top:.625em;scroll-snap-align:start;transition:color 125ms}.md-nav__link--passed{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active,.md-nav__item .md-nav__link--active code{color:var(--md-typeset-a-color)}.md-nav__link .md-ellipsis{position:relative}.md-nav__link .md-icon:last-child{margin-left:auto}.md-nav__link svg{fill:currentcolor;flex-shrink:0;height:1.3em}[dir=ltr] .md-nav__link svg+*{margin-left:.4rem}[dir=rtl] .md-nav__link svg+*{margin-right:.4rem}.md-nav__link:not(.md-nav__container):focus,.md-nav__link:not(.md-nav__container):hover{color:var(--md-accent-fg-color);cursor:pointer}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentcolor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__container>.md-nav__link{margin-top:0}.md-nav__container>.md-nav__link:first-child{flex-grow:1}.md-nav__icon{flex-shrink:0}.md-nav__source{display:none}@media screen and (max-width:76.1875em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}[dir=ltr] .md-nav--primary .md-nav__title .md-nav__icon{left:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);font-weight:700}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;right:.2rem;top:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest);padding:0}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link svg{margin-top:.1em}.md-nav--primary .md-nav__link>.md-nav__link{padding:0}[dir=ltr] .md-nav--primary .md-nav__link .md-nav__icon{margin-right:-.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{margin-left:-.2rem}.md-nav--primary .md-nav__link .md-nav__icon{font-size:1.2rem;height:1.2rem;width:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav{background-color:initial;position:static}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.9375em){.md-nav--primary .md-nav__link[for=__toc]{display:flex}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-nav--integrated .md-nav__link[for=__toc]{display:flex}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav--secondary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}}@media screen and (min-width:76.25em){.md-nav{transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon,.md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav,.md-nav__toggle:indeterminate~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{font-weight:700}.md-nav__item--section>.md-nav__link[for]{color:var(--md-default-fg-color--light)}.md-nav__item--section>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav__item--section>.md-nav__link .md-nav__icon{display:none}.md-nav__item--section>.md-nav{display:block}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{border-radius:100%;height:.9rem;transition:background-color .25s;width:.9rem}.md-nav__icon:hover{background-color:var(--md-accent-fg-color--transparent)}.md-nav__icon:after{background-color:currentcolor;border-radius:100%;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:transform .25s;vertical-align:-.1rem;width:100%}[dir=rtl] .md-nav__icon:after{transform:rotate(180deg)}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon:after,.md-nav__item--nested .md-nav__toggle:indeterminate~.md-nav__link .md-nav__icon:after{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__list>.md-nav__item--nested,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block;padding:0}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);font-weight:700;margin-top:0;padding:0 .6rem;position:sticky;top:0;z-index:1}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link .md-nav__icon{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item>[for]{color:var(--md-default-fg-color--light)}.md-nav--lifted .md-nav[data-md-level="1"]{display:block}[dir=ltr] .md-nav--lifted .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-right:.6rem}[dir=rtl] .md-nav--lifted .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-left:.6rem}.md-nav--integrated>.md-nav__list>.md-nav__item--active:not(.md-nav__item--nested){padding:0 .6rem}.md-nav--integrated>.md-nav__list>.md-nav__item--active:not(.md-nav__item--nested)>.md-nav__link{padding:0}[dir=ltr] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-left:.05rem solid var(--md-primary-fg-color)}[dir=rtl] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-right:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{display:block;margin-bottom:1.25em}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__title{display:none}}.md-pagination{font-size:.8rem;font-weight:700;gap:.4rem}.md-pagination,.md-pagination>*{align-items:center;display:flex;justify-content:center}.md-pagination>*{border-radius:.2rem;height:1.8rem;min-width:1.8rem;text-align:center}.md-pagination__current{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light)}.md-pagination__link{transition:color 125ms,background-color 125ms}.md-pagination__link:focus,.md-pagination__link:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-pagination__link:focus svg,.md-pagination__link:hover svg{color:var(--md-accent-fg-color)}.md-pagination__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-pagination__link svg{fill:currentcolor;color:var(--md-default-fg-color--lighter);display:block;max-height:100%;width:1.2rem}.md-post__back{border-bottom:.05rem solid var(--md-default-fg-color--lightest);margin-bottom:1.2rem;padding-bottom:1.2rem}@media screen and (max-width:76.1875em){.md-post__back{display:none}}[dir=rtl] .md-post__back svg{transform:scaleX(-1)}.md-post__authors{display:flex;flex-direction:column;gap:.6rem;margin:0 .6rem}.md-post .md-post__meta a{transition:color 125ms}.md-post .md-post__meta a:focus,.md-post .md-post__meta a:hover{color:var(--md-accent-fg-color)}.md-post--excerpt{margin-bottom:3.2rem}.md-post--excerpt .md-post__header{align-items:center;display:flex;gap:.6rem;min-height:1.6rem}.md-post--excerpt .md-post__authors{align-items:center;display:inline-flex;flex-direction:row;gap:.2rem;margin:0;min-height:2.4rem}[dir=ltr] .md-post--excerpt .md-post__meta .md-meta__list{margin-right:.4rem}[dir=rtl] .md-post--excerpt .md-post__meta .md-meta__list{margin-left:.4rem}.md-post--excerpt .md-post__content>:first-child{--md-scroll-margin:6rem;margin-top:0}.md-post>.md-nav--secondary,.md-post>.md-nav:first-child>.md-nav__list{margin:1em 0}.md-profile{align-items:center;display:flex;font-size:.7rem;gap:.6rem;line-height:1.4;width:100%}.md-profile__description{flex-grow:1}.md-content--post{display:flex}@media screen and (max-width:76.1875em){.md-content--post{flex-flow:column-reverse}}.md-content--post>.md-content__inner{min-width:0}@media screen and (min-width:76.25em){[dir=ltr] .md-content--post>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-content--post>.md-content__inner{margin-right:1.2rem}}@media screen and (max-width:76.1875em){.md-sidebar.md-sidebar--post{padding:0}}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.9375em){[dir=ltr] .md-search__overlay{left:-2.2rem}[dir=rtl] .md-search__overlay{right:-2.2rem}.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){[dir=ltr] .md-search__overlay{left:0}[dir=rtl] .md-search__overlay{right:0}.md-search__overlay{background-color:#0000008a;cursor:pointer;height:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.9375em){[dir=ltr] .md-search__inner{left:0}[dir=rtl] .md-search__inner{right:0}.md-search__inner{height:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__inner{float:right}[dir=rtl] .md-search__inner{float:left}.md-search__inner{padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}}@media screen and (min-width:60em) and (max-width:76.1875em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem #0000;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:#00000042;border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:#ffffff1f}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem #00000012;color:var(--md-default-fg-color)}[dir=ltr] .md-search__input{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__input{padding-left:2.2rem;padding-right:3.6rem}.md-search__input{background:#0000;font-size:.9rem;height:100%;position:relative;text-overflow:ellipsis;width:100%;z-index:2}.md-search__input::placeholder{transition:color .25s}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.9375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__input{padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input{color:inherit;font-size:.8rem}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:#0000}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}[dir=ltr] .md-search__icon[for=__search]{left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem}.md-search__icon[for=__search]{position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.9375em){[dir=ltr] .md-search__icon[for=__search]{left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem}.md-search__icon[for=__search]{top:.6rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}[dir=ltr] .md-search__options{right:.5rem}[dir=rtl] .md-search__options{left:.5rem}.md-search__options{pointer-events:none;position:absolute;top:.3rem;z-index:2}@media screen and (max-width:59.9375em){[dir=ltr] .md-search__options{right:.8rem}[dir=rtl] .md-search__options{left:.8rem}.md-search__options{top:.6rem}}[dir=ltr] .md-search__options>.md-icon{margin-left:.2rem}[dir=rtl] .md-search__options>.md-icon{margin-right:.2rem}.md-search__options>.md-icon{color:var(--md-default-fg-color--light);opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>.md-icon:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon:hover{opacity:.7}[dir=ltr] .md-search__suggest{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__suggest{padding-left:2.2rem;padding-right:3.6rem}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}@media screen and (min-width:60em){[dir=ltr] .md-search__suggest{padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}.md-search__suggest{font-size:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}[dir=ltr] .md-search__output{border-bottom-left-radius:.1rem}[dir=ltr] .md-search__output,[dir=rtl] .md-search__output{border-bottom-right-radius:.1rem}[dir=rtl] .md-search__output{border-bottom-left-radius:.1rem}.md-search__output{overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.9375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:var(--md-shadow-z3);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){[dir=ltr] .md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0;-webkit-user-select:none;user-select:none}.md-search-result__item{box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more>summary{cursor:pointer;display:block;outline:none;position:sticky;scroll-snap-align:start;top:0;z-index:1}.md-search-result__more>summary::marker{display:none}.md-search-result__more>summary::-webkit-details-marker{display:none}.md-search-result__more>summary>div{color:var(--md-typeset-a-color);font-size:.64rem;padding:.75em .8rem;transition:color .25s,background-color .25s}@media screen and (min-width:60em){[dir=ltr] .md-search-result__more>summary>div{padding-left:2.2rem}[dir=rtl] .md-search-result__more>summary>div{padding-right:2.2rem}}.md-search-result__more>summary:focus>div,.md-search-result__more>summary:hover>div{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more[open]>summary{background-color:var(--md-default-bg-color)}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){[dir=ltr] .md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem}}[dir=ltr] .md-search-result__icon{left:0}[dir=rtl] .md-search-result__icon{right:0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.9375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentcolor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result .md-typeset{color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6}.md-search-result .md-typeset h1{color:var(--md-default-fg-color);font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result .md-typeset h1 mark{text-decoration:none}.md-search-result .md-typeset h2{color:var(--md-default-fg-color);font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result .md-typeset h2 mark{text-decoration:none}.md-search-result__terms{color:var(--md-default-fg-color);display:block;font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:initial;color:var(--md-accent-fg-color);text-decoration:underline}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid #0000;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid #0000;border-right:.2rem solid #0000;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}[dir=ltr] .md-select__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.1875em){[dir=ltr] .md-sidebar--primary{left:-12.1rem}[dir=rtl] .md-sidebar--primary{right:-12.1rem}.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:5}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:var(--md-shadow-z3);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}.md-header--lifted~.md-container .md-sidebar{top:4.8rem}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{scrollbar-gutter:stable;-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap:focus-within,.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb:hover,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@supports selector(::-webkit-scrollbar){.md-sidebar__scrollwrap{scrollbar-gutter:auto}[dir=ltr] .md-sidebar__inner{padding-right:calc(100% - 11.5rem)}[dir=rtl] .md-sidebar__inner{padding-left:calc(100% - 11.5rem)}}@media screen and (max-width:76.1875em){.md-overlay{background-color:#0000008a;height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:5}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@keyframes facts{0%{height:0}to{height:.65rem}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}[dir=ltr] .md-source__icon svg{margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem}.md-source__icon svg{margin-top:.6rem}[dir=ltr] .md-source__icon+.md-source__repository{padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{padding-right:2rem}[dir=ltr] .md-source__icon+.md-source__repository{margin-left:-2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem}[dir=ltr] .md-source__repository{margin-left:.6rem}[dir=rtl] .md-source__repository{margin-right:.6rem}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{display:flex;font-size:.55rem;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source__repository--active .md-source__facts{animation:facts .25s ease-in}.md-source__fact{overflow:hidden;text-overflow:ellipsis}.md-source__repository--active .md-source__fact{animation:fact .4s ease-out}[dir=ltr] .md-source__fact:before{margin-right:.1rem}[dir=rtl] .md-source__fact:before{margin-left:.1rem}.md-source__fact:before{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}.md-source__fact:nth-child(1n+2){flex-shrink:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}:root{--md-status:url('data:image/svg+xml;charset=utf-8,');--md-status--new:url('data:image/svg+xml;charset=utf-8,');--md-status--deprecated:url('data:image/svg+xml;charset=utf-8,');--md-status--encrypted:url('data:image/svg+xml;charset=utf-8,')}.md-status{margin-left:.2rem}.md-status:after{background-color:var(--md-default-fg-color--light);content:"";display:inline-block;height:1.125em;-webkit-mask-image:var(--md-status);mask-image:var(--md-status);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-bottom;width:1.125em}.md-status:hover:after{background-color:currentcolor}.md-status--new:after{-webkit-mask-image:var(--md-status--new);mask-image:var(--md-status--new)}.md-status--deprecated:after{-webkit-mask-image:var(--md-status--deprecated);mask-image:var(--md-status--deprecated)}.md-status--encrypted:after{-webkit-mask-image:var(--md-status--encrypted);mask-image:var(--md-status--encrypted)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);display:block;line-height:1.3;overflow:auto;width:100%;z-index:3}@media print{.md-tabs{display:none}}@media screen and (max-width:76.1875em){.md-tabs{display:none}}.md-tabs[hidden]{pointer-events:none}[dir=ltr] .md-tabs__list{margin-left:.2rem}[dir=rtl] .md-tabs__list{margin-right:.2rem}.md-tabs__list{contain:content;display:flex;list-style:none;margin:0;overflow:auto;padding:0;scrollbar-width:none;white-space:nowrap}.md-tabs__list::-webkit-scrollbar{display:none}.md-tabs__item{height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__item--active .md-tabs__link{color:inherit;opacity:1}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:flex;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}[dir=ltr] .md-tabs__link svg{margin-right:.4rem}[dir=rtl] .md-tabs__link svg{margin-left:.4rem}.md-tabs__link svg{fill:currentcolor;height:1.3em}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}:root{--md-tag-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-tags{margin-bottom:.75em;margin-top:-.125em}[dir=ltr] .md-typeset .md-tag{margin-right:.5em}[dir=rtl] .md-typeset .md-tag{margin-left:.5em}.md-typeset .md-tag{background:var(--md-default-fg-color--lightest);border-radius:2.4rem;display:inline-block;font-size:.64rem;font-weight:700;letter-spacing:normal;line-height:1.6;margin-bottom:.5em;padding:.3125em .9375em;vertical-align:middle}.md-typeset .md-tag[href]{-webkit-tap-highlight-color:transparent;color:inherit;outline:none;transition:color 125ms,background-color 125ms}.md-typeset .md-tag[href]:focus,.md-typeset .md-tag[href]:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[id]>.md-typeset .md-tag{vertical-align:text-top}.md-typeset .md-tag-icon:before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline-block;height:1.2em;margin-right:.4em;-webkit-mask-image:var(--md-tag-icon);mask-image:var(--md-tag-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset .md-tag-icon[href]:focus:before,.md-typeset .md-tag-icon[href]:hover:before{background-color:var(--md-accent-bg-color)}@keyframes pulse{0%{transform:scale(.95)}75%{transform:scale(1)}to{transform:scale(.95)}}:root{--md-annotation-bg-icon:url('data:image/svg+xml;charset=utf-8,');--md-annotation-icon:url('data:image/svg+xml;charset=utf-8,');--md-tooltip-width:20rem}.md-tooltip{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);font-family:var(--md-text-font-family);left:clamp(var(--md-tooltip-0,0rem) + .8rem,var(--md-tooltip-x),100vw + var(--md-tooltip-0,0rem) + .8rem - var(--md-tooltip-width) - 2 * .8rem);max-width:calc(100vw - 1.6rem);opacity:0;position:absolute;top:var(--md-tooltip-y);transform:translateY(-.4rem);transition:transform 0ms .25s,opacity .25s,z-index .25s;width:var(--md-tooltip-width);z-index:0}.md-tooltip--active{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,z-index 0ms;z-index:2}.focus-visible>.md-tooltip,.md-tooltip:target{outline:var(--md-accent-fg-color) auto}.md-tooltip__inner{font-size:.64rem;padding:.8rem}.md-tooltip__inner.md-typeset>:first-child{margin-top:0}.md-tooltip__inner.md-typeset>:last-child{margin-bottom:0}.md-annotation{font-weight:400;outline:none;vertical-align:text-bottom;white-space:normal}[dir=rtl] .md-annotation{direction:rtl}code .md-annotation{font-family:var(--md-code-font-family);font-size:inherit}.md-annotation:not([hidden]){display:inline-block;line-height:1.25}.md-annotation__index{border-radius:.01px;cursor:pointer;display:inline-block;margin-left:.4ch;margin-right:.4ch;outline:none;overflow:hidden;position:relative;-webkit-user-select:none;user-select:none;vertical-align:text-top;z-index:0}.md-annotation .md-annotation__index{transition:z-index .25s}@media screen{.md-annotation__index{width:2.2ch}[data-md-visible]>.md-annotation__index{animation:pulse 2s infinite}.md-annotation__index:before{background:var(--md-default-bg-color);-webkit-mask-image:var(--md-annotation-bg-icon);mask-image:var(--md-annotation-bg-icon)}.md-annotation__index:after,.md-annotation__index:before{content:"";height:2.2ch;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:-.1ch;width:2.2ch;z-index:-1}.md-annotation__index:after{background-color:var(--md-default-fg-color--lighter);-webkit-mask-image:var(--md-annotation-icon);mask-image:var(--md-annotation-icon);transform:scale(1.0001);transition:background-color .25s,transform .25s}.md-tooltip--active+.md-annotation__index:after{transform:rotate(45deg)}.md-tooltip--active+.md-annotation__index:after,:hover>.md-annotation__index:after{background-color:var(--md-accent-fg-color)}}.md-tooltip--active+.md-annotation__index{animation-play-state:paused;transition-duration:0ms;z-index:2}.md-annotation__index [data-md-annotation-id]{display:inline-block}@media print{.md-annotation__index [data-md-annotation-id]{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);font-weight:700;padding:0 .6ch;white-space:nowrap}.md-annotation__index [data-md-annotation-id]:after{content:attr(data-md-annotation-id)}}.md-typeset .md-annotation-list{counter-reset:xxx;list-style:none}.md-typeset .md-annotation-list li{position:relative}[dir=ltr] .md-typeset .md-annotation-list li:before{left:-2.125em}[dir=rtl] .md-typeset .md-annotation-list li:before{right:-2.125em}.md-typeset .md-annotation-list li:before{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);content:counter(xxx);counter-increment:xxx;font-size:.8875em;font-weight:700;height:2ch;line-height:1.25;min-width:2ch;padding:0 .6ch;position:absolute;text-align:center;top:.25em}[dir=ltr] .md-top{margin-left:50%}[dir=rtl] .md-top{margin-right:50%}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color--light);cursor:pointer;display:block;font-size:.7rem;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{transform:translate(50%)}.md-top[hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}[dir=rtl] .md-top[hidden]{transform:translate(50%,.2rem)}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}[dir=ltr] .md-version__current{margin-left:1.4rem;margin-right:.4rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current{color:inherit;cursor:pointer;outline:none;position:relative;top:.05rem}[dir=ltr] .md-version__current:after{margin-left:.4rem}[dir=rtl] .md-version__current:after{margin-right:.4rem}.md-version__current:after{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.4rem}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:3}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (hover:none),(pointer:coarse){.md-version:hover .md-version__list{animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{animation:none}}.md-version__item{line-height:1.8rem}[dir=ltr] .md-version__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border:.05rem solid #448aff;border-radius:.2rem;box-shadow:var(--md-shadow-z1);color:var(--md-admonition-fg-color);display:flow-root;font-size:.64rem;margin:1.5625em 0;padding:0 .6rem;page-break-inside:avoid;transition:box-shadow 125ms}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}.md-typeset .admonition:focus-within,.md-typeset details:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .admonition>*,.md-typeset details>*{box-sizing:border-box}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{padding-left:2rem;padding-right:.6rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{padding-left:.6rem;padding-right:2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-left-width:.2rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-right-width:.2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset .admonition-title,.md-typeset summary{background-color:#448aff1a;border:none;font-weight:700;margin:0 -.6rem;padding-bottom:.4rem;padding-top:.4rem;position:relative}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}[dir=ltr] .md-typeset .admonition-title:before,[dir=ltr] .md-typeset summary:before{left:.6rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;width:1rem}.md-typeset .admonition-title code,.md-typeset summary code{box-shadow:0 0 0 .05rem var(--md-default-fg-color--lightest)}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .admonition.note:focus-within,.md-typeset details.note:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:#448aff1a}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note)}.md-typeset .note>.admonition-title:after,.md-typeset .note>summary:after{color:#448aff}.md-typeset .admonition.abstract,.md-typeset details.abstract{border-color:#00b0ff}.md-typeset .admonition.abstract:focus-within,.md-typeset details.abstract:focus-within{box-shadow:0 0 0 .2rem #00b0ff1a}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary{background-color:#00b0ff1a}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract)}.md-typeset .abstract>.admonition-title:after,.md-typeset .abstract>summary:after{color:#00b0ff}.md-typeset .admonition.info,.md-typeset details.info{border-color:#00b8d4}.md-typeset .admonition.info:focus-within,.md-typeset details.info:focus-within{box-shadow:0 0 0 .2rem #00b8d41a}.md-typeset .info>.admonition-title,.md-typeset .info>summary{background-color:#00b8d41a}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info)}.md-typeset .info>.admonition-title:after,.md-typeset .info>summary:after{color:#00b8d4}.md-typeset .admonition.tip,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .admonition.tip:focus-within,.md-typeset details.tip:focus-within{box-shadow:0 0 0 .2rem #00bfa51a}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:#00bfa51a}.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip)}.md-typeset .tip>.admonition-title:after,.md-typeset .tip>summary:after{color:#00bfa5}.md-typeset .admonition.success,.md-typeset details.success{border-color:#00c853}.md-typeset .admonition.success:focus-within,.md-typeset details.success:focus-within{box-shadow:0 0 0 .2rem #00c8531a}.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:#00c8531a}.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success)}.md-typeset .success>.admonition-title:after,.md-typeset .success>summary:after{color:#00c853}.md-typeset .admonition.question,.md-typeset details.question{border-color:#64dd17}.md-typeset .admonition.question:focus-within,.md-typeset details.question:focus-within{box-shadow:0 0 0 .2rem #64dd171a}.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:#64dd171a}.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question)}.md-typeset .question>.admonition-title:after,.md-typeset .question>summary:after{color:#64dd17}.md-typeset .admonition.warning,.md-typeset details.warning{border-color:#ff9100}.md-typeset .admonition.warning:focus-within,.md-typeset details.warning:focus-within{box-shadow:0 0 0 .2rem #ff91001a}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:#ff91001a}.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning)}.md-typeset .warning>.admonition-title:after,.md-typeset .warning>summary:after{color:#ff9100}.md-typeset .admonition.failure,.md-typeset details.failure{border-color:#ff5252}.md-typeset .admonition.failure:focus-within,.md-typeset details.failure:focus-within{box-shadow:0 0 0 .2rem #ff52521a}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary{background-color:#ff52521a}.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure)}.md-typeset .failure>.admonition-title:after,.md-typeset .failure>summary:after{color:#ff5252}.md-typeset .admonition.danger,.md-typeset details.danger{border-color:#ff1744}.md-typeset .admonition.danger:focus-within,.md-typeset details.danger:focus-within{box-shadow:0 0 0 .2rem #ff17441a}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary{background-color:#ff17441a}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger)}.md-typeset .danger>.admonition-title:after,.md-typeset .danger>summary:after{color:#ff1744}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .admonition.bug:focus-within,.md-typeset details.bug:focus-within{box-shadow:0 0 0 .2rem #f500571a}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:#f500571a}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug)}.md-typeset .bug>.admonition-title:after,.md-typeset .bug>summary:after{color:#f50057}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .admonition.example:focus-within,.md-typeset details.example:focus-within{box-shadow:0 0 0 .2rem #7c4dff1a}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:#7c4dff1a}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example)}.md-typeset .example>.admonition-title:after,.md-typeset .example>summary:after{color:#7c4dff}.md-typeset .admonition.quote,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .admonition.quote:focus-within,.md-typeset details.quote:focus-within{box-shadow:0 0 0 .2rem #9e9e9e1a}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:#9e9e9e1a}.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote)}.md-typeset .quote>.admonition-title:after,.md-typeset .quote>summary:after{color:#9e9e9e}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}[dir=ltr] .md-typeset .footnote>ol{margin-left:0}[dir=rtl] .md-typeset .footnote>ol{margin-right:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:focus-within .footnote-backref{opacity:1;transform:translateX(0);transition:none}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentcolor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}[dir=ltr] .md-typeset .headerlink{margin-left:.5rem}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{--md-scroll-margin:3.6rem;--md-scroll-offset:0rem;scroll-margin-top:calc(var(--md-scroll-margin) - var(--md-scroll-offset))}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{--md-scroll-margin:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{--md-scroll-offset:0.2rem}.md-typeset h4:target{--md-scroll-offset:0.15rem}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.9375em){.md-typeset div.arithmatex{margin:0 -.8rem}}.md-typeset div.arithmatex>*{margin-left:auto!important;margin-right:auto!important;padding:0 .8rem;touch-action:auto;width:-webkit-min-content;width:min-content}.md-typeset div.arithmatex>* mjx-container{margin:0!important}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{-webkit-box-decoration-break:clone;box-decoration-break:clone;color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}[dir=ltr] .md-typeset summary{padding-right:1.8rem}[dir=rtl] .md-typeset summary{padding-left:1.8rem}[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset summary{cursor:pointer;display:block;min-height:1rem}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[dir=ltr] .md-typeset summary:after{right:.4rem}[dir=rtl] .md-typeset summary:after{left:.4rem}.md-typeset summary:after{background-color:currentcolor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{transform:rotate(180deg)}.md-typeset summary::marker{display:none}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{display:inline-flex;height:1.125em;vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentcolor;max-height:100%;width:1.125em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color);display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight span.filename{background-color:var(--md-code-bg-color);border-bottom:.05rem solid var(--md-default-fg-color--lightest);border-top-left-radius:.1rem;border-top-right-radius:.1rem;display:flow-root;font-size:.85em;font-weight:700;margin-top:1em;padding:.6617647059em 1.1764705882em;position:relative}.highlight span.filename+pre{margin-top:0}.highlight span.filename+pre>code{border-top-left-radius:0;border-top-right-radius:0}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:sticky;-webkit-user-select:none;user-select:none;z-index:3}.highlight code a[id]{position:absolute;visibility:hidden}.highlight code[data-md-copying] .hll{display:contents}.highlight code[data-md-copying] .md-annotation{display:none}.highlighttable{display:flow-root}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable th.filename{flex-grow:1;padding:0;text-align:left}.highlighttable th.filename span.filename{margin-top:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-top-left-radius:.1rem;font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;padding-right:.5882352941em}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;min-width:0}.linenodiv a{color:inherit}.md-typeset .highlighttable{direction:ltr;margin:1em 0}.md-typeset .highlighttable>tbody>tr>.code>div>pre>code{border-bottom-left-radius:0;border-top-left-radius:0}.md-typeset .highlight+.result{border:.05rem solid var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-bottom-right-radius:.1rem;border-top-width:.1rem;margin-top:-1.125em;overflow:visible;padding:0 1em}.md-typeset .highlight+.result:after{clear:both;content:"";display:block}@media screen and (max-width:44.9375em){.md-content__inner>.highlight{margin:1em -.8rem}.md-content__inner>.highlight>.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.code>div>pre>code,.md-content__inner>.highlight>.highlighttable>tbody>tr>.filename span.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.linenos,.md-content__inner>.highlight>pre>code{border-radius:0}.md-content__inner>.highlight+.result{border-left-width:0;border-radius:0;border-right-width:0;margin-left:-.8rem;margin-right:-.8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"↓";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"←";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"→";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"↑";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"⌫";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"⇤";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"⇪";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"⌧";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"☰";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"⌦";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"⏏";padding-right:.4em}.md-typeset .keys .key-end:before{content:"⤓";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"⎋";padding-right:.4em}.md-typeset .keys .key-home:before{content:"⤒";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"⎀";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"⇟";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"⇞";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"⎙";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"⇥";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"⌤";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"⏎";padding-left:.4em}:root{--md-tabbed-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-tabbed-icon--next:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-flow:column wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:target{--md-scroll-offset:0.625em}.md-typeset .tabbed-labels{-ms-overflow-style:none;box-shadow:0 -.05rem var(--md-default-fg-color--lightest) inset;display:flex;max-width:100%;overflow:auto;scrollbar-width:none}@media print{.md-typeset .tabbed-labels{display:contents}}@media screen{.js .md-typeset .tabbed-labels{position:relative}.js .md-typeset .tabbed-labels:before{background:var(--md-accent-fg-color);bottom:0;content:"";display:block;height:2px;left:0;position:absolute;transform:translateX(var(--md-indicator-x));transition:width 225ms,transform .25s;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--md-indicator-width)}}.md-typeset .tabbed-labels::-webkit-scrollbar{display:none}.md-typeset .tabbed-labels>label{border-bottom:.1rem solid #0000;border-radius:.1rem .1rem 0 0;color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;font-size:.64rem;font-weight:700;padding:.78125em 1.25em .625em;scroll-margin-inline-start:1rem;transition:background-color .25s,color .25s;white-space:nowrap;width:auto}@media print{.md-typeset .tabbed-labels>label:first-child{order:1}.md-typeset .tabbed-labels>label:nth-child(2){order:2}.md-typeset .tabbed-labels>label:nth-child(3){order:3}.md-typeset .tabbed-labels>label:nth-child(4){order:4}.md-typeset .tabbed-labels>label:nth-child(5){order:5}.md-typeset .tabbed-labels>label:nth-child(6){order:6}.md-typeset .tabbed-labels>label:nth-child(7){order:7}.md-typeset .tabbed-labels>label:nth-child(8){order:8}.md-typeset .tabbed-labels>label:nth-child(9){order:9}.md-typeset .tabbed-labels>label:nth-child(10){order:10}.md-typeset .tabbed-labels>label:nth-child(11){order:11}.md-typeset .tabbed-labels>label:nth-child(12){order:12}.md-typeset .tabbed-labels>label:nth-child(13){order:13}.md-typeset .tabbed-labels>label:nth-child(14){order:14}.md-typeset .tabbed-labels>label:nth-child(15){order:15}.md-typeset .tabbed-labels>label:nth-child(16){order:16}.md-typeset .tabbed-labels>label:nth-child(17){order:17}.md-typeset .tabbed-labels>label:nth-child(18){order:18}.md-typeset .tabbed-labels>label:nth-child(19){order:19}.md-typeset .tabbed-labels>label:nth-child(20){order:20}}.md-typeset .tabbed-labels>label:hover{color:var(--md-accent-fg-color)}.md-typeset .tabbed-content{width:100%}@media print{.md-typeset .tabbed-content{display:contents}}.md-typeset .tabbed-block{display:none}@media print{.md-typeset .tabbed-block{display:block}.md-typeset .tabbed-block:first-child{order:1}.md-typeset .tabbed-block:nth-child(2){order:2}.md-typeset .tabbed-block:nth-child(3){order:3}.md-typeset .tabbed-block:nth-child(4){order:4}.md-typeset .tabbed-block:nth-child(5){order:5}.md-typeset .tabbed-block:nth-child(6){order:6}.md-typeset .tabbed-block:nth-child(7){order:7}.md-typeset .tabbed-block:nth-child(8){order:8}.md-typeset .tabbed-block:nth-child(9){order:9}.md-typeset .tabbed-block:nth-child(10){order:10}.md-typeset .tabbed-block:nth-child(11){order:11}.md-typeset .tabbed-block:nth-child(12){order:12}.md-typeset .tabbed-block:nth-child(13){order:13}.md-typeset .tabbed-block:nth-child(14){order:14}.md-typeset .tabbed-block:nth-child(15){order:15}.md-typeset .tabbed-block:nth-child(16){order:16}.md-typeset .tabbed-block:nth-child(17){order:17}.md-typeset .tabbed-block:nth-child(18){order:18}.md-typeset .tabbed-block:nth-child(19){order:19}.md-typeset .tabbed-block:nth-child(20){order:20}}.md-typeset .tabbed-block>.highlight:first-child>pre,.md-typeset .tabbed-block>pre:first-child{margin:0}.md-typeset .tabbed-block>.highlight:first-child>pre>code,.md-typeset .tabbed-block>pre:first-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child>.filename{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable{margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.filename span.filename,.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.linenos{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.code>div>pre>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child+.result{margin-top:-.125em}.md-typeset .tabbed-block>.tabbed-set{margin:0}.md-typeset .tabbed-button{align-self:center;border-radius:100%;color:var(--md-default-fg-color--light);cursor:pointer;display:block;height:.9rem;margin-top:.1rem;pointer-events:auto;transition:background-color .25s;width:.9rem}.md-typeset .tabbed-button:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .tabbed-button:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-tabbed-icon--prev);mask-image:var(--md-tabbed-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color .25s,transform .25s;width:100%}.md-typeset .tabbed-control{background:linear-gradient(to right,var(--md-default-bg-color) 60%,#0000);display:flex;height:1.9rem;justify-content:start;pointer-events:none;position:absolute;transition:opacity 125ms;width:1.2rem}[dir=rtl] .md-typeset .tabbed-control{transform:rotate(180deg)}.md-typeset .tabbed-control[hidden]{opacity:0}.md-typeset .tabbed-control--next{background:linear-gradient(to left,var(--md-default-bg-color) 60%,#0000);justify-content:end;right:0}.md-typeset .tabbed-control--next .tabbed-button:after{-webkit-mask-image:var(--md-tabbed-icon--next);mask-image:var(--md-tabbed-icon--next)}@media screen and (max-width:44.9375em){[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels{padding-right:.8rem}.md-content__inner>.tabbed-set .tabbed-labels{margin:0 -.8rem;max-width:100vw;scroll-padding-inline-start:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-left:.8rem}.md-content__inner>.tabbed-set .tabbed-labels:after{content:""}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-right:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-left:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-right:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{width:2rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-left:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-right:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-left:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{width:2rem}}@media screen{.md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){color:var(--md-accent-fg-color)}.md-typeset .no-js .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .no-js .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .no-js .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .no-js .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .no-js .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .no-js .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .no-js .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .no-js .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .no-js .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .no-js .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .no-js .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .no-js .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .no-js .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .no-js .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .no-js .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .no-js .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .no-js .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .no-js .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .no-js .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .no-js .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.no-js .md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.no-js .md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.no-js .md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.no-js .md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.no-js .md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.no-js .md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.no-js .md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.no-js .md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.no-js .md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.no-js .md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.no-js .md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.no-js .md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.no-js .md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.no-js .md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.no-js .md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.no-js .md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.no-js .md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.no-js .md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.no-js .md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.no-js .md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){border-color:var(--md-accent-fg-color)}}.md-typeset .tabbed-set>input:first-child.focus-visible~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10).focus-visible~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11).focus-visible~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12).focus-visible~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13).focus-visible~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14).focus-visible~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15).focus-visible~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16).focus-visible~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17).focus-visible~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18).focus-visible~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19).focus-visible~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2).focus-visible~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20).focus-visible~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3).focus-visible~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4).focus-visible~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5).focus-visible~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6).focus-visible~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7).focus-visible~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8).focus-visible~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9).focus-visible~.tabbed-labels>:nth-child(9){background-color:var(--md-accent-fg-color--transparent)}.md-typeset .tabbed-set>input:first-child:checked~.tabbed-content>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-content>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-content>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-content>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-content>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-content>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-content>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-content>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-content>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-content>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-content>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-content>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-content>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-content>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-content>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-content>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-content>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-content>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-content>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-content>:nth-child(9){display:block}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}[dir=ltr] .md-typeset .task-list-item [type=checkbox]{left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}[dir=ltr] .md-typeset .task-list-indicator:before{left:-1.5em}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}:root>*{--md-mermaid-font-family:var(--md-text-font-family),sans-serif;--md-mermaid-edge-color:var(--md-code-fg-color);--md-mermaid-node-bg-color:var(--md-accent-fg-color--transparent);--md-mermaid-node-fg-color:var(--md-accent-fg-color);--md-mermaid-label-bg-color:var(--md-default-bg-color);--md-mermaid-label-fg-color:var(--md-code-fg-color);--md-mermaid-sequence-actor-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actor-fg-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-actor-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-actor-line-color:var(--md-default-fg-color--lighter);--md-mermaid-sequence-actorman-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actorman-line-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-box-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-box-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-label-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-label-fg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-loop-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-loop-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-loop-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-message-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-message-line-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-note-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-border-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-number-bg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-number-fg-color:var(--md-accent-bg-color)}.mermaid{line-height:normal;margin:1em 0}@media screen and (min-width:45em){[dir=ltr] .md-typeset .inline{float:left}[dir=rtl] .md-typeset .inline{float:right}[dir=ltr] .md-typeset .inline{margin-right:.8rem}[dir=rtl] .md-typeset .inline{margin-left:.8rem}.md-typeset .inline{margin-bottom:.8rem;margin-top:0;width:11.7rem}[dir=ltr] .md-typeset .inline.end{float:right}[dir=rtl] .md-typeset .inline.end{float:left}[dir=ltr] .md-typeset .inline.end{margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{margin-left:0;margin-right:.8rem}}
\ No newline at end of file
diff --git a/assets/stylesheets/main.046329b4.min.css.map b/assets/stylesheets/main.046329b4.min.css.map
deleted file mode 100644
index d84ca009..00000000
--- a/assets/stylesheets/main.046329b4.min.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["src/assets/stylesheets/main/components/_meta.scss","../../../src/assets/stylesheets/main.scss","src/assets/stylesheets/main/_resets.scss","src/assets/stylesheets/main/_colors.scss","src/assets/stylesheets/main/_icons.scss","src/assets/stylesheets/main/_typeset.scss","src/assets/stylesheets/utilities/_break.scss","src/assets/stylesheets/main/components/_author.scss","src/assets/stylesheets/main/components/_banner.scss","src/assets/stylesheets/main/components/_base.scss","src/assets/stylesheets/main/components/_clipboard.scss","src/assets/stylesheets/main/components/_consent.scss","src/assets/stylesheets/main/components/_content.scss","src/assets/stylesheets/main/components/_dialog.scss","src/assets/stylesheets/main/components/_feedback.scss","src/assets/stylesheets/main/components/_footer.scss","src/assets/stylesheets/main/components/_form.scss","src/assets/stylesheets/main/components/_header.scss","node_modules/material-design-color/material-color.scss","src/assets/stylesheets/main/components/_nav.scss","src/assets/stylesheets/main/components/_pagination.scss","src/assets/stylesheets/main/components/_post.scss","src/assets/stylesheets/main/components/_search.scss","src/assets/stylesheets/main/components/_select.scss","src/assets/stylesheets/main/components/_sidebar.scss","src/assets/stylesheets/main/components/_source.scss","src/assets/stylesheets/main/components/_status.scss","src/assets/stylesheets/main/components/_tabs.scss","src/assets/stylesheets/main/components/_tag.scss","src/assets/stylesheets/main/components/_tooltip.scss","src/assets/stylesheets/main/components/_top.scss","src/assets/stylesheets/main/components/_version.scss","src/assets/stylesheets/main/extensions/markdown/_admonition.scss","src/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/assets/stylesheets/main/extensions/markdown/_toc.scss","src/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/assets/stylesheets/main/integrations/_mermaid.scss","src/assets/stylesheets/main/_modifiers.scss"],"names":[],"mappings":"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gC/BupGN,C+BlpGE,4DACE,0B/BqpGJ,C+BlpGI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yC/BopGN,C+BhpGI,wEACE,a/BkpGN,C+BvqGE,4DACE,oB/B0qGJ,C+BvqGI,sFACE,gC/ByqGN,C+BpqGE,oEACE,0B/BuqGJ,C+BpqGI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6C/BsqGN,C+BlqGI,gFACE,a/BoqGN,C+BzrGE,8DACE,oB/B4rGJ,C+BzrGI,wFACE,gC/B2rGN,C+BtrGE,sEACE,0B/ByrGJ,C+BtrGI,oFACE,wBAlBG,CAmBH,sDAAA,CAAA,8C/BwrGN,C+BprGI,kFACE,a/BsrGN,C+B3sGE,4DACE,oB/B8sGJ,C+B3sGI,sFACE,gC/B6sGN,C+BxsGE,oEACE,0B/B2sGJ,C+BxsGI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6C/B0sGN,C+BtsGI,gFACE,a/BwsGN,C+B7tGE,4DACE,oB/BguGJ,C+B7tGI,sFACE,gC/B+tGN,C+B1tGE,oEACE,0B/B6tGJ,C+B1tGI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6C/B4tGN,C+BxtGI,gFACE,a/B0tGN,C+B/uGE,0DACE,oB/BkvGJ,C+B/uGI,oFACE,gC/BivGN,C+B5uGE,kEACE,0B/B+uGJ,C+B5uGI,gFACE,wBAlBG,CAmBH,oDAAA,CAAA,4C/B8uGN,C+B1uGI,8EACE,a/B4uGN,C+BjwGE,oDACE,oB/BowGJ,C+BjwGI,8EACE,gC/BmwGN,C+B9vGE,4DACE,0B/BiwGJ,C+B9vGI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yC/BgwGN,C+B5vGI,wEACE,a/B8vGN,C+BnxGE,4DACE,oB/BsxGJ,C+BnxGI,sFACE,gC/BqxGN,C+BhxGE,oEACE,0B/BmxGJ,C+BhxGI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6C/BkxGN,C+B9wGI,gFACE,a/BgxGN,C+BryGE,wDACE,oB/BwyGJ,C+BryGI,kFACE,gC/BuyGN,C+BlyGE,gEACE,0B/BqyGJ,C+BlyGI,8EACE,wBAlBG,CAmBH,mDAAA,CAAA,2C/BoyGN,C+BhyGI,4EACE,a/B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","file":"main.css"}
\ No newline at end of file
diff --git a/assets/stylesheets/main.30068a00.min.css b/assets/stylesheets/main.30068a00.min.css
new file mode 100644
index 00000000..f813e9a8
--- /dev/null
+++ b/assets/stylesheets/main.30068a00.min.css
@@ -0,0 +1 @@
+@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}@media (prefers-reduced-motion){*,:after,:before{transition:none!important}}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:initial;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:#0000;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-scheme=default]{color-scheme:light}[data-md-color-scheme=default] img[src$="#gh-dark-mode-only"],[data-md-color-scheme=default] img[src$="#only-dark"]{display:none}:root,[data-md-color-scheme=default]{--md-default-fg-color:#000000de;--md-default-fg-color--light:#0000008a;--md-default-fg-color--lighter:#00000052;--md-default-fg-color--lightest:#00000012;--md-default-bg-color:#fff;--md-default-bg-color--light:#ffffffb3;--md-default-bg-color--lighter:#ffffff4d;--md-default-bg-color--lightest:#ffffff1f;--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:#4287ff;--md-code-hl-color--light:#4287ff1a;--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-mark-color:#ffff0080;--md-typeset-del-color:#f5503d26;--md-typeset-ins-color:#0bd57026;--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-table-color:#0000001f;--md-typeset-table-color--light:rgba(0,0,0,.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-warning-fg-color:#000000de;--md-warning-bg-color:#ff9;--md-footer-fg-color:#fff;--md-footer-fg-color--light:#ffffffb3;--md-footer-fg-color--lighter:#ffffff73;--md-footer-bg-color:#000000de;--md-footer-bg-color--dark:#00000052;--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #0000001a,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0003,0 0 0.05rem #00000059}.md-icon svg{fill:currentcolor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--md-text-font-family:var(--md-text-font,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;--md-code-font-family:var(--md-code-font,_),SFMono-Regular,Consolas,Menlo,monospace}aside,body,input{font-feature-settings:"kern","liga";color:var(--md-typeset-color);font-family:var(--md-text-font-family)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family)}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin-bottom:1em;margin-top:1em}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a:focus code,.md-typeset a:hover code{background-color:var(--md-accent-fg-color--transparent)}.md-typeset a code{color:currentcolor;transition:background-color 125ms}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr;font-variant-ligatures:none}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;outline-color:var(--md-accent-fg-color);overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help;text-decoration:none}@media (hover:none){.md-typeset abbr[title]:focus:after,.md-typeset abbr[title]:hover:after{background-color:var(--md-default-fg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z3);color:var(--md-default-bg-color);content:attr(title);font-size:.7rem;left:.8rem;margin-top:2em;padding:.2rem .3rem;position:absolute;right:.8rem}}.md-typeset small{opacity:.75}[dir=ltr] .md-typeset sub,[dir=ltr] .md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em}[dir=ltr] .md-typeset blockquote{padding-left:.6rem}[dir=rtl] .md-typeset blockquote{padding-right:.6rem}[dir=ltr] .md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{border-right:.2rem solid var(--md-default-fg-color--lighter)}.md-typeset blockquote{color:var(--md-default-fg-color--light);margin-left:0;margin-right:0}.md-typeset ul{list-style-type:disc}[dir=ltr] .md-typeset ol,[dir=ltr] .md-typeset ul{margin-left:.625em}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em}.md-typeset ol,.md-typeset ul{padding:0}.md-typeset ol:not([hidden]),.md-typeset ul:not([hidden]){display:flow-root}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}[dir=ltr] .md-typeset ol li,[dir=ltr] .md-typeset ul li{margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}[dir=ltr] .md-typeset ol li ol,[dir=ltr] .md-typeset ol li ul,[dir=ltr] .md-typeset ul li ol,[dir=ltr] .md-typeset ul li ul{margin-left:.625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin-bottom:.5em;margin-top:.5em}[dir=ltr] .md-typeset dd{margin-left:1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em}.md-typeset dd{margin-bottom:1.5em;margin-top:1em}.md-typeset img,.md-typeset svg,.md-typeset video{height:auto;max-width:100%}.md-typeset img[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:1em auto;max-width:100%;text-align:center;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block}.md-typeset figcaption{font-style:italic;margin:1em auto;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:var(--md-typeset-table-color--light);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}[dir=ltr] .md-typeset table th[role=columnheader]:after{margin-left:.5em}[dir=rtl] .md-typeset table th[role=columnheader]:after{margin-right:.5em}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}@media screen and (max-width:44.9375em){.md-content__inner>pre{margin:1em -.8rem}.md-content__inner>pre code{border-radius:0}}.md-typeset .md-author{display:block;flex-shrink:0;height:1.6rem;overflow:hidden;position:relative;transition:color 125ms,transform 125ms;width:1.6rem}.md-typeset .md-author img{border-radius:100%;display:block}.md-typeset .md-author--more{background:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--lighter);font-size:.6rem;font-weight:700;line-height:1.6rem;text-align:center}.md-typeset .md-author--long{height:2.4rem;width:2.4rem}.md-typeset a.md-author{transform:scale(1)}.md-typeset a.md-author img{filter:grayscale(100%) opacity(75%);transition:filter 125ms}.md-typeset a.md-author:focus,.md-typeset a.md-author:hover{transform:scale(1.1);z-index:1}.md-typeset a.md-author:focus img,.md-typeset a.md-author:hover img{filter:grayscale(0)}.md-banner{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color);overflow:auto}@media print{.md-banner{display:none}}.md-banner--warning{background-color:var(--md-warning-bg-color);color:var(--md-warning-fg-color)}.md-banner__inner{font-size:.7rem;margin:.6rem auto;padding:0 .8rem}[dir=ltr] .md-banner__button{float:right}[dir=rtl] .md-banner__button{float:left}.md-banner__button{color:inherit;cursor:pointer;transition:opacity .25s}.no-js .md-banner__button{display:none}.md-banner__button:hover{opacity:.7}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.9375em){body[data-md-scrolllock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;position:absolute;right:.5em;top:.5em;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}@keyframes consent{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes overlay{0%{opacity:0}to{opacity:1}}.md-consent__overlay{animation:overlay .25s both;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);background-color:#0000008a;height:100%;opacity:1;position:fixed;top:0;width:100%;z-index:5}.md-consent__inner{animation:consent .5s cubic-bezier(.1,.7,.1,1) both;background-color:var(--md-default-bg-color);border:0;border-radius:.1rem;bottom:0;box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;max-height:100%;overflow:auto;padding:0;position:fixed;width:100%;z-index:5}.md-consent__form{padding:.8rem}.md-consent__settings{display:none;margin:1em 0}input:checked+.md-consent__settings{display:block}.md-consent__controls{margin-bottom:.8rem}.md-typeset .md-consent__controls .md-button{display:inline}@media screen and (max-width:44.9375em){.md-typeset .md-consent__controls .md-button{display:block;margin-top:.4rem;text-align:center;width:100%}}.md-consent label{cursor:pointer}.md-content{flex-grow:1;min-width:0}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){[dir=ltr] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=ltr] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner,[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}[dir=ltr] .md-content__button{float:right}[dir=rtl] .md-content__button{float:left}[dir=ltr] .md-content__button{margin-left:.4rem}[dir=rtl] .md-content__button{margin-right:.4rem}.md-content__button{margin:.4rem 0;padding:0}@media print{.md-content__button{display:none}}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}[dir=ltr] .md-dialog{right:.8rem}[dir=rtl] .md-dialog{left:.8rem}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:var(--md-shadow-z3);min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:4}@media print{.md-dialog{display:none}}.md-dialog--active{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-feedback{margin:2em 0 1em;text-align:center}.md-feedback fieldset{border:none;margin:0;padding:0}.md-feedback__title{font-weight:700;margin:1em auto}.md-feedback__inner{position:relative}.md-feedback__list{align-content:baseline;display:flex;flex-wrap:wrap;justify-content:center;position:relative}.md-feedback__list:hover .md-icon:not(:disabled){color:var(--md-default-fg-color--lighter)}:disabled .md-feedback__list{min-height:1.8rem}.md-feedback__icon{color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;margin:0 .1rem;transition:color 125ms}.md-feedback__icon:not(:disabled).md-icon:hover{color:var(--md-accent-fg-color)}.md-feedback__icon:disabled{color:var(--md-default-fg-color--lightest);pointer-events:none}.md-feedback__note{opacity:0;position:relative;transform:translateY(.4rem);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-feedback__note>*{margin:0 auto;max-width:16rem}:disabled .md-feedback__note{opacity:1;transform:translateY(0)}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{justify-content:space-between;overflow:auto;padding:.2rem}.md-footer__inner:not([hidden]){display:flex}.md-footer__link{align-items:end;display:flex;flex-grow:0.01;margin-bottom:.4rem;margin-top:1rem;max-width:100%;outline-color:var(--md-accent-fg-color);overflow:hidden;transition:opacity .25s}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}[dir=rtl] .md-footer__link svg{transform:scaleX(-1)}@media screen and (max-width:44.9375em){.md-footer__link--prev{flex-shrink:0}.md-footer__link--prev .md-footer__title{display:none}}[dir=ltr] .md-footer__link--next{margin-left:auto}[dir=rtl] .md-footer__link--next{margin-right:auto}.md-footer__link--next{text-align:right}[dir=rtl] .md-footer__link--next{text-align:left}.md-footer__title{flex-grow:1;font-size:.9rem;margin-bottom:.7rem;max-width:calc(100% - 2.4rem);padding:0 1rem;white-space:nowrap}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-copyright{width:auto}}.md-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-social{display:inline-flex;gap:.2rem;margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-social{padding:.6rem 0}}.md-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-social__link:before{line-height:1.9}.md-social__link svg{fill:currentcolor;max-height:.8rem;vertical-align:-25%}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[dir=ltr] .md-typeset .md-input{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .md-input,[dir=rtl] .md-typeset .md-input{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .md-input{border-top-left-radius:.1rem}.md-typeset .md-input{border-bottom:.1rem solid var(--md-default-fg-color--lighter);box-shadow:var(--md-shadow-z1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:border .25s,box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{border-bottom-color:var(--md-accent-fg-color);box-shadow:var(--md-shadow-z2)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem #0000,0 .2rem .4rem #0000;color:var(--md-primary-bg-color);display:block;left:0;position:sticky;right:0;top:0;z-index:4}@media print{.md-header{display:none}}.md-header[hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header--shadow{box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentcolor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.1875em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentcolor;display:block;height:1.2rem;width:auto}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;white-space:nowrap}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__topic:first-child{font-weight:700}[dir=ltr] .md-header__title{margin-left:1rem}[dir=rtl] .md-header__title{margin-right:1rem}[dir=ltr] .md-header__title{margin-right:.4rem}[dir=rtl] .md-header__title{margin-left:.4rem}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem}.md-header__title--active .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title--active .md-header__topic{transform:translateX(1.25rem)}.md-header__title--active .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__option>input{bottom:0}.md-header__source{display:none}@media screen and (min-width:60em){[dir=ltr] .md-header__source{margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem}.md-header__source{display:block;max-width:11.7rem;width:11.7rem}}@media screen and (min-width:76.25em){[dir=ltr] .md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-meta{color:var(--md-default-fg-color--light);font-size:.7rem;line-height:1.3}.md-meta__list{display:inline-flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.md-meta__item:not(:last-child):after{content:"·";margin-left:.2rem;margin-right:.2rem}.md-meta__link{color:var(--md-typeset-a-color)}.md-meta__link:focus,.md-meta__link:hover{color:var(--md-accent-fg-color)}.md-draft{background-color:#ff1744;border-radius:.125em;color:#fff;display:inline-block;font-weight:700;padding-left:.5714285714em;padding-right:.5714285714em}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{color:var(--md-default-fg-color--light);display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentcolor;display:block;height:2.4rem;max-width:100%;object-fit:contain;width:auto}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__item{padding:0 .6rem}[dir=ltr] .md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-left:0}.md-nav__link{align-items:flex-start;display:flex;margin-top:.625em;scroll-snap-align:start;transition:color 125ms}.md-nav__link--passed{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active,.md-nav__item .md-nav__link--active code{color:var(--md-typeset-a-color)}.md-nav__link .md-ellipsis{position:relative}.md-nav__link .md-icon:last-child{margin-left:auto}.md-nav__link svg{fill:currentcolor;flex-shrink:0;height:1.3em}[dir=ltr] .md-nav__link svg+*{margin-left:.4rem}[dir=rtl] .md-nav__link svg+*{margin-right:.4rem}.md-nav__link:not(.md-nav__container):focus,.md-nav__link:not(.md-nav__container):hover{color:var(--md-accent-fg-color);cursor:pointer}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentcolor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__container>.md-nav__link{margin-top:0}.md-nav__container>.md-nav__link:first-child{flex-grow:1}.md-nav__icon{flex-shrink:0}.md-nav__source{display:none}@media screen and (max-width:76.1875em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}[dir=ltr] .md-nav--primary .md-nav__title .md-nav__icon{left:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);font-weight:700}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;right:.2rem;top:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest);padding:0}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link svg{margin-top:.1em}.md-nav--primary .md-nav__link>.md-nav__link{padding:0}[dir=ltr] .md-nav--primary .md-nav__link .md-nav__icon{margin-right:-.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{margin-left:-.2rem}.md-nav--primary .md-nav__link .md-nav__icon{font-size:1.2rem;height:1.2rem;width:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav{background-color:initial;position:static}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.9375em){.md-nav--primary .md-nav__link[for=__toc]{display:flex}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-nav--integrated .md-nav__link[for=__toc]{display:flex}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav--secondary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}}@media screen and (min-width:76.25em){.md-nav{transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon,.md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav,.md-nav__toggle:indeterminate~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{font-weight:700}.md-nav__item--section>.md-nav__link[for]{color:var(--md-default-fg-color--light)}.md-nav__item--section>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav__item--section>.md-nav__link .md-nav__icon{display:none}.md-nav__item--section>.md-nav{display:block}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{border-radius:100%;height:.9rem;transition:background-color .25s;width:.9rem}.md-nav__icon:hover{background-color:var(--md-accent-fg-color--transparent)}.md-nav__icon:after{background-color:currentcolor;border-radius:100%;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:transform .25s;vertical-align:-.1rem;width:100%}[dir=rtl] .md-nav__icon:after{transform:rotate(180deg)}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon:after,.md-nav__item--nested .md-nav__toggle:indeterminate~.md-nav__link .md-nav__icon:after{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__list>.md-nav__item--nested,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block;padding:0}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);font-weight:700;margin-top:0;padding:0 .6rem;position:sticky;top:0;z-index:1}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link .md-nav__icon{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item>[for]{color:var(--md-default-fg-color--light)}.md-nav--lifted .md-nav[data-md-level="1"]{display:block}[dir=ltr] .md-nav--lifted .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-right:.6rem}[dir=rtl] .md-nav--lifted .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-left:.6rem}.md-nav--integrated>.md-nav__list>.md-nav__item--active:not(.md-nav__item--nested){padding:0 .6rem}.md-nav--integrated>.md-nav__list>.md-nav__item--active:not(.md-nav__item--nested)>.md-nav__link{padding:0}[dir=ltr] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-left:.05rem solid var(--md-primary-fg-color)}[dir=rtl] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-right:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{display:block;margin-bottom:1.25em}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__title{display:none}}.md-pagination{font-size:.8rem;font-weight:700;gap:.4rem}.md-pagination,.md-pagination>*{align-items:center;display:flex;justify-content:center}.md-pagination>*{border-radius:.2rem;height:1.8rem;min-width:1.8rem;text-align:center}.md-pagination__current{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light)}.md-pagination__link{transition:color 125ms,background-color 125ms}.md-pagination__link:focus,.md-pagination__link:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-pagination__link:focus svg,.md-pagination__link:hover svg{color:var(--md-accent-fg-color)}.md-pagination__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-pagination__link svg{fill:currentcolor;color:var(--md-default-fg-color--lighter);display:block;max-height:100%;width:1.2rem}.md-post__back{border-bottom:.05rem solid var(--md-default-fg-color--lightest);margin-bottom:1.2rem;padding-bottom:1.2rem}@media screen and (max-width:76.1875em){.md-post__back{display:none}}[dir=rtl] .md-post__back svg{transform:scaleX(-1)}.md-post__authors{display:flex;flex-direction:column;gap:.6rem;margin:0 .6rem}.md-post .md-post__meta a{transition:color 125ms}.md-post .md-post__meta a:focus,.md-post .md-post__meta a:hover{color:var(--md-accent-fg-color)}.md-post--excerpt{margin-bottom:3.2rem}.md-post--excerpt .md-post__header{align-items:center;display:flex;gap:.6rem;min-height:1.6rem}.md-post--excerpt .md-post__authors{align-items:center;display:inline-flex;flex-direction:row;gap:.2rem;margin:0;min-height:2.4rem}[dir=ltr] .md-post--excerpt .md-post__meta .md-meta__list{margin-right:.4rem}[dir=rtl] .md-post--excerpt .md-post__meta .md-meta__list{margin-left:.4rem}.md-post--excerpt .md-post__content>:first-child{--md-scroll-margin:6rem;margin-top:0}.md-post>.md-nav--secondary,.md-post>.md-nav:first-child>.md-nav__list{margin:1em 0}.md-profile{align-items:center;display:flex;font-size:.7rem;gap:.6rem;line-height:1.4;width:100%}.md-profile__description{flex-grow:1}.md-content--post{display:flex}@media screen and (max-width:76.1875em){.md-content--post{flex-flow:column-reverse}}.md-content--post>.md-content__inner{min-width:0}@media screen and (min-width:76.25em){[dir=ltr] .md-content--post>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-content--post>.md-content__inner{margin-right:1.2rem}}@media screen and (max-width:76.1875em){.md-sidebar.md-sidebar--post{padding:0}}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.9375em){[dir=ltr] .md-search__overlay{left:-2.2rem}[dir=rtl] .md-search__overlay{right:-2.2rem}.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){[dir=ltr] .md-search__overlay{left:0}[dir=rtl] .md-search__overlay{right:0}.md-search__overlay{background-color:#0000008a;cursor:pointer;height:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.9375em){[dir=ltr] .md-search__inner{left:0}[dir=rtl] .md-search__inner{right:0}.md-search__inner{height:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__inner{float:right}[dir=rtl] .md-search__inner{float:left}.md-search__inner{padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}}@media screen and (min-width:60em) and (max-width:76.1875em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem #0000;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:#00000042;border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:#ffffff1f}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem #00000012;color:var(--md-default-fg-color)}[dir=ltr] .md-search__input{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__input{padding-left:2.2rem;padding-right:3.6rem}.md-search__input{background:#0000;font-size:.9rem;height:100%;position:relative;text-overflow:ellipsis;width:100%;z-index:2}.md-search__input::placeholder{transition:color .25s}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.9375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__input{padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input{color:inherit;font-size:.8rem}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:#0000}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}[dir=ltr] .md-search__icon[for=__search]{left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem}.md-search__icon[for=__search]{position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.9375em){[dir=ltr] .md-search__icon[for=__search]{left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem}.md-search__icon[for=__search]{top:.6rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}[dir=ltr] .md-search__options{right:.5rem}[dir=rtl] .md-search__options{left:.5rem}.md-search__options{pointer-events:none;position:absolute;top:.3rem;z-index:2}@media screen and (max-width:59.9375em){[dir=ltr] .md-search__options{right:.8rem}[dir=rtl] .md-search__options{left:.8rem}.md-search__options{top:.6rem}}[dir=ltr] .md-search__options>.md-icon{margin-left:.2rem}[dir=rtl] .md-search__options>.md-icon{margin-right:.2rem}.md-search__options>.md-icon{color:var(--md-default-fg-color--light);opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>.md-icon:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon:hover{opacity:.7}[dir=ltr] .md-search__suggest{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__suggest{padding-left:2.2rem;padding-right:3.6rem}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}@media screen and (min-width:60em){[dir=ltr] .md-search__suggest{padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}.md-search__suggest{font-size:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}[dir=ltr] .md-search__output{border-bottom-left-radius:.1rem}[dir=ltr] .md-search__output,[dir=rtl] .md-search__output{border-bottom-right-radius:.1rem}[dir=rtl] .md-search__output{border-bottom-left-radius:.1rem}.md-search__output{overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.9375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:var(--md-shadow-z3);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){[dir=ltr] .md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0;-webkit-user-select:none;user-select:none}.md-search-result__item{box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more>summary{cursor:pointer;display:block;outline:none;position:sticky;scroll-snap-align:start;top:0;z-index:1}.md-search-result__more>summary::marker{display:none}.md-search-result__more>summary::-webkit-details-marker{display:none}.md-search-result__more>summary>div{color:var(--md-typeset-a-color);font-size:.64rem;padding:.75em .8rem;transition:color .25s,background-color .25s}@media screen and (min-width:60em){[dir=ltr] .md-search-result__more>summary>div{padding-left:2.2rem}[dir=rtl] .md-search-result__more>summary>div{padding-right:2.2rem}}.md-search-result__more>summary:focus>div,.md-search-result__more>summary:hover>div{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more[open]>summary{background-color:var(--md-default-bg-color)}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){[dir=ltr] .md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem}}[dir=ltr] .md-search-result__icon{left:0}[dir=rtl] .md-search-result__icon{right:0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.9375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentcolor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result .md-typeset{color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6}.md-search-result .md-typeset h1{color:var(--md-default-fg-color);font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result .md-typeset h1 mark{text-decoration:none}.md-search-result .md-typeset h2{color:var(--md-default-fg-color);font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result .md-typeset h2 mark{text-decoration:none}.md-search-result__terms{color:var(--md-default-fg-color);display:block;font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:initial;color:var(--md-accent-fg-color);text-decoration:underline}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid #0000;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid #0000;border-right:.2rem solid #0000;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}[dir=ltr] .md-select__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.1875em){[dir=ltr] .md-sidebar--primary{left:-12.1rem}[dir=rtl] .md-sidebar--primary{right:-12.1rem}.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:5}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:var(--md-shadow-z3);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}.md-header--lifted~.md-container .md-sidebar{top:4.8rem}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{scrollbar-gutter:stable;-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap:focus-within,.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb:hover,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@supports selector(::-webkit-scrollbar){.md-sidebar__scrollwrap{scrollbar-gutter:auto}[dir=ltr] .md-sidebar__inner{padding-right:calc(100% - 11.5rem)}[dir=rtl] .md-sidebar__inner{padding-left:calc(100% - 11.5rem)}}@media screen and (max-width:76.1875em){.md-overlay{background-color:#0000008a;height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:5}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@keyframes facts{0%{height:0}to{height:.65rem}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}[dir=ltr] .md-source__icon svg{margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem}.md-source__icon svg{margin-top:.6rem}[dir=ltr] .md-source__icon+.md-source__repository{padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{padding-right:2rem}[dir=ltr] .md-source__icon+.md-source__repository{margin-left:-2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem}[dir=ltr] .md-source__repository{margin-left:.6rem}[dir=rtl] .md-source__repository{margin-right:.6rem}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{display:flex;font-size:.55rem;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source__repository--active .md-source__facts{animation:facts .25s ease-in}.md-source__fact{overflow:hidden;text-overflow:ellipsis}.md-source__repository--active .md-source__fact{animation:fact .4s ease-out}[dir=ltr] .md-source__fact:before{margin-right:.1rem}[dir=rtl] .md-source__fact:before{margin-left:.1rem}.md-source__fact:before{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}.md-source__fact:nth-child(1n+2){flex-shrink:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}:root{--md-status:url('data:image/svg+xml;charset=utf-8,');--md-status--new:url('data:image/svg+xml;charset=utf-8,');--md-status--deprecated:url('data:image/svg+xml;charset=utf-8,');--md-status--encrypted:url('data:image/svg+xml;charset=utf-8,')}.md-status{margin-left:.2rem}.md-status:after{background-color:var(--md-default-fg-color--light);content:"";display:inline-block;height:1.125em;-webkit-mask-image:var(--md-status);mask-image:var(--md-status);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-bottom;width:1.125em}.md-status:hover:after{background-color:currentcolor}.md-status--new:after{-webkit-mask-image:var(--md-status--new);mask-image:var(--md-status--new)}.md-status--deprecated:after{-webkit-mask-image:var(--md-status--deprecated);mask-image:var(--md-status--deprecated)}.md-status--encrypted:after{-webkit-mask-image:var(--md-status--encrypted);mask-image:var(--md-status--encrypted)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);display:block;line-height:1.3;overflow:auto;width:100%;z-index:3}@media print{.md-tabs{display:none}}@media screen and (max-width:76.1875em){.md-tabs{display:none}}.md-tabs[hidden]{pointer-events:none}[dir=ltr] .md-tabs__list{margin-left:.2rem}[dir=rtl] .md-tabs__list{margin-right:.2rem}.md-tabs__list{contain:content;display:flex;list-style:none;margin:0;overflow:auto;padding:0;scrollbar-width:none;white-space:nowrap}.md-tabs__list::-webkit-scrollbar{display:none}.md-tabs__item{height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__item--active .md-tabs__link{color:inherit;opacity:1}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:flex;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}[dir=ltr] .md-tabs__link svg{margin-right:.4rem}[dir=rtl] .md-tabs__link svg{margin-left:.4rem}.md-tabs__link svg{fill:currentcolor;height:1.3em}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}:root{--md-tag-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-tags{margin-bottom:.75em;margin-top:-.125em}[dir=ltr] .md-typeset .md-tag{margin-right:.5em}[dir=rtl] .md-typeset .md-tag{margin-left:.5em}.md-typeset .md-tag{background:var(--md-default-fg-color--lightest);border-radius:2.4rem;display:inline-block;font-size:.64rem;font-weight:700;letter-spacing:normal;line-height:1.6;margin-bottom:.5em;padding:.3125em .9375em}.md-typeset .md-tag[href]{-webkit-tap-highlight-color:transparent;color:inherit;outline:none;transition:color 125ms,background-color 125ms}.md-typeset .md-tag[href]:focus,.md-typeset .md-tag[href]:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[id]>.md-typeset .md-tag{vertical-align:text-top}.md-typeset .md-tag-icon:before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline-block;height:1.2em;margin-right:.4em;-webkit-mask-image:var(--md-tag-icon);mask-image:var(--md-tag-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset .md-tag-icon[href]:focus:before,.md-typeset .md-tag-icon[href]:hover:before{background-color:var(--md-accent-bg-color)}@keyframes pulse{0%{transform:scale(.95)}75%{transform:scale(1)}to{transform:scale(.95)}}:root{--md-annotation-bg-icon:url('data:image/svg+xml;charset=utf-8,');--md-annotation-icon:url('data:image/svg+xml;charset=utf-8,');--md-tooltip-width:20rem}.md-tooltip{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);font-family:var(--md-text-font-family);left:clamp(var(--md-tooltip-0,0rem) + .8rem,var(--md-tooltip-x),100vw + var(--md-tooltip-0,0rem) + .8rem - var(--md-tooltip-width) - 2 * .8rem);max-width:calc(100vw - 1.6rem);opacity:0;position:absolute;top:var(--md-tooltip-y);transform:translateY(-.4rem);transition:transform 0ms .25s,opacity .25s,z-index .25s;width:var(--md-tooltip-width);z-index:0}.md-tooltip--active{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,z-index 0ms;z-index:2}.focus-visible>.md-tooltip,.md-tooltip:target{outline:var(--md-accent-fg-color) auto}.md-tooltip__inner{font-size:.64rem;padding:.8rem}.md-tooltip__inner.md-typeset>:first-child{margin-top:0}.md-tooltip__inner.md-typeset>:last-child{margin-bottom:0}.md-annotation{font-weight:400;outline:none;vertical-align:text-bottom;white-space:normal}[dir=rtl] .md-annotation{direction:rtl}code .md-annotation{font-family:var(--md-code-font-family);font-size:inherit}.md-annotation:not([hidden]){display:inline-block;line-height:1.25}.md-annotation__index{border-radius:.01px;cursor:pointer;display:inline-block;margin-left:.4ch;margin-right:.4ch;outline:none;overflow:hidden;position:relative;-webkit-user-select:none;user-select:none;vertical-align:text-top;z-index:0}.md-annotation .md-annotation__index{transition:z-index .25s}@media screen{.md-annotation__index{width:2.2ch}[data-md-visible]>.md-annotation__index{animation:pulse 2s infinite}.md-annotation__index:before{background:var(--md-default-bg-color);-webkit-mask-image:var(--md-annotation-bg-icon);mask-image:var(--md-annotation-bg-icon)}.md-annotation__index:after,.md-annotation__index:before{content:"";height:2.2ch;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:-.1ch;width:2.2ch;z-index:-1}.md-annotation__index:after{background-color:var(--md-default-fg-color--lighter);-webkit-mask-image:var(--md-annotation-icon);mask-image:var(--md-annotation-icon);transform:scale(1.0001);transition:background-color .25s,transform .25s}.md-tooltip--active+.md-annotation__index:after{transform:rotate(45deg)}.md-tooltip--active+.md-annotation__index:after,:hover>.md-annotation__index:after{background-color:var(--md-accent-fg-color)}}.md-tooltip--active+.md-annotation__index{animation-play-state:paused;transition-duration:0ms;z-index:2}.md-annotation__index [data-md-annotation-id]{display:inline-block}@media print{.md-annotation__index [data-md-annotation-id]{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);font-weight:700;padding:0 .6ch;white-space:nowrap}.md-annotation__index [data-md-annotation-id]:after{content:attr(data-md-annotation-id)}}.md-typeset .md-annotation-list{counter-reset:xxx;list-style:none}.md-typeset .md-annotation-list li{position:relative}[dir=ltr] .md-typeset .md-annotation-list li:before{left:-2.125em}[dir=rtl] .md-typeset .md-annotation-list li:before{right:-2.125em}.md-typeset .md-annotation-list li:before{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);content:counter(xxx);counter-increment:xxx;font-size:.8875em;font-weight:700;height:2ch;line-height:1.25;min-width:2ch;padding:0 .6ch;position:absolute;text-align:center;top:.25em}[dir=ltr] .md-top{margin-left:50%}[dir=rtl] .md-top{margin-right:50%}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color--light);cursor:pointer;display:block;font-size:.7rem;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{transform:translate(50%)}.md-top[hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}[dir=rtl] .md-top[hidden]{transform:translate(50%,.2rem)}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}[dir=ltr] .md-version__current{margin-left:1.4rem;margin-right:.4rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current{color:inherit;cursor:pointer;outline:none;position:relative;top:.05rem}[dir=ltr] .md-version__current:after{margin-left:.4rem}[dir=rtl] .md-version__current:after{margin-right:.4rem}.md-version__current:after{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.4rem}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:3}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (hover:none),(pointer:coarse){.md-version:hover .md-version__list{animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{animation:none}}.md-version__item{line-height:1.8rem}[dir=ltr] .md-version__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border:.05rem solid #448aff;border-radius:.2rem;box-shadow:var(--md-shadow-z1);color:var(--md-admonition-fg-color);display:flow-root;font-size:.64rem;margin:1.5625em 0;padding:0 .6rem;page-break-inside:avoid;transition:box-shadow 125ms}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}.md-typeset .admonition:focus-within,.md-typeset details:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .admonition>*,.md-typeset details>*{box-sizing:border-box}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{padding-left:2rem;padding-right:.6rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{padding-left:.6rem;padding-right:2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-left-width:.2rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-right-width:.2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset .admonition-title,.md-typeset summary{background-color:#448aff1a;border:none;font-weight:700;margin:0 -.6rem;padding-bottom:.4rem;padding-top:.4rem;position:relative}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}[dir=ltr] .md-typeset .admonition-title:before,[dir=ltr] .md-typeset summary:before{left:.6rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;width:1rem}.md-typeset .admonition-title code,.md-typeset summary code{box-shadow:0 0 0 .05rem var(--md-default-fg-color--lightest)}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .admonition.note:focus-within,.md-typeset details.note:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:#448aff1a}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note)}.md-typeset .note>.admonition-title:after,.md-typeset .note>summary:after{color:#448aff}.md-typeset .admonition.abstract,.md-typeset details.abstract{border-color:#00b0ff}.md-typeset .admonition.abstract:focus-within,.md-typeset details.abstract:focus-within{box-shadow:0 0 0 .2rem #00b0ff1a}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary{background-color:#00b0ff1a}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract)}.md-typeset .abstract>.admonition-title:after,.md-typeset .abstract>summary:after{color:#00b0ff}.md-typeset .admonition.info,.md-typeset details.info{border-color:#00b8d4}.md-typeset .admonition.info:focus-within,.md-typeset details.info:focus-within{box-shadow:0 0 0 .2rem #00b8d41a}.md-typeset .info>.admonition-title,.md-typeset .info>summary{background-color:#00b8d41a}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info)}.md-typeset .info>.admonition-title:after,.md-typeset .info>summary:after{color:#00b8d4}.md-typeset .admonition.tip,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .admonition.tip:focus-within,.md-typeset details.tip:focus-within{box-shadow:0 0 0 .2rem #00bfa51a}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:#00bfa51a}.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip)}.md-typeset .tip>.admonition-title:after,.md-typeset .tip>summary:after{color:#00bfa5}.md-typeset .admonition.success,.md-typeset details.success{border-color:#00c853}.md-typeset .admonition.success:focus-within,.md-typeset details.success:focus-within{box-shadow:0 0 0 .2rem #00c8531a}.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:#00c8531a}.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success)}.md-typeset .success>.admonition-title:after,.md-typeset .success>summary:after{color:#00c853}.md-typeset .admonition.question,.md-typeset details.question{border-color:#64dd17}.md-typeset .admonition.question:focus-within,.md-typeset details.question:focus-within{box-shadow:0 0 0 .2rem #64dd171a}.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:#64dd171a}.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question)}.md-typeset .question>.admonition-title:after,.md-typeset .question>summary:after{color:#64dd17}.md-typeset .admonition.warning,.md-typeset details.warning{border-color:#ff9100}.md-typeset .admonition.warning:focus-within,.md-typeset details.warning:focus-within{box-shadow:0 0 0 .2rem #ff91001a}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:#ff91001a}.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning)}.md-typeset .warning>.admonition-title:after,.md-typeset .warning>summary:after{color:#ff9100}.md-typeset .admonition.failure,.md-typeset details.failure{border-color:#ff5252}.md-typeset .admonition.failure:focus-within,.md-typeset details.failure:focus-within{box-shadow:0 0 0 .2rem #ff52521a}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary{background-color:#ff52521a}.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure)}.md-typeset .failure>.admonition-title:after,.md-typeset .failure>summary:after{color:#ff5252}.md-typeset .admonition.danger,.md-typeset details.danger{border-color:#ff1744}.md-typeset .admonition.danger:focus-within,.md-typeset details.danger:focus-within{box-shadow:0 0 0 .2rem #ff17441a}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary{background-color:#ff17441a}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger)}.md-typeset .danger>.admonition-title:after,.md-typeset .danger>summary:after{color:#ff1744}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .admonition.bug:focus-within,.md-typeset details.bug:focus-within{box-shadow:0 0 0 .2rem #f500571a}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:#f500571a}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug)}.md-typeset .bug>.admonition-title:after,.md-typeset .bug>summary:after{color:#f50057}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .admonition.example:focus-within,.md-typeset details.example:focus-within{box-shadow:0 0 0 .2rem #7c4dff1a}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:#7c4dff1a}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example)}.md-typeset .example>.admonition-title:after,.md-typeset .example>summary:after{color:#7c4dff}.md-typeset .admonition.quote,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .admonition.quote:focus-within,.md-typeset details.quote:focus-within{box-shadow:0 0 0 .2rem #9e9e9e1a}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:#9e9e9e1a}.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote)}.md-typeset .quote>.admonition-title:after,.md-typeset .quote>summary:after{color:#9e9e9e}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}[dir=ltr] .md-typeset .footnote>ol{margin-left:0}[dir=rtl] .md-typeset .footnote>ol{margin-right:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:focus-within .footnote-backref{opacity:1;transform:translateX(0);transition:none}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentcolor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}[dir=ltr] .md-typeset .headerlink{margin-left:.5rem}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{--md-scroll-margin:3.6rem;--md-scroll-offset:0rem;scroll-margin-top:calc(var(--md-scroll-margin) - var(--md-scroll-offset))}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{--md-scroll-margin:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{--md-scroll-offset:0.2rem}.md-typeset h4:target{--md-scroll-offset:0.15rem}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.9375em){.md-typeset div.arithmatex{margin:0 -.8rem}}.md-typeset div.arithmatex>*{margin-left:auto!important;margin-right:auto!important;padding:0 .8rem;touch-action:auto;width:-webkit-min-content;width:min-content}.md-typeset div.arithmatex>* mjx-container{margin:0!important}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{-webkit-box-decoration-break:clone;box-decoration-break:clone;color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}[dir=ltr] .md-typeset summary{padding-right:1.8rem}[dir=rtl] .md-typeset summary{padding-left:1.8rem}[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset summary{cursor:pointer;display:block;min-height:1rem}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[dir=ltr] .md-typeset summary:after{right:.4rem}[dir=rtl] .md-typeset summary:after{left:.4rem}.md-typeset summary:after{background-color:currentcolor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{transform:rotate(180deg)}.md-typeset summary::marker{display:none}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{display:inline-flex;height:1.125em;vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentcolor;max-height:100%;width:1.125em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color--light);box-shadow:2px 0 0 0 var(--md-code-hl-color) inset;display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight span.filename{background-color:var(--md-code-bg-color);border-bottom:.05rem solid var(--md-default-fg-color--lightest);border-top-left-radius:.1rem;border-top-right-radius:.1rem;display:flow-root;font-size:.85em;font-weight:700;margin-top:1em;padding:.6617647059em 1.1764705882em;position:relative}.highlight span.filename+pre{margin-top:0}.highlight span.filename+pre>code{border-top-left-radius:0;border-top-right-radius:0}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:sticky;-webkit-user-select:none;user-select:none;z-index:3}.highlight code a[id]{position:absolute;visibility:hidden}.highlight code[data-md-copying] .hll{display:contents}.highlight code[data-md-copying] .md-annotation{display:none}.highlighttable{display:flow-root}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable th.filename{flex-grow:1;padding:0;text-align:left}.highlighttable th.filename span.filename{margin-top:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-top-left-radius:.1rem;font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;padding-right:.5882352941em}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;min-width:0}.linenodiv a{color:inherit}.md-typeset .highlighttable{direction:ltr;margin:1em 0}.md-typeset .highlighttable>tbody>tr>.code>div>pre>code{border-bottom-left-radius:0;border-top-left-radius:0}.md-typeset .highlight+.result{border:.05rem solid var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-bottom-right-radius:.1rem;border-top-width:.1rem;margin-top:-1.125em;overflow:visible;padding:0 1em}.md-typeset .highlight+.result:after{clear:both;content:"";display:block}@media screen and (max-width:44.9375em){.md-content__inner>.highlight{margin:1em -.8rem}.md-content__inner>.highlight>.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.code>div>pre>code,.md-content__inner>.highlight>.highlighttable>tbody>tr>.filename span.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.linenos,.md-content__inner>.highlight>pre>code{border-radius:0}.md-content__inner>.highlight+.result{border-left-width:0;border-radius:0;border-right-width:0;margin-left:-.8rem;margin-right:-.8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"↓";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"←";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"→";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"↑";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"⌫";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"⇤";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"⇪";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"⌧";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"☰";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"⌦";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"⏏";padding-right:.4em}.md-typeset .keys .key-end:before{content:"⤓";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"⎋";padding-right:.4em}.md-typeset .keys .key-home:before{content:"⤒";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"⎀";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"⇟";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"⇞";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"⎙";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"⇥";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"⌤";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"⏎";padding-left:.4em}:root{--md-tabbed-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-tabbed-icon--next:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-flow:column wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:target{--md-scroll-offset:0.625em}.md-typeset .tabbed-labels{-ms-overflow-style:none;box-shadow:0 -.05rem var(--md-default-fg-color--lightest) inset;display:flex;max-width:100%;overflow:auto;scrollbar-width:none}@media print{.md-typeset .tabbed-labels{display:contents}}@media screen{.js .md-typeset .tabbed-labels{position:relative}.js .md-typeset .tabbed-labels:before{background:var(--md-accent-fg-color);bottom:0;content:"";display:block;height:2px;left:0;position:absolute;transform:translateX(var(--md-indicator-x));transition:width 225ms,transform .25s;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--md-indicator-width)}}.md-typeset .tabbed-labels::-webkit-scrollbar{display:none}.md-typeset .tabbed-labels>label{border-bottom:.1rem solid #0000;border-radius:.1rem .1rem 0 0;color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;font-size:.64rem;font-weight:700;padding:.78125em 1.25em .625em;scroll-margin-inline-start:1rem;transition:background-color .25s,color .25s;white-space:nowrap;width:auto}@media print{.md-typeset .tabbed-labels>label:first-child{order:1}.md-typeset .tabbed-labels>label:nth-child(2){order:2}.md-typeset .tabbed-labels>label:nth-child(3){order:3}.md-typeset .tabbed-labels>label:nth-child(4){order:4}.md-typeset .tabbed-labels>label:nth-child(5){order:5}.md-typeset .tabbed-labels>label:nth-child(6){order:6}.md-typeset .tabbed-labels>label:nth-child(7){order:7}.md-typeset .tabbed-labels>label:nth-child(8){order:8}.md-typeset .tabbed-labels>label:nth-child(9){order:9}.md-typeset .tabbed-labels>label:nth-child(10){order:10}.md-typeset .tabbed-labels>label:nth-child(11){order:11}.md-typeset .tabbed-labels>label:nth-child(12){order:12}.md-typeset .tabbed-labels>label:nth-child(13){order:13}.md-typeset .tabbed-labels>label:nth-child(14){order:14}.md-typeset .tabbed-labels>label:nth-child(15){order:15}.md-typeset .tabbed-labels>label:nth-child(16){order:16}.md-typeset .tabbed-labels>label:nth-child(17){order:17}.md-typeset .tabbed-labels>label:nth-child(18){order:18}.md-typeset .tabbed-labels>label:nth-child(19){order:19}.md-typeset .tabbed-labels>label:nth-child(20){order:20}}.md-typeset .tabbed-labels>label:hover{color:var(--md-accent-fg-color)}.md-typeset .tabbed-content{width:100%}@media print{.md-typeset .tabbed-content{display:contents}}.md-typeset .tabbed-block{display:none}@media print{.md-typeset .tabbed-block{display:block}.md-typeset .tabbed-block:first-child{order:1}.md-typeset .tabbed-block:nth-child(2){order:2}.md-typeset .tabbed-block:nth-child(3){order:3}.md-typeset .tabbed-block:nth-child(4){order:4}.md-typeset .tabbed-block:nth-child(5){order:5}.md-typeset .tabbed-block:nth-child(6){order:6}.md-typeset .tabbed-block:nth-child(7){order:7}.md-typeset .tabbed-block:nth-child(8){order:8}.md-typeset .tabbed-block:nth-child(9){order:9}.md-typeset .tabbed-block:nth-child(10){order:10}.md-typeset .tabbed-block:nth-child(11){order:11}.md-typeset .tabbed-block:nth-child(12){order:12}.md-typeset .tabbed-block:nth-child(13){order:13}.md-typeset .tabbed-block:nth-child(14){order:14}.md-typeset .tabbed-block:nth-child(15){order:15}.md-typeset .tabbed-block:nth-child(16){order:16}.md-typeset .tabbed-block:nth-child(17){order:17}.md-typeset .tabbed-block:nth-child(18){order:18}.md-typeset .tabbed-block:nth-child(19){order:19}.md-typeset .tabbed-block:nth-child(20){order:20}}.md-typeset .tabbed-block>.highlight:first-child>pre,.md-typeset .tabbed-block>pre:first-child{margin:0}.md-typeset .tabbed-block>.highlight:first-child>pre>code,.md-typeset .tabbed-block>pre:first-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child>.filename{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable{margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.filename span.filename,.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.linenos{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.code>div>pre>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child+.result{margin-top:-.125em}.md-typeset .tabbed-block>.tabbed-set{margin:0}.md-typeset .tabbed-button{align-self:center;border-radius:100%;color:var(--md-default-fg-color--light);cursor:pointer;display:block;height:.9rem;margin-top:.1rem;pointer-events:auto;transition:background-color .25s;width:.9rem}.md-typeset .tabbed-button:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .tabbed-button:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-tabbed-icon--prev);mask-image:var(--md-tabbed-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color .25s,transform .25s;width:100%}.md-typeset .tabbed-control{background:linear-gradient(to right,var(--md-default-bg-color) 60%,#0000);display:flex;height:1.9rem;justify-content:start;pointer-events:none;position:absolute;transition:opacity 125ms;width:1.2rem}[dir=rtl] .md-typeset .tabbed-control{transform:rotate(180deg)}.md-typeset .tabbed-control[hidden]{opacity:0}.md-typeset .tabbed-control--next{background:linear-gradient(to left,var(--md-default-bg-color) 60%,#0000);justify-content:end;right:0}.md-typeset .tabbed-control--next .tabbed-button:after{-webkit-mask-image:var(--md-tabbed-icon--next);mask-image:var(--md-tabbed-icon--next)}@media screen and (max-width:44.9375em){[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels{padding-right:.8rem}.md-content__inner>.tabbed-set .tabbed-labels{margin:0 -.8rem;max-width:100vw;scroll-padding-inline-start:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-left:.8rem}.md-content__inner>.tabbed-set .tabbed-labels:after{content:""}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-right:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-left:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-right:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{width:2rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-left:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-right:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-left:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{width:2rem}}@media screen{.md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){color:var(--md-accent-fg-color)}.md-typeset .no-js .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .no-js .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .no-js .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .no-js .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .no-js .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .no-js .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .no-js .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .no-js .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .no-js .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .no-js .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .no-js .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .no-js .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .no-js .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .no-js .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .no-js .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .no-js .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .no-js .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .no-js .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .no-js .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .no-js .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.no-js .md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.no-js .md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.no-js .md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.no-js .md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.no-js .md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.no-js .md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.no-js .md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.no-js .md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.no-js .md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.no-js .md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.no-js .md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.no-js .md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.no-js .md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.no-js .md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.no-js .md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.no-js .md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.no-js .md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.no-js .md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.no-js .md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.no-js .md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){border-color:var(--md-accent-fg-color)}}.md-typeset .tabbed-set>input:first-child.focus-visible~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10).focus-visible~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11).focus-visible~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12).focus-visible~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13).focus-visible~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14).focus-visible~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15).focus-visible~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16).focus-visible~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17).focus-visible~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18).focus-visible~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19).focus-visible~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2).focus-visible~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20).focus-visible~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3).focus-visible~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4).focus-visible~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5).focus-visible~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6).focus-visible~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7).focus-visible~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8).focus-visible~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9).focus-visible~.tabbed-labels>:nth-child(9){background-color:var(--md-accent-fg-color--transparent)}.md-typeset .tabbed-set>input:first-child:checked~.tabbed-content>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-content>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-content>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-content>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-content>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-content>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-content>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-content>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-content>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-content>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-content>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-content>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-content>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-content>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-content>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-content>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-content>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-content>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-content>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-content>:nth-child(9){display:block}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}[dir=ltr] .md-typeset .task-list-item [type=checkbox]{left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}[dir=ltr] .md-typeset .task-list-indicator:before{left:-1.5em}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}:root>*{--md-mermaid-font-family:var(--md-text-font-family),sans-serif;--md-mermaid-edge-color:var(--md-code-fg-color);--md-mermaid-node-bg-color:var(--md-accent-fg-color--transparent);--md-mermaid-node-fg-color:var(--md-accent-fg-color);--md-mermaid-label-bg-color:var(--md-default-bg-color);--md-mermaid-label-fg-color:var(--md-code-fg-color);--md-mermaid-sequence-actor-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actor-fg-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-actor-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-actor-line-color:var(--md-default-fg-color--lighter);--md-mermaid-sequence-actorman-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actorman-line-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-box-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-box-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-label-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-label-fg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-loop-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-loop-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-loop-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-message-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-message-line-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-note-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-border-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-number-bg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-number-fg-color:var(--md-accent-bg-color)}.mermaid{line-height:normal;margin:1em 0}@media screen and (min-width:45em){[dir=ltr] .md-typeset .inline{float:left}[dir=rtl] .md-typeset .inline{float:right}[dir=ltr] .md-typeset .inline{margin-right:.8rem}[dir=rtl] .md-typeset .inline{margin-left:.8rem}.md-typeset .inline{margin-bottom:.8rem;margin-top:0;width:11.7rem}[dir=ltr] .md-typeset .inline.end{float:right}[dir=rtl] .md-typeset .inline.end{float:left}[dir=ltr] .md-typeset .inline.end{margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{margin-left:0;margin-right:.8rem}}
\ No newline at end of file
diff --git a/assets/stylesheets/main.30068a00.min.css.map b/assets/stylesheets/main.30068a00.min.css.map
new file mode 100644
index 00000000..23cd93fc
--- /dev/null
+++ b/assets/stylesheets/main.30068a00.min.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["src/assets/stylesheets/main/components/_meta.scss","../../../src/assets/stylesheets/main.scss","src/assets/stylesheets/main/_resets.scss","src/assets/stylesheets/main/_colors.scss","src/assets/stylesheets/main/_icons.scss","src/assets/stylesheets/main/_typeset.scss","src/assets/stylesheets/utilities/_break.scss","src/assets/stylesheets/main/components/_author.scss","src/assets/stylesheets/main/components/_banner.scss","src/assets/stylesheets/main/components/_base.scss","src/assets/stylesheets/main/components/_clipboard.scss","src/assets/stylesheets/main/components/_consent.scss","src/assets/stylesheets/main/components/_content.scss","src/assets/stylesheets/main/components/_dialog.scss","src/assets/stylesheets/main/components/_feedback.scss","src/assets/stylesheets/main/components/_footer.scss","src/assets/stylesheets/main/components/_form.scss","src/assets/stylesheets/main/components/_header.scss","node_modules/material-design-color/material-color.scss","src/assets/stylesheets/main/components/_nav.scss","src/assets/stylesheets/main/components/_pagination.scss","src/assets/stylesheets/main/components/_post.scss","src/assets/stylesheets/main/components/_search.scss","src/assets/stylesheets/main/components/_select.scss","src/assets/stylesheets/main/components/_sidebar.scss","src/assets/stylesheets/main/components/_source.scss","src/assets/stylesheets/main/components/_status.scss","src/assets/stylesheets/main/components/_tabs.scss","src/assets/stylesheets/main/components/_tag.scss","src/assets/stylesheets/main/components/_tooltip.scss","src/assets/stylesheets/main/components/_top.scss","src/assets/stylesheets/main/components/_version.scss","src/assets/stylesheets/main/extensions/markdown/_admonition.scss","src/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/assets/stylesheets/main/extensions/markdown/_toc.scss","src/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/assets/stylesheets/main/integrations/_mermaid.scss","src/assets/stylesheets/main/_modifiers.scss"],"names":[],"mappings":"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C/BgpGN,C+B3oGE,4DACE,0B/B8oGJ,C+B3oGI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yC/B6oGN,C+BzoGI,wEACE,a/B2oGN,C+BhqGE,4DACE,oB/BmqGJ,C+BhqGI,sFACE,gC/BkqGN,C+B7pGE,oEACE,0B/BgqGJ,C+B7pGI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6C/B+pGN,C+B3pGI,gFACE,a/B6pGN,C+BlrGE,8DACE,oB/BqrGJ,C+BlrGI,wFACE,gC/BorGN,C+B/qGE,sEACE,0B/BkrGJ,C+B/qGI,oFACE,wBAlBG,CAmBH,sDAAA,CAAA,8C/BirGN,C+B7qGI,kFACE,a/B+qGN,C+BpsGE,4DACE,oB/BusGJ,C+BpsGI,sFACE,gC/BssGN,C+BjsGE,oEACE,0B/BosGJ,C+BjsGI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6C/BmsGN,C+B/rGI,gFACE,a/BisGN,C+BttGE,4DACE,oB/BytGJ,C+BttGI,sFACE,gC/BwtGN,C+BntGE,oEACE,0B/BstGJ,C+BntGI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6C/BqtGN,C+BjtGI,gFACE,a/BmtGN,C+BxuGE,0DACE,oB/B2uGJ,C+BxuGI,oFACE,gC/B0uGN,C+BruGE,kEACE,0B/BwuGJ,C+BruGI,gFACE,wBAlBG,CAmBH,oDAAA,CAAA,4C/BuuGN,C+BnuGI,8EACE,a/BquGN,C+B1vGE,oDACE,oB/B6vGJ,C+B1vGI,8EACE,gC/B4vGN,C+BvvGE,4DACE,0B/B0vGJ,C+BvvGI,0EACE,wBAlBG,CAmBH,iDAAA,CAAA,yC/ByvGN,C+BrvGI,wEACE,a/BuvGN,C+B5wGE,4DACE,oB/B+wGJ,C+B5wGI,sFACE,gC/B8wGN,C+BzwGE,oEACE,0B/B4wGJ,C+BzwGI,kFACE,wBAlBG,CAmBH,qDAAA,CAAA,6C/B2wGN,C+BvwGI,gFACE,a/BywGN,C+B9xGE,wDACE,oB/BiyGJ,C+B9xGI,kFACE,gC/BgyGN,C+B3xGE,gEACE,0B/B8xGJ,C+B3xGI,8EACE,wBAlBG,CAmBH,mDAAA,CAAA,2C/B6xGN,C+BzxGI,4EACE,a/B2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","file":"main.css"}
\ No newline at end of file
diff --git a/blog/index.html b/blog/index.html
index 4f27124f..1c42c758 100644
--- a/blog/index.html
+++ b/blog/index.html
@@ -36,7 +36,7 @@
-
+
@@ -44,7 +44,7 @@
-
+
diff --git a/config/index.html b/config/index.html
index c3f10836..e3e0c857 100644
--- a/config/index.html
+++ b/config/index.html
@@ -40,7 +40,7 @@
-
+
@@ -48,7 +48,7 @@
-
+
diff --git a/default_dict/index.html b/default_dict/index.html
index 9461d7dd..0e42aa5d 100644
--- a/default_dict/index.html
+++ b/default_dict/index.html
@@ -40,7 +40,7 @@
-
+
@@ -48,7 +48,7 @@
-
+
diff --git a/feed_rss_created.xml b/feed_rss_created.xml
index 68c64017..1583c81b 100644
--- a/feed_rss_created.xml
+++ b/feed_rss_created.xml
@@ -1 +1 @@
-
CHANfiG aims to make your configuration easier.
There are tons of configurable parameters in training a Machine Learning model. To configure all these parameters, researchers usually need to write gigantic config files, sometimes even thousands of lines. Most of the configs are just replicates of the default arguments of certain functions, resulting in many unnecessary declarations. It is also very hard to alter the configurations. One needs to navigate and open the right configuration file, make changes, save and exit. These had wasted an uncountable1 amount of precious time and are no doubt a crime. Using argparse
could relieve the burdens to some extent. However, it takes a lot of work to make it compatible with existing config files, and its lack of nesting limits its potential.
CHANfiG would like to make a change.
You just type the alternations in the command line, and leave everything else to CHANfiG.
CHANfiG is highly inspired by YACS. Different from the paradigm of YACS( your code + a YACS config for experiment E (+ external dependencies + hardware + other nuisance terms ...) = reproducible experiment E
), The paradigm of CHANfiG is:
your code + command line arguments (+ optional CHANfiG config + external dependencies + hardware + other nuisance terms ...) = reproducible experiment E (+ optional CHANfiG config for experiment E)
A Config is basically a nested dict structure.
However, the default Python dict is hard to manipulate.
The only way to access a dict member is through dict['name']
, which is obviously extremely complex. Even worse, if the dict is nested like a config, member access could be something like dict['parent']['children']['name']
.
Enough is enough, it is time to make a change.
We need attribute-style access, and we need it now. dict.name
and dict.parent.children.name
is all you need.
Although there have been some other works that achieve a similar functionality of attribute-style access to dict members. Their Config object either uses a separate dict to store information from attribute-style access (EasyDict), which may lead to inconsistency between attribute-style access and dict-style access; or re-use the existing __dict__
and redirect dict-style access (ml_collections), which may result in confliction between attributes and members of Config.
To overcome the aforementioned limitations, we inherit the Python built-in dict
to create FlatDict
, DefaultDict
, NestedDict
, Config
, and Registry
. We also introduce Variable
to allow sharing a value across multiple places, and ConfigParser
to parse command line arguments.
FlatDict
improves the default dict
in 3 aspects.
FlatDict
incorporates a merge
method that allows you to merge a Mapping
, an Iterable
, or a path to the FlatDict
. Different from built-in update
, merge
assign values instead of replace, which makes it works better with DefaultDict
.
dict
in Python is ordered since Python 3.7, but there isn\u2019t a built-in method to help you sort a dict
. FlatDict
supportssort
to help you manage your dict.
Moreover, FlatDict
comes with difference
and intersect
, which makes it very easy to compare a FlatDict
with other Mapping
, Iterable
, or a path.
FlatDict
supports to
method similar to PyTorch Tensors. You can simply convert all member values of FlatDict
to a certain type or pass to a device in the same way.
FlatDict
also integrates cpu
, gpu
(cuda
), and tpu
(xla
) methods for easier access.
FlatDict
provides json
, jsons
, yaml
and yamls
methods to dump FlatDict
to a file or string. It also provides from_json
, from_jsons
, from_yaml
and from_yamls
methods to build a FlatDict
from a string or file.
FlatDict
also includes dump
and load
methods which determines the type by its extension and dump/load FlatDict
to/from a file.
To facility the needs of default values, we incorporate DefaultDict
which accepts default_factory
and works just like a collections.defaultdict
.
Since most Configs are in a nested structure, we further propose a NestedDict
.
Based on DefaultDict
, NestedDict
provides all_keys
, all_values
, and all_items
methods to allow iterating over the whole nested structure at once.
NestedDict
also comes with apply
and apply_
methods, which made it easier to manipulate the nested structure.
Config
extends the functionality by supporting freeze
and defrost
, and by adding a built-in ConfigParser
to pare command line arguments.
Note that Config
also has default_factory=Config()
by default for convenience.
Registry
extends the NestedDict
and supports register
, lookup
, and build
to help you register constructors and build objects from a Config
.
Have one value for multiple names at multiple places? We got you covered.
Just wrap the value with Variable
, and one alteration will be reflected everywhere.
Variable
also supports type
, choices
, validator
, and required
to ensure the correctness of the value.
To make it even easier, Variable
also supports help
to provide a description when using ConfigParser
.
ConfigParser
extends ArgumentParser
and provides parse
and parse_config
to parse command line arguments.
CHANfiG has great backward compatibility with previous configs.
No matter if your old config is json or yaml, you could directly read from them.
And if you are using yacs, just replace CfgNode
with Config
and enjoy all the additional benefits that CHANfiG provides.
Moreover, if you find a name in the config is too long for command-line, you could simply call self.add_argument
with proper dest
to use a shorter name in command-line, as you do with argparse
.
from chanfig import Config, Variable\nclass Model:\ndef __init__(self, encoder, dropout=0.1, activation='ReLU'):\nself.encoder = Encoder(**encoder)\nself.dropout = Dropout(dropout)\nself.activation = getattr(Activation, activation)\ndef main(config):\nmodel = Model(**config.model)\noptimizer = Optimizer(**config.optimizer)\nscheduler = Scheduler(**config.scheduler)\ndataset = Dataset(**config.dataset)\ndataloader = Dataloader(**config.dataloader)\nclass TestConfig(Config):\ndef __init__(self):\nsuper().__init__()\ndropout = Variable(0.1)\nself.name = \"CHANfiG\"\nself.seed = 1013\nself.data.batch_size = 64\nself.model.encoder.num_layers = 6\nself.model.decoder.num_layers = 6\nself.model.dropout = dropout\nself.model.encoder.dropout = dropout\nself.model.decoder.dropout = dropout\nself.activation = \"GELU\"\nself.optim.lr = 1e-3\nself.add_argument(\"--batch_size\", dest=\"data.batch_size\")\nself.add_argument(\"--lr\", dest=\"optim.lr\")\ndef post(self):\nself.id = f\"{self.name}_{self.seed}\"\nif __name__ == '__main__':\n# config = Config.load('config.yaml') # in case you want to read from a yaml\n# config = Config.load('config.json') # in case you want to read from a json\n# existing_configs = {'data.batch_size': 64, 'model.encoder.num_layers': 8}\n# config = Config(**existing_configs) # in case you have some config in dict to load\nconfig = TestConfig()\nconfig = config.parse()\n# config.merge('dataset.yaml') # in case you want to merge a yaml\n# config.merge('dataset.json') # in case you want to merge a json\n# note that the value of merge will override current values\nconfig.model.decoder.num_layers = 8\nconfig.freeze()\nprint(config)\n# main(config)\n# config.yaml('config.yaml') # in case you want to save a yaml\n# config.json('config.json') # in case you want to save a json\n
All you need to do is just run a line:
Bashpython main.py --model.encoder.num_layers 8 --model.dropout=0.2 --lr 5e-3\n
You could also load a default configure file and make changes based on it:
Note, you must specify config.parse(default_config='config')
to correctly load the default config.
python main.py --config meow.yaml --model.encoder.num_layers 8 --model.dropout=0.2 --lr 5e-3\n
If you have made it dump current configurations, this should be in the written file:
YAMLactivation: GELU\ndata:\nbatch_size: 64\nid: CHANfiG_1013\nmodel:\ndecoder:\ndropout: 0.1\nnum_layers: 6\ndropout: 0.1\nencoder:\ndropout: 0.1\nnum_layers: 6\nname: CHANfiG\noptim:\nlr: 0.005\nseed: 1013\n
JSON{\n\"name\": \"CHANfiG\",\n\"seed\": 1013,\n\"data\": {\n\"batch_size\": 64\n},\n\"model\": {\n\"encoder\": {\n\"num_layers\": 6,\n\"dropout\": 0.1\n},\n\"decoder\": {\n\"num_layers\": 6,\n\"dropout\": 0.1\n},\n\"dropout\": 0.1\n},\n\"activation\": \"GELU\",\n\"optim\": {\n\"lr\": 0.005\n},\n\"id\": \"CHANfiG_1013\"\n}\n
Define the default arguments in function, put alterations in CLI, and leave the rest to CHANfiG.
"},{"location":"#installation","title":"Installation","text":"Install the most recent stable version on pypi:
Bashpip install chanfig\n
Install the latest version from source:
Bashpip install git+https://github.com/ZhiyuanChen/CHANfiG\n
It works the way it should have worked.
"},{"location":"#license","title":"License","text":"CHANfiG is multi-licensed under the following licenses:
You can choose any (one or more) of these licenses if you use this work.
SPDX-License-Identifier: Unlicense OR AGPL-3.0-or-later OR GPL-2.0-or-later OR BSD-4-Clause OR MIT OR Apache-2.0
fun fact: time is always uncountable.\u00a0\u21a9
Config
","text":" Bases: NestedDict
Config
is an extension of NestedDict
.
The differences between Config
and NestedDict
lies in 3 aspects:
Config
has default_factory
set to Config
and convert_mapping
set to True
by default.Config
has a frozen
attribute, which can be toggled with freeze
(lock
) & defrost
(unlock
) or temporarily changed with locked
& unlocked
.Config
has a ConfigParser
built-in, and supports add_argument
and parse
.Config also features a post
method and a boot
method to support lazy-initilisation. This is useful when you want to perform some post-processing on the config. For example, some values may be a combination of other values, and you may define them in post
.
boot
is introduced to call all post
methods in the nested structure of Config
object. By default, boot
will be called to after Config
is parsed.
You could also manually call boot
if you you don\u2019t parse command-line arguments.
Since Config
has default_factory
set to Config
, accessing anything that does not exist will create a new empty Config sub-attribute.
A frozen Config
does not have this behaviour and will raises KeyError
when accessing anything that does not exist.
It is recommended to call config.freeze()
or config.to(NestedDict)
to avoid this behaviour.
Attributes:
Name Type Descriptionparser
ConfigParser
Parser for command-line arguments.
frozen
bool
If True
, the config is frozen and cannot be altered.
Examples:
Python Console Session>>> c = Config(**{\"f.n\": \"chang\"})\n>>> c.i.d = 1013\n>>> c.i.d\n1013\n>>> c.d.i\nConfig(<class 'chanfig.config.Config'>, )\n>>> c.freeze().dict()\n{'f': {'n': 'chang'}, 'i': {'d': 1013}, 'd': {'i': {}}}\n>>> c.d.i = 1013\nTraceback (most recent call last):\nValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n>>> c.d.e\nTraceback (most recent call last):\nAttributeError: 'Config' object has no attribute 'e'\n>>> with c.unlocked():\n... del c.d\n>>> c.dict()\n{'f': {'n': 'chang'}, 'i': {'d': 1013}}\n
Source code in chanfig/config.py
Pythonclass Config(NestedDict): # type: ignore\nr\"\"\"\n `Config` is an extension of `NestedDict`.\n The differences between `Config` and `NestedDict` lies in 3 aspects:\n 1. `Config` has `default_factory` set to `Config` and `convert_mapping` set to `True` by default.\n 2. `Config` has a `frozen` attribute, which can be toggled with `freeze`(`lock`) & `defrost`(`unlock`)\n or temporarily changed with `locked` & `unlocked`.\n 3. `Config` has a `ConfigParser` built-in, and supports `add_argument` and `parse`.\n Config also features a `post` method and a `boot` method to support lazy-initilisation.\n This is useful when you want to perform some post-processing on the config.\n For example, some values may be a combination of other values, and you may define them in `post`.\n `boot` is introduced to call all `post` methods in the nested structure of `Config` object.\n By default, `boot` will be called to after `Config` is parsed.\n You could also manually call `boot` if you you don't parse command-line arguments.\n Notes:\n Since `Config` has `default_factory` set to `Config`,\n accessing anything that does not exist will create a new empty Config sub-attribute.\n A **frozen** `Config` does not have this behaviour and\n will raises `KeyError` when accessing anything that does not exist.\n It is recommended to call `config.freeze()` or `config.to(NestedDict)` to avoid this behaviour.\n Attributes:\n parser (ConfigParser): Parser for command-line arguments.\n frozen (bool): If `True`, the config is frozen and cannot be altered.\n Examples:\n >>> c = Config(**{\"f.n\": \"chang\"})\n >>> c.i.d = 1013\n >>> c.i.d\n 1013\n >>> c.d.i\n Config(<class 'chanfig.config.Config'>, )\n >>> c.freeze().dict()\n {'f': {'n': 'chang'}, 'i': {'d': 1013}, 'd': {'i': {}}}\n >>> c.d.i = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.d.e\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'e'\n >>> with c.unlocked():\n ... del c.d\n >>> c.dict()\n {'f': {'n': 'chang'}, 'i': {'d': 1013}}\n \"\"\"\nparser: None # ConfigParser, Python 3.7 does not support forward reference\nfrozen: bool = False\ndef __init__(self, *args: Any, default_factory: Callable | None = None, **kwargs: Any):\nif default_factory is None:\ndefault_factory = Config\nsuper().__init__(*args, default_factory=default_factory, **kwargs)\ndef post(self) -> Config | None:\nr\"\"\"\n Post process of `Config`.\n Some `Config` may need to do some post process after `Config` is initialised.\n `post` is provided for this lazy-initialisation purpose.\n By default, `post` calls `interpolate` to perform variable interpolation.\n Note that you should always call `boot` to apply `post` rather than calling `post` directly,\n as `boot` recursively call `post` on sub-configs.\n See Also: [`boot`][chanfig.Config.boot]\n Returns:\n self:\n Examples:\n >>> class PostConfig(Config):\n ... def post(self):\n ... if isinstance(self.data, str):\n ... self.data = Config(feature=self.data, label=self.data)\n ... return self\n >>> c = PostConfig(data=\"path\")\n >>> c.post()\n PostConfig(<class 'chanfig.config.Config'>,\n ('data'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n \"\"\"\nself.interpolate()\nself.validate()\nreturn self\ndef boot(self) -> Config:\nr\"\"\"\n Apply `post` recursively.\n Sub-config may have their own `post` method.\n `boot` is provided to apply `post` recursively.\n By default, `boot` is called after `Config` is parsed.\n If you don't need to parse command-line arguments, you should call `boot` manually.\n See Also: [`post`][chanfig.Config.post]\n Returns:\n self:\n Examples:\n >>> class DataConfig(Config):\n ... def post(self):\n ... if isinstance(self.path, str):\n ... self.path = Config(feature=self.path, label=self.path)\n ... return self\n >>> class BootConfig(Config):\n ... def __init__(self, *args, **kwargs):\n ... super().__init__(*args, **kwargs)\n ... self.dataset = DataConfig(path=\"path\")\n ... def post(self):\n ... if isinstance(self.id, str):\n ... self.id += \"_id\"\n ... return self\n >>> c = BootConfig(id=\"boot\")\n >>> c.boot()\n BootConfig(<class 'chanfig.config.Config'>,\n ('id'): 'boot_id'\n ('dataset'): DataConfig(<class 'chanfig.config.Config'>,\n ('path'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n )\n \"\"\"\nfor value in self.values():\nif isinstance(value, Config):\nvalue.boot()\nself.post()\nreturn self\ndef parse(\nself,\nargs: Iterable[str] | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\nboot: bool = True,\n) -> Config:\nr\"\"\"\n Parse command-line arguments with `ConfigParser`.\n `parse` will try to parse all command-line arguments,\n you don't need to pre-define them but typos may cause trouble.\n By default, this method internally calls `Config.boot()`.\n To disable this behaviour, set `boot` to `False`.\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `None`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n boot (bool, optional): If `True`, call `Config.boot()` after parsing. Defaults to `True`.\n See Also:\n [`chanfig.ConfigParser.parse`][chanfig.ConfigParser.parse]: Implementation of `parse`.\n [`parse_config`][chanfig.Config.parse_config]: Only parse valid config arguments.\n Examples:\n >>> c = Config(a=0)\n >>> c.dict()\n {'a': 0}\n >>> c.parse(['--a', '1', '--b', '2', '--c', '3']).dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nself.getattr(\"parser\").parse(args, self, default_config, no_default_config_action)\nif boot:\nself.boot()\nreturn self\ndef parse_config(\nself,\nargs: Iterable[str] | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\nboot: bool = True,\n) -> Config:\nr\"\"\"\n Parse command-line arguments with `ConfigParser`.\n `parse_config` only parse command-line arguments that is in defined in `Config`.\n By default, this method internally calls `Config.boot()`.\n To disable this behaviour, set `boot` to `False`.\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `None`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n boot (bool, optional): If `True`, call `Config.boot()` after parsing. Defaults to `True`.\n See Also:\n [`chanfig.ConfigParser.parse_config`][chanfig.ConfigParser.parse_config]: Implementation of `parse_config`.\n [`parse`][chanfig.Config.parse]: Parse all command-line arguments.\n Examples:\n >>> c = Config(a=0, b=0, c=0)\n >>> c.dict()\n {'a': 0, 'b': 0, 'c': 0}\n >>> c.parse_config(['--a', '1', '--b', '2', '--c', '3']).dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nself.getattr(\"parser\").parse_config(args, self, default_config, no_default_config_action)\nif boot:\nself.boot()\nreturn self\ndef add_argument(self, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Add an argument to `ConfigParser`.\n Note that value defined in `Config` will override the default value defined in `add_argument`.\n Examples:\n >>> c = Config(a=0, c=1)\n >>> arg = c.add_argument(\"--a\", type=int, default=1)\n >>> arg = c.add_argument(\"--b\", type=int, default=2)\n >>> c.parse(['--c', '4']).dict()\n {'a': 1, 'c': 4, 'b': 2}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nreturn self.getattr(\"parser\").add_argument(*args, **kwargs)\ndef freeze(self, recursive: bool = True) -> Config:\nr\"\"\"\n Freeze `Config`.\n Args:\n recursive:\n **Alias**:\n + `lock`\n Examples:\n >>> c = Config(**{'i.d': 1013})\n >>> c.getattr('frozen')\n False\n >>> c.freeze(recursive=False).dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n True\n >>> c.i.getattr('frozen')\n False\n >>> c.lock().dict() # alias\n {'i': {'d': 1013}}\n >>> c.i.getattr('frozen')\n True\n \"\"\"\n@wraps(self.freeze)\ndef freeze(config: Config) -> None:\nif isinstance(config, Config):\nconfig.setattr(\"frozen\", True)\nif recursive:\nself.apply_(freeze)\nelse:\nfreeze(self)\nreturn self\ndef lock(self, recursive: bool = True) -> Config:\nr\"\"\"\n Alias of [`freeze`][chanfig.Config.freeze].\n \"\"\"\nreturn self.freeze(recursive=recursive)\n@contextmanager\ndef locked(self):\n\"\"\"\n Context manager which temporarily locks `Config`.\n Examples:\n >>> c = Config()\n >>> with c.locked():\n ... c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.i.d = 1013\n >>> c.dict()\n {'i': {'d': 1013}}\n \"\"\"\nwas_frozen = self.getattr(\"frozen\", False)\ntry:\nself.freeze()\nyield self\nfinally:\nif not was_frozen:\nself.defrost()\ndef defrost(self, recursive: bool = True) -> Config:\nr\"\"\"\n Defrost `Config`.\n Args:\n recursive:\n **Alias**:\n + `unlock`\n Examples:\n >>> c = Config(**{'i.d': 1013})\n >>> c.getattr('frozen')\n False\n >>> c.freeze().dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n True\n >>> c.defrost(recursive=False).dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n False\n >>> c.i.getattr('frozen')\n True\n >>> c.unlock().dict() # alias\n {'i': {'d': 1013}}\n >>> c.i.getattr('frozen')\n False\n \"\"\"\n@wraps(self.defrost)\ndef defrost(config: Config) -> None:\nif isinstance(config, Config):\nconfig.setattr(\"frozen\", False)\nif recursive:\nself.apply_(defrost)\nelse:\ndefrost(self)\nreturn self\ndef unlock(self, recursive: bool = True) -> Config:\nr\"\"\"\n Alias of [`defrost`][chanfig.Config.defrost].\n \"\"\"\nreturn self.defrost(recursive=recursive)\n@contextmanager\ndef unlocked(self):\n\"\"\"\n Context manager which temporarily unlocks `Config`.\n Examples:\n >>> c = Config()\n >>> c.freeze().dict()\n {}\n >>> with c.unlocked():\n ... c['i.d'] = 1013\n >>> c.defrost().dict()\n {'i': {'d': 1013}}\n \"\"\"\nwas_frozen = self.getattr(\"frozen\", False)\ntry:\nself.defrost()\nyield self\nfinally:\nif was_frozen:\nself.freeze()\ndef get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\nr\"\"\"\n Get value from `Config`.\n Note that `default` has higher priority than `default_factory`.\n Args:\n name:\n default:\n Returns:\n value:\n If `Config` does not contain `name`, return `default`.\n If `default` is not specified, return `default_factory()`.\n Raises:\n KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.\n Examples:\n >>> d = Config(**{\"i.d\": 1013})\n >>> d.get('i.d')\n 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.f\n Config(<class 'chanfig.config.Config'>, )\n >>> del d.f\n >>> d.freeze()\n Config(<class 'chanfig.config.Config'>,\n ('i'): Config(<class 'chanfig.config.Config'>,\n ('d'): 1013\n )\n )\n >>> d.f\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'f'\n >>> d[\"f.n\"]\n Traceback (most recent call last):\n KeyError: 'f.n'\n \"\"\"\nif not self.hasattr(\"default_factory\"): # did not call super().__init__() in sub-class\nself.setattr(\"default_factory\", Config)\nif name in self or not self.getattr(\"frozen\", False):\nreturn super().get(name, default, fallback)\nraise KeyError(name)\n@frozen_check\ndef set(\nself,\nname: Any,\nvalue: Any,\nconvert_mapping: bool | None = None,\n) -> None:\nr\"\"\"\n Set value of `Config`.\n Args:\n name:\n value:\n convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n Defaults to self.convert_mapping.\n Raises:\n ValueError: If `Config` is frozen.\n Examples:\n >>> c = Config()\n >>> c['i.d'] = 1013\n >>> c.i.d\n 1013\n >>> c.freeze().dict()\n {'i': {'d': 1013}}\n >>> c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.defrost().dict()\n {'i': {'d': 1013}}\n >>> c['i.d'] = 1013\n >>> c.i.d\n 1013\n \"\"\"\nreturn super().set(name, value, convert_mapping)\n@frozen_check\ndef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `Config`.\n Args:\n name:\n Examples:\n >>> d = Config(**{\"i.d\": 1013, \"f.n\": \"chang\"})\n >>> d.i.d\n 1013\n >>> d.f.n\n 'chang'\n >>> d.delete('i.d')\n >>> \"i.d\" in d\n False\n >>> d.i.d\n Config(<class 'chanfig.config.Config'>, )\n >>> \"i.d\" in d\n True\n >>> del d.f.n\n >>> d.f.n\n Config(<class 'chanfig.config.Config'>, )\n >>> del d.c\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'c'\n \"\"\"\nsuper().delete(name)\n@frozen_check\ndef pop(self, name: Any, default: Any = Null) -> Any:\nr\"\"\"\n Pop value from `Config`.\n Args:\n name:\n default:\n Returns:\n value: If `Config` does not contain `name`, return `default`.\n Examples:\n >>> c = Config()\n >>> c['i.d'] = 1013\n >>> c.pop('i.d')\n 1013\n >>> c.pop('i.d', True)\n True\n >>> c.freeze().dict()\n {'i': {}}\n >>> c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.defrost().dict()\n {'i': {}}\n >>> c['i.d'] = 1013\n >>> c.pop('i.d')\n 1013\n \"\"\"\nreturn super().pop(name, default)\n
"},{"location":"config/#chanfig.config.Config.add_argument","title":"add_argument(*args, **kwargs)
","text":"Add an argument to ConfigParser
.
Note that value defined in Config
will override the default value defined in add_argument
.
Examples:
Python Console Session>>> c = Config(a=0, c=1)\n>>> arg = c.add_argument(\"--a\", type=int, default=1)\n>>> arg = c.add_argument(\"--b\", type=int, default=2)\n>>> c.parse(['--c', '4']).dict()\n{'a': 1, 'c': 4, 'b': 2}\n
Source code in chanfig/config.py
Pythondef add_argument(self, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Add an argument to `ConfigParser`.\n Note that value defined in `Config` will override the default value defined in `add_argument`.\n Examples:\n >>> c = Config(a=0, c=1)\n >>> arg = c.add_argument(\"--a\", type=int, default=1)\n >>> arg = c.add_argument(\"--b\", type=int, default=2)\n >>> c.parse(['--c', '4']).dict()\n {'a': 1, 'c': 4, 'b': 2}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nreturn self.getattr(\"parser\").add_argument(*args, **kwargs)\n
"},{"location":"config/#chanfig.config.Config.boot","title":"boot()
","text":"Apply post
recursively.
Sub-config may have their own post
method. boot
is provided to apply post
recursively.
By default, boot
is called after Config
is parsed. If you don\u2019t need to parse command-line arguments, you should call boot
manually.
See Also: post
Returns:
Name Type Descriptionself
Config
Examples:
Python Console Session>>> class DataConfig(Config):\n... def post(self):\n... if isinstance(self.path, str):\n... self.path = Config(feature=self.path, label=self.path)\n... return self\n>>> class BootConfig(Config):\n... def __init__(self, *args, **kwargs):\n... super().__init__(*args, **kwargs)\n... self.dataset = DataConfig(path=\"path\")\n... def post(self):\n... if isinstance(self.id, str):\n... self.id += \"_id\"\n... return self\n>>> c = BootConfig(id=\"boot\")\n>>> c.boot()\nBootConfig(<class 'chanfig.config.Config'>,\n ('id'): 'boot_id'\n ('dataset'): DataConfig(<class 'chanfig.config.Config'>,\n ('path'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n)\n
Source code in chanfig/config.py
Pythondef boot(self) -> Config:\nr\"\"\"\n Apply `post` recursively.\n Sub-config may have their own `post` method.\n `boot` is provided to apply `post` recursively.\n By default, `boot` is called after `Config` is parsed.\n If you don't need to parse command-line arguments, you should call `boot` manually.\n See Also: [`post`][chanfig.Config.post]\n Returns:\n self:\n Examples:\n >>> class DataConfig(Config):\n ... def post(self):\n ... if isinstance(self.path, str):\n ... self.path = Config(feature=self.path, label=self.path)\n ... return self\n >>> class BootConfig(Config):\n ... def __init__(self, *args, **kwargs):\n ... super().__init__(*args, **kwargs)\n ... self.dataset = DataConfig(path=\"path\")\n ... def post(self):\n ... if isinstance(self.id, str):\n ... self.id += \"_id\"\n ... return self\n >>> c = BootConfig(id=\"boot\")\n >>> c.boot()\n BootConfig(<class 'chanfig.config.Config'>,\n ('id'): 'boot_id'\n ('dataset'): DataConfig(<class 'chanfig.config.Config'>,\n ('path'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n )\n \"\"\"\nfor value in self.values():\nif isinstance(value, Config):\nvalue.boot()\nself.post()\nreturn self\n
"},{"location":"config/#chanfig.config.Config.defrost","title":"defrost(recursive=True)
","text":"Defrost Config
.
Parameters:
Name Type Description Defaultrecursive
bool
True
Alias:
unlock
Examples:
Python Console Session>>> c = Config(**{'i.d': 1013})\n>>> c.getattr('frozen')\nFalse\n>>> c.freeze().dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nTrue\n>>> c.defrost(recursive=False).dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nFalse\n>>> c.i.getattr('frozen')\nTrue\n>>> c.unlock().dict() # alias\n{'i': {'d': 1013}}\n>>> c.i.getattr('frozen')\nFalse\n
Source code in chanfig/config.py
Pythondef defrost(self, recursive: bool = True) -> Config:\nr\"\"\"\n Defrost `Config`.\n Args:\n recursive:\n **Alias**:\n + `unlock`\n Examples:\n >>> c = Config(**{'i.d': 1013})\n >>> c.getattr('frozen')\n False\n >>> c.freeze().dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n True\n >>> c.defrost(recursive=False).dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n False\n >>> c.i.getattr('frozen')\n True\n >>> c.unlock().dict() # alias\n {'i': {'d': 1013}}\n >>> c.i.getattr('frozen')\n False\n \"\"\"\n@wraps(self.defrost)\ndef defrost(config: Config) -> None:\nif isinstance(config, Config):\nconfig.setattr(\"frozen\", False)\nif recursive:\nself.apply_(defrost)\nelse:\ndefrost(self)\nreturn self\n
"},{"location":"config/#chanfig.config.Config.delete","title":"delete(name)
","text":"Delete value from Config
.
Parameters:
Name Type Description Defaultname
Any
required Examples:
Python Console Session>>> d = Config(**{\"i.d\": 1013, \"f.n\": \"chang\"})\n>>> d.i.d\n1013\n>>> d.f.n\n'chang'\n>>> d.delete('i.d')\n>>> \"i.d\" in d\nFalse\n>>> d.i.d\nConfig(<class 'chanfig.config.Config'>, )\n>>> \"i.d\" in d\nTrue\n>>> del d.f.n\n>>> d.f.n\nConfig(<class 'chanfig.config.Config'>, )\n>>> del d.c\nTraceback (most recent call last):\nAttributeError: 'Config' object has no attribute 'c'\n
Source code in chanfig/config.py
Python@frozen_check\ndef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `Config`.\n Args:\n name:\n Examples:\n >>> d = Config(**{\"i.d\": 1013, \"f.n\": \"chang\"})\n >>> d.i.d\n 1013\n >>> d.f.n\n 'chang'\n >>> d.delete('i.d')\n >>> \"i.d\" in d\n False\n >>> d.i.d\n Config(<class 'chanfig.config.Config'>, )\n >>> \"i.d\" in d\n True\n >>> del d.f.n\n >>> d.f.n\n Config(<class 'chanfig.config.Config'>, )\n >>> del d.c\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'c'\n \"\"\"\nsuper().delete(name)\n
"},{"location":"config/#chanfig.config.Config.freeze","title":"freeze(recursive=True)
","text":"Freeze Config
.
Parameters:
Name Type Description Defaultrecursive
bool
True
Alias:
lock
Examples:
Python Console Session>>> c = Config(**{'i.d': 1013})\n>>> c.getattr('frozen')\nFalse\n>>> c.freeze(recursive=False).dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nTrue\n>>> c.i.getattr('frozen')\nFalse\n>>> c.lock().dict() # alias\n{'i': {'d': 1013}}\n>>> c.i.getattr('frozen')\nTrue\n
Source code in chanfig/config.py
Pythondef freeze(self, recursive: bool = True) -> Config:\nr\"\"\"\n Freeze `Config`.\n Args:\n recursive:\n **Alias**:\n + `lock`\n Examples:\n >>> c = Config(**{'i.d': 1013})\n >>> c.getattr('frozen')\n False\n >>> c.freeze(recursive=False).dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n True\n >>> c.i.getattr('frozen')\n False\n >>> c.lock().dict() # alias\n {'i': {'d': 1013}}\n >>> c.i.getattr('frozen')\n True\n \"\"\"\n@wraps(self.freeze)\ndef freeze(config: Config) -> None:\nif isinstance(config, Config):\nconfig.setattr(\"frozen\", True)\nif recursive:\nself.apply_(freeze)\nelse:\nfreeze(self)\nreturn self\n
"},{"location":"config/#chanfig.config.Config.get","title":"get(name, default=None, fallback=None)
","text":"Get value from Config
.
Note that default
has higher priority than default_factory
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
None
Returns:
Name Type Descriptionvalue
Any
If Config
does not contain name
, return default
. If default
is not specified, return default_factory()
.
Raises:
Type DescriptionKeyError
If Config
does not contain name
and default
/default_factory
is not specified.
Examples:
Python Console Session>>> d = Config(**{\"i.d\": 1013})\n>>> d.get('i.d')\n1013\n>>> d['i.d']\n1013\n>>> d.i.d\n1013\n>>> d.get('f', 2)\n2\n>>> d.f\nConfig(<class 'chanfig.config.Config'>, )\n>>> del d.f\n>>> d.freeze()\nConfig(<class 'chanfig.config.Config'>,\n ('i'): Config(<class 'chanfig.config.Config'>,\n ('d'): 1013\n )\n)\n>>> d.f\nTraceback (most recent call last):\nAttributeError: 'Config' object has no attribute 'f'\n>>> d[\"f.n\"]\nTraceback (most recent call last):\nKeyError: 'f.n'\n
Source code in chanfig/config.py
Pythondef get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\nr\"\"\"\n Get value from `Config`.\n Note that `default` has higher priority than `default_factory`.\n Args:\n name:\n default:\n Returns:\n value:\n If `Config` does not contain `name`, return `default`.\n If `default` is not specified, return `default_factory()`.\n Raises:\n KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.\n Examples:\n >>> d = Config(**{\"i.d\": 1013})\n >>> d.get('i.d')\n 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.f\n Config(<class 'chanfig.config.Config'>, )\n >>> del d.f\n >>> d.freeze()\n Config(<class 'chanfig.config.Config'>,\n ('i'): Config(<class 'chanfig.config.Config'>,\n ('d'): 1013\n )\n )\n >>> d.f\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'f'\n >>> d[\"f.n\"]\n Traceback (most recent call last):\n KeyError: 'f.n'\n \"\"\"\nif not self.hasattr(\"default_factory\"): # did not call super().__init__() in sub-class\nself.setattr(\"default_factory\", Config)\nif name in self or not self.getattr(\"frozen\", False):\nreturn super().get(name, default, fallback)\nraise KeyError(name)\n
"},{"location":"config/#chanfig.config.Config.lock","title":"lock(recursive=True)
","text":"Alias of freeze
.
chanfig/config.py
Pythondef lock(self, recursive: bool = True) -> Config:\nr\"\"\"\n Alias of [`freeze`][chanfig.Config.freeze].\n \"\"\"\nreturn self.freeze(recursive=recursive)\n
"},{"location":"config/#chanfig.config.Config.locked","title":"locked()
","text":"Context manager which temporarily locks Config
.
Examples:
Python Console Session>>> c = Config()\n>>> with c.locked():\n... c['i.d'] = 1013\nTraceback (most recent call last):\nValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n>>> c.i.d = 1013\n>>> c.dict()\n{'i': {'d': 1013}}\n
Source code in chanfig/config.py
Python@contextmanager\ndef locked(self):\n\"\"\"\n Context manager which temporarily locks `Config`.\n Examples:\n >>> c = Config()\n >>> with c.locked():\n ... c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.i.d = 1013\n >>> c.dict()\n {'i': {'d': 1013}}\n \"\"\"\nwas_frozen = self.getattr(\"frozen\", False)\ntry:\nself.freeze()\nyield self\nfinally:\nif not was_frozen:\nself.defrost()\n
"},{"location":"config/#chanfig.config.Config.parse","title":"parse(args=None, default_config=None, no_default_config_action='raise', boot=True)
","text":"Parse command-line arguments with ConfigParser
.
parse
will try to parse all command-line arguments, you don\u2019t need to pre-define them but typos may cause trouble.
By default, this method internally calls Config.boot()
. To disable this behaviour, set boot
to False
.
Parameters:
Name Type Description Defaultargs
Iterable[str] | None
Command-line arguments. Defaults to None
.
None
default_config
str | None
Path to default config file. Defaults to None
.
None
no_default_config_action
str
Action when default_config
is not found. Can be one of [\"raise\", \"warn\", \"ignore\"]
. Defaults to \"raise\"
.
'raise'
boot
bool
If True
, call Config.boot()
after parsing. Defaults to True
.
True
See Also chanfig.ConfigParser.parse
: Implementation of parse
. parse_config
: Only parse valid config arguments.
Examples:
Python Console Session>>> c = Config(a=0)\n>>> c.dict()\n{'a': 0}\n>>> c.parse(['--a', '1', '--b', '2', '--c', '3']).dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/config.py
Pythondef parse(\nself,\nargs: Iterable[str] | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\nboot: bool = True,\n) -> Config:\nr\"\"\"\n Parse command-line arguments with `ConfigParser`.\n `parse` will try to parse all command-line arguments,\n you don't need to pre-define them but typos may cause trouble.\n By default, this method internally calls `Config.boot()`.\n To disable this behaviour, set `boot` to `False`.\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `None`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n boot (bool, optional): If `True`, call `Config.boot()` after parsing. Defaults to `True`.\n See Also:\n [`chanfig.ConfigParser.parse`][chanfig.ConfigParser.parse]: Implementation of `parse`.\n [`parse_config`][chanfig.Config.parse_config]: Only parse valid config arguments.\n Examples:\n >>> c = Config(a=0)\n >>> c.dict()\n {'a': 0}\n >>> c.parse(['--a', '1', '--b', '2', '--c', '3']).dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nself.getattr(\"parser\").parse(args, self, default_config, no_default_config_action)\nif boot:\nself.boot()\nreturn self\n
"},{"location":"config/#chanfig.config.Config.parse_config","title":"parse_config(args=None, default_config=None, no_default_config_action='raise', boot=True)
","text":"Parse command-line arguments with ConfigParser
.
parse_config
only parse command-line arguments that is in defined in Config
.
By default, this method internally calls Config.boot()
. To disable this behaviour, set boot
to False
.
Parameters:
Name Type Description Defaultargs
Iterable[str] | None
Command-line arguments. Defaults to None
.
None
default_config
str | None
Path to default config file. Defaults to None
.
None
no_default_config_action
str
Action when default_config
is not found. Can be one of [\"raise\", \"warn\", \"ignore\"]
. Defaults to \"raise\"
.
'raise'
boot
bool
If True
, call Config.boot()
after parsing. Defaults to True
.
True
See Also chanfig.ConfigParser.parse_config
: Implementation of parse_config
. parse
: Parse all command-line arguments.
Examples:
Python Console Session>>> c = Config(a=0, b=0, c=0)\n>>> c.dict()\n{'a': 0, 'b': 0, 'c': 0}\n>>> c.parse_config(['--a', '1', '--b', '2', '--c', '3']).dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/config.py
Pythondef parse_config(\nself,\nargs: Iterable[str] | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\nboot: bool = True,\n) -> Config:\nr\"\"\"\n Parse command-line arguments with `ConfigParser`.\n `parse_config` only parse command-line arguments that is in defined in `Config`.\n By default, this method internally calls `Config.boot()`.\n To disable this behaviour, set `boot` to `False`.\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `None`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n boot (bool, optional): If `True`, call `Config.boot()` after parsing. Defaults to `True`.\n See Also:\n [`chanfig.ConfigParser.parse_config`][chanfig.ConfigParser.parse_config]: Implementation of `parse_config`.\n [`parse`][chanfig.Config.parse]: Parse all command-line arguments.\n Examples:\n >>> c = Config(a=0, b=0, c=0)\n >>> c.dict()\n {'a': 0, 'b': 0, 'c': 0}\n >>> c.parse_config(['--a', '1', '--b', '2', '--c', '3']).dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nself.getattr(\"parser\").parse_config(args, self, default_config, no_default_config_action)\nif boot:\nself.boot()\nreturn self\n
"},{"location":"config/#chanfig.config.Config.pop","title":"pop(name, default=Null)
","text":"Pop value from Config
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
Null
Returns:
Name Type Descriptionvalue
Any
If Config
does not contain name
, return default
.
Examples:
Python Console Session>>> c = Config()\n>>> c['i.d'] = 1013\n>>> c.pop('i.d')\n1013\n>>> c.pop('i.d', True)\nTrue\n>>> c.freeze().dict()\n{'i': {}}\n>>> c['i.d'] = 1013\nTraceback (most recent call last):\nValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n>>> c.defrost().dict()\n{'i': {}}\n>>> c['i.d'] = 1013\n>>> c.pop('i.d')\n1013\n
Source code in chanfig/config.py
Python@frozen_check\ndef pop(self, name: Any, default: Any = Null) -> Any:\nr\"\"\"\n Pop value from `Config`.\n Args:\n name:\n default:\n Returns:\n value: If `Config` does not contain `name`, return `default`.\n Examples:\n >>> c = Config()\n >>> c['i.d'] = 1013\n >>> c.pop('i.d')\n 1013\n >>> c.pop('i.d', True)\n True\n >>> c.freeze().dict()\n {'i': {}}\n >>> c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.defrost().dict()\n {'i': {}}\n >>> c['i.d'] = 1013\n >>> c.pop('i.d')\n 1013\n \"\"\"\nreturn super().pop(name, default)\n
"},{"location":"config/#chanfig.config.Config.post","title":"post()
","text":"Post process of Config
.
Some Config
may need to do some post process after Config
is initialised. post
is provided for this lazy-initialisation purpose.
By default, post
calls interpolate
to perform variable interpolation.
Note that you should always call boot
to apply post
rather than calling post
directly, as boot
recursively call post
on sub-configs.
See Also: boot
Returns:
Name Type Descriptionself
Config | None
Examples:
Python Console Session>>> class PostConfig(Config):\n... def post(self):\n... if isinstance(self.data, str):\n... self.data = Config(feature=self.data, label=self.data)\n... return self\n>>> c = PostConfig(data=\"path\")\n>>> c.post()\nPostConfig(<class 'chanfig.config.Config'>,\n ('data'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n)\n
Source code in chanfig/config.py
Pythondef post(self) -> Config | None:\nr\"\"\"\n Post process of `Config`.\n Some `Config` may need to do some post process after `Config` is initialised.\n `post` is provided for this lazy-initialisation purpose.\n By default, `post` calls `interpolate` to perform variable interpolation.\n Note that you should always call `boot` to apply `post` rather than calling `post` directly,\n as `boot` recursively call `post` on sub-configs.\n See Also: [`boot`][chanfig.Config.boot]\n Returns:\n self:\n Examples:\n >>> class PostConfig(Config):\n ... def post(self):\n ... if isinstance(self.data, str):\n ... self.data = Config(feature=self.data, label=self.data)\n ... return self\n >>> c = PostConfig(data=\"path\")\n >>> c.post()\n PostConfig(<class 'chanfig.config.Config'>,\n ('data'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n \"\"\"\nself.interpolate()\nself.validate()\nreturn self\n
"},{"location":"config/#chanfig.config.Config.set","title":"set(name, value, convert_mapping=None)
","text":"Set value of Config
.
Parameters:
Name Type Description Defaultname
Any
required value
Any
required convert_mapping
bool | None
Whether to convert Mapping
to NestedDict
. Defaults to self.convert_mapping.
None
Raises:
Type DescriptionValueError
If Config
is frozen.
Examples:
Python Console Session>>> c = Config()\n>>> c['i.d'] = 1013\n>>> c.i.d\n1013\n>>> c.freeze().dict()\n{'i': {'d': 1013}}\n>>> c['i.d'] = 1013\nTraceback (most recent call last):\nValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n>>> c.defrost().dict()\n{'i': {'d': 1013}}\n>>> c['i.d'] = 1013\n>>> c.i.d\n1013\n
Source code in chanfig/config.py
Python@frozen_check\ndef set(\nself,\nname: Any,\nvalue: Any,\nconvert_mapping: bool | None = None,\n) -> None:\nr\"\"\"\n Set value of `Config`.\n Args:\n name:\n value:\n convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n Defaults to self.convert_mapping.\n Raises:\n ValueError: If `Config` is frozen.\n Examples:\n >>> c = Config()\n >>> c['i.d'] = 1013\n >>> c.i.d\n 1013\n >>> c.freeze().dict()\n {'i': {'d': 1013}}\n >>> c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.defrost().dict()\n {'i': {'d': 1013}}\n >>> c['i.d'] = 1013\n >>> c.i.d\n 1013\n \"\"\"\nreturn super().set(name, value, convert_mapping)\n
"},{"location":"config/#chanfig.config.Config.unlock","title":"unlock(recursive=True)
","text":"Alias of defrost
.
chanfig/config.py
Pythondef unlock(self, recursive: bool = True) -> Config:\nr\"\"\"\n Alias of [`defrost`][chanfig.Config.defrost].\n \"\"\"\nreturn self.defrost(recursive=recursive)\n
"},{"location":"config/#chanfig.config.Config.unlocked","title":"unlocked()
","text":"Context manager which temporarily unlocks Config
.
Examples:
Python Console Session>>> c = Config()\n>>> c.freeze().dict()\n{}\n>>> with c.unlocked():\n... c['i.d'] = 1013\n>>> c.defrost().dict()\n{'i': {'d': 1013}}\n
Source code in chanfig/config.py
Python@contextmanager\ndef unlocked(self):\n\"\"\"\n Context manager which temporarily unlocks `Config`.\n Examples:\n >>> c = Config()\n >>> c.freeze().dict()\n {}\n >>> with c.unlocked():\n ... c['i.d'] = 1013\n >>> c.defrost().dict()\n {'i': {'d': 1013}}\n \"\"\"\nwas_frozen = self.getattr(\"frozen\", False)\ntry:\nself.defrost()\nyield self\nfinally:\nif was_frozen:\nself.freeze()\n
"},{"location":"config/#chanfig.config.frozen_check","title":"frozen_check(func)
","text":"Decorator check if the object is frozen.
Source code inchanfig/config.py
Pythondef frozen_check(func: Callable):\nr\"\"\"\n Decorator check if the object is frozen.\n \"\"\"\n@wraps(func)\ndef decorator(self, *args: Any, **kwargs: Any):\nif self.getattr(\"frozen\", False):\nraise ValueError(\"Attempting to alter a frozen config. Run config.defrost() to defrost first.\")\nreturn func(self, *args, **kwargs)\nreturn decorator\n
"},{"location":"default_dict/","title":"DefaultDict","text":" Bases: FlatDict
DefaultDict
inherits from FlatDict
and incorporates support of default_factory
in the same manner as collections.defaultdict
. If default_factory is not None
, the value will be set to default_factory()
when you access a key that does not exist in DefaultDict
.
You may specify DefaultDict(default_factory=FlatDict)
when creating DefaultDict
or by calling dict.setattr('default_factory', FlatDict)
for existing DefaultDict
objects.
Note that just like collections.defaultdict
, default_factory()
is called without any arguments.
Attributes:
Name Type Descriptiondefault_factory
Optional[Callable]
Default factory for defaultdict behaviour.
Raises:
Type DescriptionTypeError
If default_factory
is not callable.
Unlike collections.defaultdict
, DefaultDict
will not automatically create entries when name starts and ends with __
. This is to avoid conflicts with Python magic methods. You can still creates them manually with DefaultDict.fromkeys
or DefaultDict.add
.
Examples:
Python Console Session>>> d = DefaultDict(list)\n>>> d.a.append(1)\n>>> d.a\n[1]\n>>> d = DefaultDict([])\nTraceback (most recent call last):\nTypeError: `default_factory=[]` must be Callable, but got <class 'list'>.\n
Source code in chanfig/default_dict.py
Pythonclass DefaultDict(FlatDict): # type: ignore\nr\"\"\"\n `DefaultDict` inherits from `FlatDict` and incorporates support of `default_factory`\n in the same manner as `collections.defaultdict`.\n If `default_factory is not None`, the value will be set to `default_factory()`\n when you access a key that does not exist in `DefaultDict`.\n You may specify `DefaultDict(default_factory=FlatDict)` when creating `DefaultDict` or\n by calling `dict.setattr('default_factory', FlatDict)` for existing `DefaultDict` objects.\n Note that just like `collections.defaultdict`, `default_factory()` is called without any arguments.\n Attributes:\n default_factory: Default factory for defaultdict behaviour.\n Raises:\n TypeError: If `default_factory` is not callable.\n Notes:\n Unlike `collections.defaultdict`, `DefaultDict` will not automatically create entries when name starts and ends\n with `__`. This is to avoid conflicts with Python magic methods.\n You can still creates them manually with `DefaultDict.fromkeys` or `DefaultDict.add`.\n Examples:\n >>> d = DefaultDict(list)\n >>> d.a.append(1)\n >>> d.a\n [1]\n >>> d = DefaultDict([])\n Traceback (most recent call last):\n TypeError: `default_factory=[]` must be Callable, but got <class 'list'>.\n \"\"\"\ndefault_factory: Optional[Callable] = None\ndef __init__( # pylint: disable=W1113\nself, default_factory: Callable | None = None, *args: Any, **kwargs: Any\n) -> None:\nsuper().__init__(*args, **kwargs)\nif default_factory is not None:\nif callable(default_factory):\nself.setattr(\"default_factory\", default_factory)\nelse:\nraise TypeError(\nf\"`default_factory={default_factory}` must be Callable, but got {type(default_factory)}.\"\n)\ndef __missing__(self, name: Any, default=Null) -> Any: # pylint: disable=R1710\nif default is Null:\nif self.getattr(\"default_factory\", None) in (None, Null) or (name.startswith(\"__\") and name.endswith(\"__\")):\nraise KeyError(name) from None\ndefault = self.getattr(\"default_factory\")()\nif isinstance(default, FlatDict):\ndefault.__dict__.update(self.__dict__)\nsuper().set(name, default)\nreturn default\ndef __repr__(self) -> str:\nif self.default_factory is None:\nreturn super().__repr__()\nsuper_repr = super().__repr__()[len(self.__class__.__name__) :] # noqa: E203\nif len(super_repr) == 2:\nreturn f\"{self.__class__.__name__}({self.default_factory}, )\"\nreturn f\"{self.__class__.__name__}({self.default_factory},\" + super_repr[1:]\ndef add(self, name: Any):\nr\"\"\"\n Add a new default factory to the dictionary.\n Args:\n name:\n Raises:\n ValueError: If `default_factory` is None.\n Examples:\n >>> d = DefaultDict(default_factory=DefaultDict)\n >>> d.add('d')\n DefaultDict()\n >>> d.get('d')\n DefaultDict()\n >>> d['n'] = 'chang'\n >>> d.n\n 'chang'\n >>> d.n = 'liu'\n >>> d['n']\n 'liu'\n >>> d = DefaultDict()\n >>> d.add('a')\n Traceback (most recent call last):\n ValueError: Cannot add to a DefaultDict with no default_factory\n \"\"\"\nif self.default_factory is None:\nraise ValueError(\"Cannot add to a DefaultDict with no default_factory\")\nself.set(name, self.default_factory()) # pylint: disable=E1102\nreturn self.get(name)\n
"},{"location":"default_dict/#chanfig.DefaultDict.add","title":"add(name)
","text":"Add a new default factory to the dictionary.
Parameters:
Name Type Description Defaultname
Any
required Raises:
Type DescriptionValueError
If default_factory
is None.
Examples:
Python Console Session>>> d = DefaultDict(default_factory=DefaultDict)\n>>> d.add('d')\nDefaultDict()\n>>> d.get('d')\nDefaultDict()\n>>> d['n'] = 'chang'\n>>> d.n\n'chang'\n>>> d.n = 'liu'\n>>> d['n']\n'liu'\n>>> d = DefaultDict()\n>>> d.add('a')\nTraceback (most recent call last):\nValueError: Cannot add to a DefaultDict with no default_factory\n
Source code in chanfig/default_dict.py
Pythondef add(self, name: Any):\nr\"\"\"\n Add a new default factory to the dictionary.\n Args:\n name:\n Raises:\n ValueError: If `default_factory` is None.\n Examples:\n >>> d = DefaultDict(default_factory=DefaultDict)\n >>> d.add('d')\n DefaultDict()\n >>> d.get('d')\n DefaultDict()\n >>> d['n'] = 'chang'\n >>> d.n\n 'chang'\n >>> d.n = 'liu'\n >>> d['n']\n 'liu'\n >>> d = DefaultDict()\n >>> d.add('a')\n Traceback (most recent call last):\n ValueError: Cannot add to a DefaultDict with no default_factory\n \"\"\"\nif self.default_factory is None:\nraise ValueError(\"Cannot add to a DefaultDict with no default_factory\")\nself.set(name, self.default_factory()) # pylint: disable=E1102\nreturn self.get(name)\n
"},{"location":"flat_dict/","title":"FlatDict","text":" Bases: dict
FlatDict
with attribute-style access.
FlatDict
inherits from built-in dict
.
It comes with many easy to use helper methods, such as merge
, sort
, difference
, intersect
.
It also has full support for IO operations, such as json
and yaml
.
Even better, FlatDict
has pytorch support built-in. You can directly call FlatDict.cpu()
or FlatDict.to(\"cpu\")
to move all torch.Tensor
objects across devices.
FlatDict
works best with Variable
objects. Simply call flat_dict.a = Variable(1); flat_dict.b = flat_dict.a
, and their values will be synced.
Even better, FlatDict
support variable interpolation. Just set the value of one key to another key (surrounded by braces with $ at the begin, like ${xxx}), and calls flat_dict.interpolate()
, FlatDict
will interpolate their values and create Variable
automatically.
FlatDict
has many other easy to use helper methods, such as difference
, intersect
. And has full support for IO operations, such as json
and yaml
.
FlatDict
also has pytorch support built-in. You can directly call flat_dict.cpu()
or flat_dict.to(\"cpu\")
to move all torch.Tensor
objects across devices.
Attributes:
Name Type Descriptionindent
int
Indentation level in printing and dumping to json or yaml.
NotesFlatDict
rewrite __getattribute__
and __getattr__
to supports attribute-style access to its members. Therefore, all internal attributes should be set and get through flat_dict.setattr
and flat_dict.getattr
.
Although it is possible to override other internal methods, it is not recommended to do so.
__class__
, __dict__
, and getattr
are reserved and cannot be overrode in any manner.
Examples:
Python Console Session>>> d = FlatDict()\n>>> d.d = 1013\n>>> d['d']\n1013\n>>> d['i'] = 1013\n>>> d.i\n1013\n>>> d.a = Variable(1)\n>>> d.b = d.a\n>>> d.a, d.b\n(1, 1)\n>>> d.a += 1\n>>> d.a, d.b\n(2, 2)\n>>> d.a = 3\n>>> d.a, d.b\n(3, 3)\n>>> d.a = Variable('hello')\n>>> f\"{d.a}, world!\"\n'hello, world!'\n>>> d.a = d.a + ', world!'\n>>> d.b\n'hello, world!'\n
Source code in chanfig/flat_dict.py
Pythonclass FlatDict(dict, metaclass=Dict): # type: ignore\nr\"\"\"\n `FlatDict` with attribute-style access.\n `FlatDict` inherits from built-in `dict`.\n It comes with many easy to use helper methods, such as `merge`, `sort`, `difference`, `intersect`.\n It also has full support for IO operations, such as `json` and `yaml`.\n Even better, `FlatDict` has pytorch support built-in.\n You can directly call `FlatDict.cpu()` or `FlatDict.to(\"cpu\")` to move all `torch.Tensor` objects across devices.\n `FlatDict` works best with `Variable` objects.\n Simply call `flat_dict.a = Variable(1); flat_dict.b = flat_dict.a`, and their values will be synced.\n Even better, `FlatDict` support variable interpolation.\n Just set the value of one key to another key (surrounded by braces with $ at the begin, like ${xxx}),\n and calls `flat_dict.interpolate()`, `FlatDict` will interpolate their values and create `Variable` automatically.\n `FlatDict` has many other easy to use helper methods, such as `difference`, `intersect`.\n And has full support for IO operations, such as `json` and `yaml`.\n `FlatDict` also has pytorch support built-in.\n You can directly call `flat_dict.cpu()` or `flat_dict.to(\"cpu\")` to move all `torch.Tensor` objects across devices.\n Attributes:\n indent: Indentation level in printing and dumping to json or yaml.\n Notes:\n `FlatDict` rewrite `__getattribute__` and `__getattr__` to supports attribute-style access to its members.\n Therefore, all internal attributes should be set and get through `flat_dict.setattr` and `flat_dict.getattr`.\n Although it is possible to override other internal methods, it is not recommended to do so.\n `__class__`, `__dict__`, and `getattr` are reserved and cannot be overrode in any manner.\n Examples:\n >>> d = FlatDict()\n >>> d.d = 1013\n >>> d['d']\n 1013\n >>> d['i'] = 1013\n >>> d.i\n 1013\n >>> d.a = Variable(1)\n >>> d.b = d.a\n >>> d.a, d.b\n (1, 1)\n >>> d.a += 1\n >>> d.a, d.b\n (2, 2)\n >>> d.a = 3\n >>> d.a, d.b\n (3, 3)\n >>> d.a = Variable('hello')\n >>> f\"{d.a}, world!\"\n 'hello, world!'\n >>> d.a = d.a + ', world!'\n >>> d.b\n 'hello, world!'\n \"\"\"\n# pylint: disable=R0904\nindent: int = 2\ndef __post_init__(self, *args, **kwargs) -> None:\npass\ndef __getattribute__(self, name: Any) -> Any:\nif (name not in (\"getattr\",) and not (name.startswith(\"__\") and name.endswith(\"__\"))) and name in self:\nreturn self.get(name)\nreturn super().__getattribute__(name)\ndef get(self, name: Any, default: Any = None) -> Any:\nr\"\"\"\n Get value from `FlatDict`.\n Args:\n name:\n default:\n Returns:\n value:\n If `FlatDict` does not contain `name`, return `default`.\n Raises:\n KeyError: If `FlatDict` does not contain `name` and `default` is not specified.\n TypeError: If `name` is not hashable.\n Examples:\n >>> d = FlatDict(d=1013)\n >>> d.get('d')\n 1013\n >>> d['d']\n 1013\n >>> d.d\n 1013\n >>> d.get('d', None)\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.get('f')\n >>> d.get('f', Null)\n Traceback (most recent call last):\n KeyError: 'f'\n \"\"\"\nif name in self:\nreturn dict.__getitem__(self, name)\nif default is not Null:\nreturn default\nreturn self.__missing__(name)\ndef __getitem__(self, name: Any) -> Any:\nreturn self.get(name, default=Null)\ndef __getattr__(self, name: Any) -> Any:\ntry:\nreturn self.get(name, default=Null)\nexcept KeyError:\nraise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\ndef set(self, name: Any, value: Any) -> None:\nr\"\"\"\n Set value of `FlatDict`.\n Args:\n name:\n value:\n Examples:\n >>> d = FlatDict()\n >>> d.set('d', 1013)\n >>> d.get('d')\n 1013\n >>> d['n'] = 'chang'\n >>> d.n\n 'chang'\n >>> d.n = 'liu'\n >>> d['n']\n 'liu'\n \"\"\"\nif name in self and isinstance(self.get(name), Variable):\nself.get(name).set(value)\nelse:\ndict.__setitem__(self, name, value)\ndef __setitem__(self, name: Any, value: Any) -> None:\nself.set(name, value)\ndef __setattr__(self, name: Any, value: Any) -> None:\nself.set(name, value)\ndef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `FlatDict`.\n Args:\n name:\n Examples:\n >>> d = FlatDict(d=1016, n='chang')\n >>> d.d\n 1016\n >>> d.n\n 'chang'\n >>> d.delete('d')\n >>> d.d\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'd'\n >>> del d.n\n >>> d.n\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'n'\n >>> del d.f\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'f'\n \"\"\"\ndict.__delitem__(self, name)\ndef __delitem__(self, name: Any) -> None:\nreturn self.delete(name)\ndef __delattr__(self, name: Any) -> None:\ntry:\nself.delete(name)\nexcept KeyError:\nraise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\ndef __missing__(self, name: Any) -> Any: # pylint: disable=R1710\nraise KeyError(name)\ndef validate(self) -> None:\nr\"\"\"\n Validate `FlatDict`.\n Raises:\n TypeError: If value is not of the type declared in class annotations.\n TypeError: If `Variable` has invalid type.\n ValueError: If `Variable` has invalid value.\n Examples:\n >>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.islower()))\n >>> d = FlatDict(d=Variable(1016, type=str), n=Variable('chang', validator=lambda x: x.islower()))\n Traceback (most recent call last):\n TypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n >>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.isupper()))\n Traceback (most recent call last):\n ValueError: 'n' has invalid value. Value chang is not valid.\n \"\"\"\nself._validate(self)\n@staticmethod\ndef _validate(obj) -> None:\nif isinstance(obj, FlatDict):\nannotations = get_annotations(obj)\nfor name, value in obj.items():\nif annotations and name in annotations and not isvalid(value, annotations[name]):\nraise TypeError(f\"'{name}' has invalid type. Value {value} is not of type {annotations[name]}.\")\nif isinstance(value, Variable):\ntry:\nvalue.validate()\nexcept TypeError as exc:\nraise TypeError(f\"'{name}' has invalid type. {exc}\") from None\nexcept ValueError as exc:\nraise ValueError(f\"'{name}' has invalid value. {exc}\") from None\ndef getattr(self, name: str, default: Any = Null) -> Any:\nr\"\"\"\n Get attribute of `FlatDict`.\n Note that it won't retrieve value in `FlatDict`,\n Args:\n name:\n default:\n Returns:\n value: If `FlatDict` does not contain `name`, return `default`.\n Raises:\n AttributeError: If `FlatDict` does not contain `name` and `default` is not specified.\n Examples:\n >>> d = FlatDict(a=1)\n >>> d.get('a')\n 1\n >>> d.getattr('a')\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'a'\n >>> d.getattr('b', 2)\n 2\n >>> d.setattr('b', 3)\n >>> d.getattr('b')\n 3\n \"\"\"\ntry:\nif name in self.__dict__:\nreturn self.__dict__[name]\nif name in self.__class__.__dict__:\nreturn self.__class__.__dict__[name]\nreturn super().getattr(name, default) # type: ignore\nexcept AttributeError:\nif default is not Null:\nreturn default\nraise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\ndef setattr(self, name: str, value: Any) -> None:\nr\"\"\"\n Set attribute of `FlatDict`.\n Note that it won't alter values in `FlatDict`.\n Args:\n name:\n value:\n Warns:\n RuntimeWarning: If name already exists in `FlatDict`.\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('attr', 'value')\n >>> d.getattr('attr')\n 'value'\n >>> d.set('d', 1013)\n >>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict.\n >>> d.get('d')\n 1013\n >>> d.d\n 1013\n >>> d.getattr('d')\n 1031\n \"\"\"\nif name in self:\nwarn(\nf\"{name} already exists in {self.__class__.__name__}.\\n\"\nf\"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.\",\nRuntimeWarning,\n)\nself.__dict__[name] = value\ndef delattr(self, name: str) -> None:\nr\"\"\"\n Delete attribute of `FlatDict`.\n Note that it won't delete values in `FlatDict`.\n Args:\n name:\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('name', 'chang')\n >>> d.getattr('name')\n 'chang'\n >>> d.delattr('name')\n >>> d.getattr('name')\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'name'\n \"\"\"\ndel self.__dict__[name]\ndef hasattr(self, name: str) -> bool:\nr\"\"\"\n Determine if an attribute exists in `FlatDict`.\n Args:\n name:\n Returns:\n (bool):\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('name', 'chang')\n >>> d.hasattr('name')\n True\n >>> d.delattr('name')\n >>> d.hasattr('name')\n False\n \"\"\"\ntry:\nif name in self.__dict__ or name in self.__class__.__dict__:\nreturn True\nreturn super().hasattr(name) # type: ignore\nexcept AttributeError:\nreturn False\ndef dict(self, cls: Callable = dict) -> Mapping:\nr\"\"\"\n Convert `FlatDict` to other `Mapping`.\n Args:\n cls: Target class to be converted to.\n Returns:\n (Mapping):\n See Also:\n [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nreturn cls(to_dict(self))\n@classmethod\ndef from_dict(cls, obj: Mapping | Sequence) -> Any: # pylint: disable=R0911\nr\"\"\"\n Convert `Mapping` or `Sequence` to `FlatDict`.\n Examples:\n >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})\n FlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])\n FlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n >>> FlatDict.from_dict({1, 2, 3})\n Traceback (most recent call last):\n TypeError: Expected Mapping or Sequence, but got <class 'set'>.\n \"\"\"\nif obj is None:\nreturn cls()\nif issubclass(cls, FlatDict):\ncls = cls.empty # type: ignore # pylint: disable=W0642\nif isinstance(obj, Mapping):\nreturn cls(obj)\nif isinstance(obj, Sequence):\ntry:\nreturn cls(obj)\nexcept ValueError:\nreturn [cls(json) for json in obj]\nraise TypeError(f\"Expected Mapping or Sequence, but got {type(obj)}.\")\ndef sort(self, key: Callable | None = None, reverse: bool = False) -> FlatDict:\nr\"\"\"\n Sort `FlatDict`.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.sort().dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d = FlatDict(b=2, c=3, a=1)\n >>> d.sort().dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> a = [1]\n >>> d = FlatDict(z=0, a=a)\n >>> a.append(2)\n >>> d.sort().dict()\n {'a': [1, 2], 'z': 0}\n \"\"\"\nitems = sorted(self.items(), key=key, reverse=reverse)\nself.clear()\nfor k, v in items:\nself[k] = v\nreturn self\ndef interpolate(self, use_variable: bool = True, interpolators: MutableMapping | None = None) -> FlatDict:\nr\"\"\"\n Perform Variable interpolation.\n Variable interpolation allows you to set the value of one key to be the value of another key easily.\n Args:\n use_variable: Whether to convert values to `Variable` objects.\n interpolators: Mapping contains values for interpolation. Defaults to `self`.\n Raises:\n ValueError: If value is not interpolatable.\n ValueError: If reference to itself.\n ValueError: If has circular reference.\n See Also:\n [Variable][`chanfig.Variable`]: Mutable wrapper of immutable objects.\n Examples:\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${a}.${b}\")\n >>> d.dict()\n {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}\n >>> d.interpolate().dict()\n {'a': 1, 'b': 1, 'c': '1.1'}\n >>> isinstance(d.a, Variable)\n True\n >>> d.a += 1\n >>> d.dict()\n {'a': 2, 'b': 2, 'c': '1.1'}\n >>> d.a is d.b\n True\n >>> d.b is d.c\n False\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${b}\")\n >>> d.dict()\n {'a': 1, 'b': '${a}', 'c': '${b}'}\n >>> d.interpolate(False).dict()\n {'a': 1, 'b': 1, 'c': 1}\n >>> isinstance(d.a, Variable)\n False\n >>> d.a += 1\n >>> d.dict()\n {'a': 2, 'b': 1, 'c': 1}\n >>> d = FlatDict(a=1, b=\"${b}\", c=\"${b}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: Cannot interpolate b to itself.\n >>> d = FlatDict(a=\"${b}\", b=\"${c}\", c=\"${d}\", d=\"${a}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: Circular reference found: a->b->c->d->a.\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${d}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: d is not found in FlatDict(\n ('a'): '1'\n ('b'): '${a}'\n ('c'): '${d}'\n ).\n \"\"\"\ninterpolators = interpolators or self\nplaceholders: dict[str, list[str]] = {}\nfor key, value in self.all_items():\nif isinstance(value, list):\nfor v in value:\nself.find_placeholders(key, v, placeholders)\nelif isinstance(value, Mapping):\nfor v in value.values():\nself.find_placeholders(key, v, placeholders)\nelse:\nself.find_placeholders(key, value, placeholders)\ncircular_references = find_circular_reference(placeholders)\nif circular_references:\nraise ValueError(f\"Circular reference found: {'->'.join(circular_references)}.\")\nif use_variable:\nplaceholder_names = {i for j in placeholders.values() for i in j}\nfor name in list(placeholder_names.difference(placeholders.keys())):\nif name not in interpolators:\nraise ValueError(f\"{name} is not found in {interpolators}.\")\nif not isinstance(interpolators[name], Variable):\ninterpolators[name] = Variable(interpolators[name])\nfor key, value in placeholders.items():\nif isinstance(self[key], list):\nfor index, v in enumerate(self[key]):\nself[key][index] = self.substitute(v, interpolators, value)\nelif isinstance(self[key], Mapping):\nfor k, v in self[key].items():\nself[key][k] = self.substitute(v, interpolators, value)\nelse:\nself[key] = self.substitute(self[key], interpolators, value)\nreturn self\n@staticmethod\ndef find_placeholders(key, value, placeholders):\nplaceholder = find_placeholders(value)\nif placeholder:\nfor index, name in enumerate(placeholder):\nif name.startswith(\".\"):\nplaceholder[index] = key.rsplit(\".\", 1)[0] + name\nif key == name:\nraise ValueError(f\"Cannot interpolate {key} to itself.\")\nplaceholders[key] = placeholder\n@staticmethod\ndef substitute(placeholder, interpolators, value):\ntry:\nif len(value) == 1 and placeholder.startswith(\"${\") and placeholder.endswith(\"}\"):\nreturn interpolators[value[0]]\nreturn placeholder.replace(\"$\", \"\").format(**interpolators)\nexcept KeyError as exc:\nraise ValueError(f\"{exc} is not found in {interpolators}.\") from None\ndef merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Merge `other` into `FlatDict`.\n Args:\n *args: `Mapping` or `Sequence` to be merged.\n overwrite: Whether to overwrite existing values.\n **kwargs: `Mapping` to be merged.\n Returns:\n self:\n **Alias**:\n + `union`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.merge(n).dict()\n {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.merge(l).dict()\n {'a': 1, 'b': 'b', 'c': 3, 'd': 4}\n >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias\n {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d = FlatDict()\n >>> d.merge({1: 1, 2: 2, 3:3}).dict()\n {1: 1, 2: 2, 3: 3}\n >>> d.merge(d.clone()).dict()\n {1: 1, 2: 2, 3: 3}\n >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()\n {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}\n \"\"\"\nif len(args) == 1:\nargs = args[0]\nif isinstance(args, (PathLike, str, bytes)):\nargs = self.load(args) # type: ignore\nwarn(\n\"merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.\",\nPendingDeprecationWarning,\n)\nself._merge(self, args, overwrite=overwrite)\nelif len(args) > 1:\nself._merge(self, args, overwrite=overwrite)\nif kwargs:\nself._merge(self, kwargs, overwrite=overwrite)\nreturn self\n@staticmethod\ndef _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:\nif not that:\nreturn this\nif isinstance(that, Mapping):\nthat = that.items()\nfor key, value in that:\nif key in this and isinstance(this[key], Mapping):\nif isinstance(value, Mapping):\nFlatDict._merge(this[key], value)\nelif overwrite:\nif isinstance(value, FlatDict):\nthis.set(key, value)\nelse:\nthis[key] = value\nelif overwrite or key not in this:\nthis.set(key, value)\nreturn this\ndef union(self, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`merge`][chanfig.FlatDict.merge].\n \"\"\"\nreturn self.merge(*args, **kwargs)\ndef merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Merge content of `file` into `FlatDict`.\n Args:\n file (File):\n *args: Passed to [`load`][chanfig.FlatDict.load].\n **kwargs: Passed to [`load`][chanfig.FlatDict.load].\n Returns:\n self:\n Examples:\n >>> d = FlatDict(a=1, b=1)\n >>> d.merge_from_file(\"tests/test.yaml\").dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nreturn self.merge(self.load(file, *args, **kwargs))\ndef intersect(self, other: Mapping | Iterable | PathStr) -> FlatDict:\nr\"\"\"\n Intersection of `FlatDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n Returns:\n (FlatDict):\n **Alias**:\n + `inter`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.intersect(n).dict()\n {}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.intersect(l).dict()\n {'c': 3}\n >>> d.merge(l).intersect(\"tests/test.yaml\").dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d.intersect(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n >>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias\n {}\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(**{key: value for key, value in other if key in self and self[key] == value}) # type: ignore\ndef inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`intersect`][chanfig.FlatDict.intersect].\n \"\"\"\nreturn self.intersect(other, *args, **kwargs)\ndef difference(self, other: Mapping | Iterable | PathStr) -> FlatDict:\nr\"\"\"\n Difference between `FlatDict` and `other`.\n Args:\n other:\n Returns:\n (FlatDict):\n **Alias**:\n + `diff`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.difference(n).dict()\n {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.difference(l).dict()\n {'d': 4}\n >>> d.merge(l).difference(\"tests/test.yaml\").dict()\n {}\n >>> d.difference(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias\n {'b': 'b', 'c': 'c', 'd': 'd'}\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(\n**{key: value for key, value in other if key not in self or self[key] != value} # type: ignore\n)\ndef diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`difference`][chanfig.FlatDict.difference].\n \"\"\"\nreturn self.difference(other, *args, **kwargs)\ndef to(self, cls: str | TorchDevice | TorchDType) -> FlatDict: # pragma: no cover\nr\"\"\"\n Convert values of `FlatDict` to target `cls`.\n Args:\n cls (str | torch.device | torch.dtype):\n Returns:\n self:\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.to(int)\n Traceback (most recent call last):\n TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.\n \"\"\"\n# pylint: disable=C0103\nif isinstance(cls, (str, TorchDevice, TorchDType)):\nfor k, v in self.all_items():\nif hasattr(v, \"to\"):\nself[k] = v.to(cls)\nreturn self\nraise TypeError(f\"to() only support torch.dtype and torch.device, but got {cls}.\")\ndef cpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to cpu.\n Returns:\n self:\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.cpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='cpu')}\n \"\"\"\nreturn self.to(TorchDevice(\"cpu\"))\ndef gpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to gpu.\n Returns:\n self:\n **Alias**:\n + `cuda`\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.gpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='cuda:0')}\n >>> d.cuda().dict() # alias # doctest: +SKIP\n {'a': tensor(1, device='cuda:0')}\n \"\"\"\nreturn self.to(TorchDevice(\"cuda\"))\ndef cuda(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Alias of [`gpu`][chanfig.FlatDict.gpu].\n \"\"\"\nreturn self.gpu()\ndef tpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to tpu.\n Returns:\n self:\n **Alias**:\n + `xla`\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.tpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='xla:0')}\n >>> d.xla().dict() # alias # doctest: +SKIP\n {'a': tensor(1, device='xla:0')}\n \"\"\"\nreturn self.to(TorchDevice(\"xla\"))\ndef xla(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Alias of [`tpu`][chanfig.FlatDict.tpu].\n \"\"\"\nreturn self.tpu()\ndef copy(self) -> FlatDict:\nr\"\"\"\n Create a shallow copy of `FlatDict`.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.copy()\n >>> c.dict()\n {'a': []}\n >>> d.a.append(1)\n >>> c.dict()\n {'a': [1]}\n >>> c.getattr(\"name\")\n 'Chang'\n \"\"\"\nreturn copy(self)\ndef __deepcopy__(self, memo: Mapping | None = None) -> FlatDict:\n# pylint: disable=C0103\nif memo is not None and id(self) in memo:\nreturn memo[id(self)]\nret = self.empty()\nret.__dict__.update(deepcopy(self.__dict__))\nfor k, v in self.items():\nif isinstance(v, FlatDict):\nret[k] = v.deepcopy(memo=memo)\nelse:\nret[k] = deepcopy(v)\nreturn ret\ndef deepcopy(self, memo: Mapping | None = None) -> FlatDict: # pylint: disable=W0613\nr\"\"\"\n Create a deep copy of `FlatDict`.\n Returns:\n (FlatDict):\n **Alias**:\n + `clone`\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.deepcopy()\n >>> c.dict()\n {'a': []}\n >>> d.a.append(1)\n >>> c.dict()\n {'a': []}\n >>> c.getattr(\"name\")\n 'Chang'\n >>> d == d.clone() # alias\n True\n \"\"\"\nreturn deepcopy(self)\ndef clone(self, memo: Mapping | None = None) -> FlatDict:\nr\"\"\"\n Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].\n \"\"\"\nreturn self.deepcopy(memo=memo)\ndef save(self, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Save `FlatDict` to file.\n Raises:\n ValueError: If save to `IO` and `method` is not specified.\n TypeError: If save to unsupported extension.\n **Alias**:\n + `save`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.save(\"tests/test.yaml\")\n >>> d.save(\"test.conf\")\n Traceback (most recent call last):\n TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"test.yaml\", \"w\") as f:\n ... d.save(f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when saving to IO.\n \"\"\"\nif method is None:\nif isinstance(file, (IOBase, IO)):\nraise ValueError(\"`method` must be specified when saving to IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in YAML:\nreturn self.yaml(file=file, *args, **kwargs) # type: ignore\nif extension in JSON:\nreturn self.json(file=file, *args, **kwargs) # type: ignore\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\ndef dump(self, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Alias of [`save`][chanfig.FlatDict.save].\n \"\"\"\nreturn self.save(file, method, *args, **kwargs)\n@classmethod\ndef load( # pylint: disable=W1113\ncls, file: File, method: str | None = None, *args: Any, **kwargs: Any\n) -> FlatDict:\n\"\"\"\n Load `FlatDict` from file.\n Args:\n file: File to load from.\n method: File type, should be in `JSON` or `YAML`.\n Returns:\n (FlatDict):\n Raises:\n ValueError: If load from `IO` and `method` is not specified.\n TypeError: If dump to unsupported extension.\n Examples:\n >>> d = FlatDict.load(\"tests/test.yaml\")\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d.load(\"tests/test.conf\")\n Traceback (most recent call last):\n TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"tests/test.yaml\") as f:\n ... d.load(f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when loading from IO.\n \"\"\"\nif method is None:\nif isinstance(file, (IOBase, IO)):\nraise ValueError(\"`method` must be specified when loading from IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in JSON:\nreturn cls.from_json(file, *args, **kwargs)\nif extension in YAML:\nreturn cls.from_yaml(file, *args, **kwargs)\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\ndef json(self, file: File, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Dump `FlatDict` to json file.\n This method internally calls `self.jsons()` to generate json string.\n You may overwrite `jsons` in case something is not json serializable.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.json(\"tests/test.json\")\n \"\"\"\nwith self.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nfp.write(self.jsons(*args, **kwargs))\n@classmethod\ndef from_json(cls, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from json file.\n This method internally calls `self.from_jsons()` to construct object from json string.\n You may overwrite `from_jsons` in case something is not json serializable.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict.from_json('tests/test.json')\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nwith cls.open(file) as fp: # pylint: disable=C0103\nif isinstance(file, (IOBase, IO)):\nreturn cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore\nreturn cls.from_jsons(fp.read(), *args, **kwargs)\ndef jsons(self, *args: Any, **kwargs: Any) -> str:\nr\"\"\"\n Dump `FlatDict` to json string.\n Returns:\n (str):\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.jsons()\n '{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}'\n \"\"\"\nkwargs.setdefault(\"cls\", JsonEncoder)\nkwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\nreturn json_dumps(self.dict(), *args, **kwargs)\n@classmethod\ndef from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from json string.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_jsons('{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_jsons('[[\"a\", 1], [\"b\", 2], [\"c\", 3]]').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_jsons('[{\"a\": 1}, {\"b\": 2}, {\"c\": 3}]')\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n \"\"\"\nreturn cls.from_dict(json_loads(string, *args, **kwargs))\ndef yaml(self, file: File, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Dump `FlatDict` to yaml file.\n This method internally calls `self.yamls()` to generate yaml string.\n You may overwrite `yamls` in case something is not yaml serializable.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.yaml(\"tests/test.yaml\")\n \"\"\"\nwith self.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nself.yamls(fp, *args, **kwargs)\n@classmethod\ndef from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from yaml file.\n This method internally calls `self.from_yamls()` to construct object from yaml string.\n You may overwrite `from_yamls` in case something is not yaml serializable.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_yaml('tests/test.yaml').dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nkwargs.setdefault(\"Loader\", YamlLoader)\nwith cls.open(file) as fp: # pylint: disable=C0103\nif isinstance(file, (IOBase, IO)):\nreturn cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore\nreturn cls.from_dict(yaml_load(fp, *args, **kwargs))\ndef yamls(self, *args: Any, **kwargs: Any) -> str:\nr\"\"\"\n Dump `FlatDict` to yaml string.\n Returns:\n (str):\n Examples:\n >>> FlatDict(a=1, b=2, c=3).yamls()\n 'a: 1\\nb: 2\\nc: 3\\n'\n \"\"\"\nkwargs.setdefault(\"Dumper\", YamlDumper)\nkwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\nreturn yaml_dump(self.dict(), *args, **kwargs) # type: ignore\n@classmethod\ndef from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from yaml string.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_yamls('a: 1\\nb: 2\\nc: 3\\n').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_yamls('- - a\\n - 1\\n- - b\\n - 2\\n- - c\\n - 3\\n').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_yamls('- a: 1\\n- b: 2\\n- c: 3\\n')\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n \"\"\"\nkwargs.setdefault(\"Loader\", SafeLoader)\nreturn cls.from_dict(yaml_load(string, *args, **kwargs))\n@staticmethod\n@contextmanager\ndef open(file: File, *args: Any, encoding: str = \"utf-8\", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:\nr\"\"\"\n Open file IO from file path or IO.\n This methods extends the ability of built-in `open` by allowing it to accept an `IOBase` object.\n Args:\n file: File path or IO.\n *args: Additional arguments passed to `open`.\n Defaults to ().\n **kwargs: Any\n Additional keyword arguments passed to `open`.\n Defaults to {}.\n Yields:\n (Generator[IOBase | IO, Any, Any]):\n Examples:\n >>> with FlatDict.open(\"tests/test.yaml\") as fp:\n ... print(fp.read())\n a: 1\n b: 2\n c: 3\n <BLANKLINE>\n >>> io = open(\"tests/test.yaml\")\n >>> with FlatDict.open(io) as fp:\n ... print(fp.read())\n a: 1\n b: 2\n c: 3\n <BLANKLINE>\n >>> with FlatDict.open(123, mode=\"w\") as fp:\n ... print(fp.read())\n Traceback (most recent call last):\n TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int\n \"\"\"\nif isinstance(file, (IOBase, IO)):\nyield file\nelif isinstance(file, (PathLike, str, bytes)):\ntry:\nfile = open(file, *args, encoding=encoding, **kwargs) # type: ignore # noqa: SIM115\nyield file # type: ignore\nfinally:\nwith suppress(Exception):\nfile.close() # type: ignore\nelse:\nraise TypeError(f\"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}\")\n@classmethod\ndef empty(cls, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Initialise an empty `FlatDict`.\n This method is helpful when you inheriting `FlatDict` with default values defined in `__init__()`.\n As use `type(self)()` in this case would copy all the default values, which might not be desired.\n This method will preserve everything in `FlatDict.__class__.__dict__`.\n Returns:\n (FlatDict):\n See Also:\n [`empty_like`][chanfig.FlatDict.empty_like]\n Examples:\n >>> d = FlatDict(a=[])\n >>> c = d.empty()\n >>> c.dict()\n {}\n \"\"\"\nempty = cls.__new__(cls)\nempty.merge(*args, **kwargs) # pylint: disable=W0212\nreturn empty\ndef empty_like(self, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Initialise an empty copy of `FlatDict`.\n This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.\n For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this\n method.\n Returns:\n (FlatDict):\n See Also:\n [`empty`][chanfig.FlatDict.empty]\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.empty_like()\n >>> c.dict()\n {}\n >>> c.getattr(\"name\")\n 'Chang'\n \"\"\"\nempty = self.empty(*args, **kwargs)\nempty.__dict__.update(self.__dict__)\nreturn empty\ndef all_keys(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_keys`][chanfig.NestedDict.all_keys]\n \"\"\"\nyield from self.keys()\ndef all_values(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_values`][chanfig.NestedDict.all_values]\n \"\"\"\nyield from self.values()\ndef all_items(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_items`][chanfig.NestedDict.all_items]\n \"\"\"\nyield from self.items()\ndef dropnull(self) -> FlatDict:\nr\"\"\"\n Drop key-value pairs with `Null` value.\n Returns:\n (FlatDict):\n **Alias**:\n + `dropna`\n Examples:\n >>> d = FlatDict(a=Null, b=Null, c=3)\n >>> d.dict()\n {'a': Null, 'b': Null, 'c': 3}\n >>> d.dropnull().dict()\n {'c': 3}\n >>> d.dropna().dict() # alias\n {'c': 3}\n \"\"\"\nreturn self.empty({k: v for k, v in self.all_items() if v is not Null})\ndef dropna(self) -> FlatDict:\nr\"\"\"\n Alias of [`dropnull`][chanfig.FlatDict.dropnull].\n \"\"\"\nreturn self.dropnull()\n@staticmethod\ndef extra_repr() -> str: # pylint: disable=C0116\nreturn \"\"\ndef __repr__(self) -> str:\nextra_lines = []\nextra_repr = self.extra_repr()\n# empty string will be split into list ['']\nif extra_repr:\nextra_lines = extra_repr.split(\"\\n\")\nchild_lines = []\nfor key, value in self.items():\nkey_repr = repr(key)\nvalue_repr = repr(value)\nvalue_repr = self._add_indent(value_repr)\nchild_lines.append(f\"({key_repr}): {value_repr}\")\n# child_lines.append(f\"{key_repr}: {value_repr}\")\nlines = extra_lines + child_lines\nmain_repr = self.__class__.__name__ + \"(\"\nif lines:\n# simple one-liner info, which most builtin Modules will use\nif len(extra_lines) == 1 and not child_lines:\nmain_repr += extra_lines[0]\nelif len(child_lines) == 1 and not extra_lines and len(child_lines[0]) < 10:\nmain_repr += child_lines[0]\nelse:\nmain_repr += \"\\n \" + \"\\n \".join(lines) + \"\\n\"\nmain_repr += \")\"\nreturn main_repr\ndef _add_indent(self, text: str) -> str:\nlines = text.split(\"\\n\")\n# don't do anything for single-line stuff\nif len(lines) == 1:\nreturn text\nfirst = lines.pop(0)\nlines = [(self.getattr(\"indent\", 2) * \" \") + line for line in lines]\ntext = \"\\n\".join(lines)\ntext = first + \"\\n\" + text\nreturn text\ndef __format__(self, format_spec: str) -> str:\nreturn repr(self.empty({k: v.__format__(format_spec) for k, v in self.all_items()}))\ndef __hash__(self):\nreturn hash(frozenset(self.items()))\ndef _ipython_display_(self): # pragma: no cover\nreturn repr(self)\ndef _ipython_canary_method_should_not_exist_(self): # pragma: no cover\nreturn None\ndef aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf(self): # pragma: no cover\nreturn None\ndef __rich__(self): # pragma: no cover\nreturn self.__repr__()\n
"},{"location":"flat_dict/#chanfig.FlatDict.all_items","title":"all_items()
","text":"Equivalent to keys
.
This method is provided solely to make methods work on both FlatDict
and NestedDict
.
all_items
chanfig/flat_dict.py
Pythondef all_items(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_items`][chanfig.NestedDict.all_items]\n \"\"\"\nyield from self.items()\n
"},{"location":"flat_dict/#chanfig.FlatDict.all_keys","title":"all_keys()
","text":"Equivalent to keys
.
This method is provided solely to make methods work on both FlatDict
and NestedDict
.
all_keys
chanfig/flat_dict.py
Pythondef all_keys(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_keys`][chanfig.NestedDict.all_keys]\n \"\"\"\nyield from self.keys()\n
"},{"location":"flat_dict/#chanfig.FlatDict.all_values","title":"all_values()
","text":"Equivalent to keys
.
This method is provided solely to make methods work on both FlatDict
and NestedDict
.
all_values
chanfig/flat_dict.py
Pythondef all_values(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_values`][chanfig.NestedDict.all_values]\n \"\"\"\nyield from self.values()\n
"},{"location":"flat_dict/#chanfig.FlatDict.clone","title":"clone(memo=None)
","text":"Alias of deepcopy
.
chanfig/flat_dict.py
Pythondef clone(self, memo: Mapping | None = None) -> FlatDict:\nr\"\"\"\n Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].\n \"\"\"\nreturn self.deepcopy(memo=memo)\n
"},{"location":"flat_dict/#chanfig.FlatDict.copy","title":"copy()
","text":"Create a shallow copy of FlatDict
.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> d = FlatDict(a=[])\n>>> d.setattr(\"name\", \"Chang\")\n>>> c = d.copy()\n>>> c.dict()\n{'a': []}\n>>> d.a.append(1)\n>>> c.dict()\n{'a': [1]}\n>>> c.getattr(\"name\")\n'Chang'\n
Source code in chanfig/flat_dict.py
Pythondef copy(self) -> FlatDict:\nr\"\"\"\n Create a shallow copy of `FlatDict`.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.copy()\n >>> c.dict()\n {'a': []}\n >>> d.a.append(1)\n >>> c.dict()\n {'a': [1]}\n >>> c.getattr(\"name\")\n 'Chang'\n \"\"\"\nreturn copy(self)\n
"},{"location":"flat_dict/#chanfig.FlatDict.cpu","title":"cpu()
","text":"Move all tensors to cpu.
Returns:
Name Type Descriptionself
FlatDict
Examples:
Python Console Session>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.cpu().dict()\n{'a': tensor(1, device='cpu')}\n
Source code in chanfig/flat_dict.py
Pythondef cpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to cpu.\n Returns:\n self:\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.cpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='cpu')}\n \"\"\"\nreturn self.to(TorchDevice(\"cpu\"))\n
"},{"location":"flat_dict/#chanfig.FlatDict.cuda","title":"cuda()
","text":"Alias of gpu
.
chanfig/flat_dict.py
Pythondef cuda(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Alias of [`gpu`][chanfig.FlatDict.gpu].\n \"\"\"\nreturn self.gpu()\n
"},{"location":"flat_dict/#chanfig.FlatDict.deepcopy","title":"deepcopy(memo=None)
","text":"Create a deep copy of FlatDict
.
Returns:
Type DescriptionFlatDict
Alias:
clone
Examples:
Python Console Session>>> d = FlatDict(a=[])\n>>> d.setattr(\"name\", \"Chang\")\n>>> c = d.deepcopy()\n>>> c.dict()\n{'a': []}\n>>> d.a.append(1)\n>>> c.dict()\n{'a': []}\n>>> c.getattr(\"name\")\n'Chang'\n>>> d == d.clone() # alias\nTrue\n
Source code in chanfig/flat_dict.py
Pythondef deepcopy(self, memo: Mapping | None = None) -> FlatDict: # pylint: disable=W0613\nr\"\"\"\n Create a deep copy of `FlatDict`.\n Returns:\n (FlatDict):\n **Alias**:\n + `clone`\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.deepcopy()\n >>> c.dict()\n {'a': []}\n >>> d.a.append(1)\n >>> c.dict()\n {'a': []}\n >>> c.getattr(\"name\")\n 'Chang'\n >>> d == d.clone() # alias\n True\n \"\"\"\nreturn deepcopy(self)\n
"},{"location":"flat_dict/#chanfig.FlatDict.delattr","title":"delattr(name)
","text":"Delete attribute of FlatDict
.
Note that it won\u2019t delete values in FlatDict
.
Parameters:
Name Type Description Defaultname
str
required Examples:
Python Console Session>>> d = FlatDict()\n>>> d.setattr('name', 'chang')\n>>> d.getattr('name')\n'chang'\n>>> d.delattr('name')\n>>> d.getattr('name')\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'name'\n
Source code in chanfig/flat_dict.py
Pythondef delattr(self, name: str) -> None:\nr\"\"\"\n Delete attribute of `FlatDict`.\n Note that it won't delete values in `FlatDict`.\n Args:\n name:\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('name', 'chang')\n >>> d.getattr('name')\n 'chang'\n >>> d.delattr('name')\n >>> d.getattr('name')\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'name'\n \"\"\"\ndel self.__dict__[name]\n
"},{"location":"flat_dict/#chanfig.FlatDict.delete","title":"delete(name)
","text":"Delete value from FlatDict
.
Parameters:
Name Type Description Defaultname
Any
required Examples:
Python Console Session>>> d = FlatDict(d=1016, n='chang')\n>>> d.d\n1016\n>>> d.n\n'chang'\n>>> d.delete('d')\n>>> d.d\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'd'\n>>> del d.n\n>>> d.n\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'n'\n>>> del d.f\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'f'\n
Source code in chanfig/flat_dict.py
Pythondef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `FlatDict`.\n Args:\n name:\n Examples:\n >>> d = FlatDict(d=1016, n='chang')\n >>> d.d\n 1016\n >>> d.n\n 'chang'\n >>> d.delete('d')\n >>> d.d\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'd'\n >>> del d.n\n >>> d.n\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'n'\n >>> del d.f\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'f'\n \"\"\"\ndict.__delitem__(self, name)\n
"},{"location":"flat_dict/#chanfig.FlatDict.dict","title":"dict(cls=dict)
","text":"Convert FlatDict
to other Mapping
.
Parameters:
Name Type Description Defaultcls
Callable
Target class to be converted to.
dict
Returns:
Type DescriptionMapping
See Also to_dict
: Implementation of dict
.
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/flat_dict.py
Pythondef dict(self, cls: Callable = dict) -> Mapping:\nr\"\"\"\n Convert `FlatDict` to other `Mapping`.\n Args:\n cls: Target class to be converted to.\n Returns:\n (Mapping):\n See Also:\n [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nreturn cls(to_dict(self))\n
"},{"location":"flat_dict/#chanfig.FlatDict.diff","title":"diff(other, *args, **kwargs)
","text":"Alias of difference
.
chanfig/flat_dict.py
Pythondef diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`difference`][chanfig.FlatDict.difference].\n \"\"\"\nreturn self.difference(other, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.difference","title":"difference(other)
","text":"Difference between FlatDict
and other
.
Parameters:
Name Type Description Defaultother
Mapping | Iterable | PathStr
required Returns:
Type DescriptionFlatDict
Alias:
diff
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.difference(n).dict()\n{'b': 'b', 'c': 'c', 'd': 'd'}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.difference(l).dict()\n{'d': 4}\n>>> d.merge(l).difference(\"tests/test.yaml\").dict()\n{}\n>>> d.difference(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n>>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias\n{'b': 'b', 'c': 'c', 'd': 'd'}\n
Source code in chanfig/flat_dict.py
Pythondef difference(self, other: Mapping | Iterable | PathStr) -> FlatDict:\nr\"\"\"\n Difference between `FlatDict` and `other`.\n Args:\n other:\n Returns:\n (FlatDict):\n **Alias**:\n + `diff`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.difference(n).dict()\n {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.difference(l).dict()\n {'d': 4}\n >>> d.merge(l).difference(\"tests/test.yaml\").dict()\n {}\n >>> d.difference(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias\n {'b': 'b', 'c': 'c', 'd': 'd'}\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(\n**{key: value for key, value in other if key not in self or self[key] != value} # type: ignore\n)\n
"},{"location":"flat_dict/#chanfig.FlatDict.dropna","title":"dropna()
","text":"Alias of dropnull
.
chanfig/flat_dict.py
Pythondef dropna(self) -> FlatDict:\nr\"\"\"\n Alias of [`dropnull`][chanfig.FlatDict.dropnull].\n \"\"\"\nreturn self.dropnull()\n
"},{"location":"flat_dict/#chanfig.FlatDict.dropnull","title":"dropnull()
","text":"Drop key-value pairs with Null
value.
Returns:
Type DescriptionFlatDict
Alias:
dropna
Examples:
Python Console Session>>> d = FlatDict(a=Null, b=Null, c=3)\n>>> d.dict()\n{'a': Null, 'b': Null, 'c': 3}\n>>> d.dropnull().dict()\n{'c': 3}\n>>> d.dropna().dict() # alias\n{'c': 3}\n
Source code in chanfig/flat_dict.py
Pythondef dropnull(self) -> FlatDict:\nr\"\"\"\n Drop key-value pairs with `Null` value.\n Returns:\n (FlatDict):\n **Alias**:\n + `dropna`\n Examples:\n >>> d = FlatDict(a=Null, b=Null, c=3)\n >>> d.dict()\n {'a': Null, 'b': Null, 'c': 3}\n >>> d.dropnull().dict()\n {'c': 3}\n >>> d.dropna().dict() # alias\n {'c': 3}\n \"\"\"\nreturn self.empty({k: v for k, v in self.all_items() if v is not Null})\n
"},{"location":"flat_dict/#chanfig.FlatDict.dump","title":"dump(file, method=None, *args, **kwargs)
","text":"Alias of save
.
chanfig/flat_dict.py
Pythondef dump(self, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Alias of [`save`][chanfig.FlatDict.save].\n \"\"\"\nreturn self.save(file, method, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.empty","title":"empty(*args, **kwargs)
classmethod
","text":"Initialise an empty FlatDict
.
This method is helpful when you inheriting FlatDict
with default values defined in __init__()
. As use type(self)()
in this case would copy all the default values, which might not be desired.
This method will preserve everything in FlatDict.__class__.__dict__
.
Returns:
Type DescriptionFlatDict
See Also empty_like
Examples:
Python Console Session>>> d = FlatDict(a=[])\n>>> c = d.empty()\n>>> c.dict()\n{}\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef empty(cls, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Initialise an empty `FlatDict`.\n This method is helpful when you inheriting `FlatDict` with default values defined in `__init__()`.\n As use `type(self)()` in this case would copy all the default values, which might not be desired.\n This method will preserve everything in `FlatDict.__class__.__dict__`.\n Returns:\n (FlatDict):\n See Also:\n [`empty_like`][chanfig.FlatDict.empty_like]\n Examples:\n >>> d = FlatDict(a=[])\n >>> c = d.empty()\n >>> c.dict()\n {}\n \"\"\"\nempty = cls.__new__(cls)\nempty.merge(*args, **kwargs) # pylint: disable=W0212\nreturn empty\n
"},{"location":"flat_dict/#chanfig.FlatDict.empty_like","title":"empty_like(*args, **kwargs)
","text":"Initialise an empty copy of FlatDict
.
This method will preserve everything in FlatDict.__class__.__dict__
and FlatDict.__dict__
.
For example, property
s are saved in __dict__
, they will keep their original reference after calling this method.
Returns:
Type DescriptionFlatDict
See Also empty
Examples:
Python Console Session>>> d = FlatDict(a=[])\n>>> d.setattr(\"name\", \"Chang\")\n>>> c = d.empty_like()\n>>> c.dict()\n{}\n>>> c.getattr(\"name\")\n'Chang'\n
Source code in chanfig/flat_dict.py
Pythondef empty_like(self, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Initialise an empty copy of `FlatDict`.\n This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.\n For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this\n method.\n Returns:\n (FlatDict):\n See Also:\n [`empty`][chanfig.FlatDict.empty]\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.empty_like()\n >>> c.dict()\n {}\n >>> c.getattr(\"name\")\n 'Chang'\n \"\"\"\nempty = self.empty(*args, **kwargs)\nempty.__dict__.update(self.__dict__)\nreturn empty\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_dict","title":"from_dict(obj)
classmethod
","text":"Convert Mapping
or Sequence
to FlatDict
.
Examples:
Python Console Session>>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})\nFlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n)\n>>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])\nFlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n)\n>>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])\n[FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n>>> FlatDict.from_dict({1, 2, 3})\nTraceback (most recent call last):\nTypeError: Expected Mapping or Sequence, but got <class 'set'>.\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_dict(cls, obj: Mapping | Sequence) -> Any: # pylint: disable=R0911\nr\"\"\"\n Convert `Mapping` or `Sequence` to `FlatDict`.\n Examples:\n >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})\n FlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])\n FlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n >>> FlatDict.from_dict({1, 2, 3})\n Traceback (most recent call last):\n TypeError: Expected Mapping or Sequence, but got <class 'set'>.\n \"\"\"\nif obj is None:\nreturn cls()\nif issubclass(cls, FlatDict):\ncls = cls.empty # type: ignore # pylint: disable=W0642\nif isinstance(obj, Mapping):\nreturn cls(obj)\nif isinstance(obj, Sequence):\ntry:\nreturn cls(obj)\nexcept ValueError:\nreturn [cls(json) for json in obj]\nraise TypeError(f\"Expected Mapping or Sequence, but got {type(obj)}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_json","title":"from_json(file, *args, **kwargs)
classmethod
","text":"Construct FlatDict
from json file.
This method internally calls self.from_jsons()
to construct object from json string. You may overwrite from_jsons
in case something is not json serializable.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> d = FlatDict.from_json('tests/test.json')\n>>> d.dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_json(cls, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from json file.\n This method internally calls `self.from_jsons()` to construct object from json string.\n You may overwrite `from_jsons` in case something is not json serializable.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict.from_json('tests/test.json')\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nwith cls.open(file) as fp: # pylint: disable=C0103\nif isinstance(file, (IOBase, IO)):\nreturn cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore\nreturn cls.from_jsons(fp.read(), *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_jsons","title":"from_jsons(string, *args, **kwargs)
classmethod
","text":"Construct FlatDict
from json string.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> FlatDict.from_jsons('{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}').dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> FlatDict.from_jsons('[[\"a\", 1], [\"b\", 2], [\"c\", 3]]').dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> FlatDict.from_jsons('[{\"a\": 1}, {\"b\": 2}, {\"c\": 3}]')\n[FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from json string.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_jsons('{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_jsons('[[\"a\", 1], [\"b\", 2], [\"c\", 3]]').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_jsons('[{\"a\": 1}, {\"b\": 2}, {\"c\": 3}]')\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n \"\"\"\nreturn cls.from_dict(json_loads(string, *args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_yaml","title":"from_yaml(file, *args, **kwargs)
classmethod
","text":"Construct FlatDict
from yaml file.
This method internally calls self.from_yamls()
to construct object from yaml string. You may overwrite from_yamls
in case something is not yaml serializable.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> FlatDict.from_yaml('tests/test.yaml').dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from yaml file.\n This method internally calls `self.from_yamls()` to construct object from yaml string.\n You may overwrite `from_yamls` in case something is not yaml serializable.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_yaml('tests/test.yaml').dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nkwargs.setdefault(\"Loader\", YamlLoader)\nwith cls.open(file) as fp: # pylint: disable=C0103\nif isinstance(file, (IOBase, IO)):\nreturn cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore\nreturn cls.from_dict(yaml_load(fp, *args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_yamls","title":"from_yamls(string, *args, **kwargs)
classmethod
","text":"Construct FlatDict
from yaml string.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> FlatDict.from_yamls('a: 1\\nb: 2\\nc: 3\\n').dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> FlatDict.from_yamls('- - a\\n - 1\\n- - b\\n - 2\\n- - c\\n - 3\\n').dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> FlatDict.from_yamls('- a: 1\\n- b: 2\\n- c: 3\\n')\n[FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from yaml string.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_yamls('a: 1\\nb: 2\\nc: 3\\n').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_yamls('- - a\\n - 1\\n- - b\\n - 2\\n- - c\\n - 3\\n').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_yamls('- a: 1\\n- b: 2\\n- c: 3\\n')\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n \"\"\"\nkwargs.setdefault(\"Loader\", SafeLoader)\nreturn cls.from_dict(yaml_load(string, *args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.get","title":"get(name, default=None)
","text":"Get value from FlatDict
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
None
Returns:
Name Type Descriptionvalue
Any
If FlatDict
does not contain name
, return default
.
Raises:
Type DescriptionKeyError
If FlatDict
does not contain name
and default
is not specified.
TypeError
If name
is not hashable.
Examples:
Python Console Session>>> d = FlatDict(d=1013)\n>>> d.get('d')\n1013\n>>> d['d']\n1013\n>>> d.d\n1013\n>>> d.get('d', None)\n1013\n>>> d.get('f', 2)\n2\n>>> d.get('f')\n>>> d.get('f', Null)\nTraceback (most recent call last):\nKeyError: 'f'\n
Source code in chanfig/flat_dict.py
Pythondef get(self, name: Any, default: Any = None) -> Any:\nr\"\"\"\n Get value from `FlatDict`.\n Args:\n name:\n default:\n Returns:\n value:\n If `FlatDict` does not contain `name`, return `default`.\n Raises:\n KeyError: If `FlatDict` does not contain `name` and `default` is not specified.\n TypeError: If `name` is not hashable.\n Examples:\n >>> d = FlatDict(d=1013)\n >>> d.get('d')\n 1013\n >>> d['d']\n 1013\n >>> d.d\n 1013\n >>> d.get('d', None)\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.get('f')\n >>> d.get('f', Null)\n Traceback (most recent call last):\n KeyError: 'f'\n \"\"\"\nif name in self:\nreturn dict.__getitem__(self, name)\nif default is not Null:\nreturn default\nreturn self.__missing__(name)\n
"},{"location":"flat_dict/#chanfig.FlatDict.getattr","title":"getattr(name, default=Null)
","text":"Get attribute of FlatDict
.
Note that it won\u2019t retrieve value in FlatDict
,
Parameters:
Name Type Description Defaultname
str
required default
Any
Null
Returns:
Name Type Descriptionvalue
Any
If FlatDict
does not contain name
, return default
.
Raises:
Type DescriptionAttributeError
If FlatDict
does not contain name
and default
is not specified.
Examples:
Python Console Session>>> d = FlatDict(a=1)\n>>> d.get('a')\n1\n>>> d.getattr('a')\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'a'\n>>> d.getattr('b', 2)\n2\n>>> d.setattr('b', 3)\n>>> d.getattr('b')\n3\n
Source code in chanfig/flat_dict.py
Pythondef getattr(self, name: str, default: Any = Null) -> Any:\nr\"\"\"\n Get attribute of `FlatDict`.\n Note that it won't retrieve value in `FlatDict`,\n Args:\n name:\n default:\n Returns:\n value: If `FlatDict` does not contain `name`, return `default`.\n Raises:\n AttributeError: If `FlatDict` does not contain `name` and `default` is not specified.\n Examples:\n >>> d = FlatDict(a=1)\n >>> d.get('a')\n 1\n >>> d.getattr('a')\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'a'\n >>> d.getattr('b', 2)\n 2\n >>> d.setattr('b', 3)\n >>> d.getattr('b')\n 3\n \"\"\"\ntry:\nif name in self.__dict__:\nreturn self.__dict__[name]\nif name in self.__class__.__dict__:\nreturn self.__class__.__dict__[name]\nreturn super().getattr(name, default) # type: ignore\nexcept AttributeError:\nif default is not Null:\nreturn default\nraise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\n
"},{"location":"flat_dict/#chanfig.FlatDict.gpu","title":"gpu()
","text":"Move all tensors to gpu.
Returns:
Name Type Descriptionself
FlatDict
Alias:
cuda
Examples:
Python Console Session>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.gpu().dict()\n{'a': tensor(1, device='cuda:0')}\n>>> d.cuda().dict() # alias\n{'a': tensor(1, device='cuda:0')}\n
Source code in chanfig/flat_dict.py
Pythondef gpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to gpu.\n Returns:\n self:\n **Alias**:\n + `cuda`\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.gpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='cuda:0')}\n >>> d.cuda().dict() # alias # doctest: +SKIP\n {'a': tensor(1, device='cuda:0')}\n \"\"\"\nreturn self.to(TorchDevice(\"cuda\"))\n
"},{"location":"flat_dict/#chanfig.FlatDict.hasattr","title":"hasattr(name)
","text":"Determine if an attribute exists in FlatDict
.
Parameters:
Name Type Description Defaultname
str
required Returns:
Type Descriptionbool
Examples:
Python Console Session>>> d = FlatDict()\n>>> d.setattr('name', 'chang')\n>>> d.hasattr('name')\nTrue\n>>> d.delattr('name')\n>>> d.hasattr('name')\nFalse\n
Source code in chanfig/flat_dict.py
Pythondef hasattr(self, name: str) -> bool:\nr\"\"\"\n Determine if an attribute exists in `FlatDict`.\n Args:\n name:\n Returns:\n (bool):\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('name', 'chang')\n >>> d.hasattr('name')\n True\n >>> d.delattr('name')\n >>> d.hasattr('name')\n False\n \"\"\"\ntry:\nif name in self.__dict__ or name in self.__class__.__dict__:\nreturn True\nreturn super().hasattr(name) # type: ignore\nexcept AttributeError:\nreturn False\n
"},{"location":"flat_dict/#chanfig.FlatDict.inter","title":"inter(other, *args, **kwargs)
","text":"Alias of intersect
.
chanfig/flat_dict.py
Pythondef inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`intersect`][chanfig.FlatDict.intersect].\n \"\"\"\nreturn self.intersect(other, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.interpolate","title":"interpolate(use_variable=True, interpolators=None)
","text":"Perform Variable interpolation.
Variable interpolation allows you to set the value of one key to be the value of another key easily.
Parameters:
Name Type Description Defaultuse_variable
bool
Whether to convert values to Variable
objects.
True
interpolators
MutableMapping | None
Mapping contains values for interpolation. Defaults to self
.
None
Raises:
Type DescriptionValueError
If value is not interpolatable.
ValueError
If reference to itself.
ValueError
If has circular reference.
See Also[Variable][chanfig.Variable
]: Mutable wrapper of immutable objects.
Examples:
Python Console Session>>> d = FlatDict(a=1, b=\"${a}\", c=\"${a}.${b}\")\n>>> d.dict()\n{'a': 1, 'b': '${a}', 'c': '${a}.${b}'}\n>>> d.interpolate().dict()\n{'a': 1, 'b': 1, 'c': '1.1'}\n>>> isinstance(d.a, Variable)\nTrue\n>>> d.a += 1\n>>> d.dict()\n{'a': 2, 'b': 2, 'c': '1.1'}\n>>> d.a is d.b\nTrue\n>>> d.b is d.c\nFalse\n>>> d = FlatDict(a=1, b=\"${a}\", c=\"${b}\")\n>>> d.dict()\n{'a': 1, 'b': '${a}', 'c': '${b}'}\n>>> d.interpolate(False).dict()\n{'a': 1, 'b': 1, 'c': 1}\n>>> isinstance(d.a, Variable)\nFalse\n>>> d.a += 1\n>>> d.dict()\n{'a': 2, 'b': 1, 'c': 1}\n>>> d = FlatDict(a=1, b=\"${b}\", c=\"${b}\")\n>>> d.interpolate().dict()\nTraceback (most recent call last):\nValueError: Cannot interpolate b to itself.\n>>> d = FlatDict(a=\"${b}\", b=\"${c}\", c=\"${d}\", d=\"${a}\")\n>>> d.interpolate().dict()\nTraceback (most recent call last):\nValueError: Circular reference found: a->b->c->d->a.\n>>> d = FlatDict(a=1, b=\"${a}\", c=\"${d}\")\n>>> d.interpolate().dict()\nTraceback (most recent call last):\nValueError: d is not found in FlatDict(\n ('a'): '1'\n ('b'): '${a}'\n ('c'): '${d}'\n).\n
Source code in chanfig/flat_dict.py
Pythondef interpolate(self, use_variable: bool = True, interpolators: MutableMapping | None = None) -> FlatDict:\nr\"\"\"\n Perform Variable interpolation.\n Variable interpolation allows you to set the value of one key to be the value of another key easily.\n Args:\n use_variable: Whether to convert values to `Variable` objects.\n interpolators: Mapping contains values for interpolation. Defaults to `self`.\n Raises:\n ValueError: If value is not interpolatable.\n ValueError: If reference to itself.\n ValueError: If has circular reference.\n See Also:\n [Variable][`chanfig.Variable`]: Mutable wrapper of immutable objects.\n Examples:\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${a}.${b}\")\n >>> d.dict()\n {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}\n >>> d.interpolate().dict()\n {'a': 1, 'b': 1, 'c': '1.1'}\n >>> isinstance(d.a, Variable)\n True\n >>> d.a += 1\n >>> d.dict()\n {'a': 2, 'b': 2, 'c': '1.1'}\n >>> d.a is d.b\n True\n >>> d.b is d.c\n False\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${b}\")\n >>> d.dict()\n {'a': 1, 'b': '${a}', 'c': '${b}'}\n >>> d.interpolate(False).dict()\n {'a': 1, 'b': 1, 'c': 1}\n >>> isinstance(d.a, Variable)\n False\n >>> d.a += 1\n >>> d.dict()\n {'a': 2, 'b': 1, 'c': 1}\n >>> d = FlatDict(a=1, b=\"${b}\", c=\"${b}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: Cannot interpolate b to itself.\n >>> d = FlatDict(a=\"${b}\", b=\"${c}\", c=\"${d}\", d=\"${a}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: Circular reference found: a->b->c->d->a.\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${d}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: d is not found in FlatDict(\n ('a'): '1'\n ('b'): '${a}'\n ('c'): '${d}'\n ).\n \"\"\"\ninterpolators = interpolators or self\nplaceholders: dict[str, list[str]] = {}\nfor key, value in self.all_items():\nif isinstance(value, list):\nfor v in value:\nself.find_placeholders(key, v, placeholders)\nelif isinstance(value, Mapping):\nfor v in value.values():\nself.find_placeholders(key, v, placeholders)\nelse:\nself.find_placeholders(key, value, placeholders)\ncircular_references = find_circular_reference(placeholders)\nif circular_references:\nraise ValueError(f\"Circular reference found: {'->'.join(circular_references)}.\")\nif use_variable:\nplaceholder_names = {i for j in placeholders.values() for i in j}\nfor name in list(placeholder_names.difference(placeholders.keys())):\nif name not in interpolators:\nraise ValueError(f\"{name} is not found in {interpolators}.\")\nif not isinstance(interpolators[name], Variable):\ninterpolators[name] = Variable(interpolators[name])\nfor key, value in placeholders.items():\nif isinstance(self[key], list):\nfor index, v in enumerate(self[key]):\nself[key][index] = self.substitute(v, interpolators, value)\nelif isinstance(self[key], Mapping):\nfor k, v in self[key].items():\nself[key][k] = self.substitute(v, interpolators, value)\nelse:\nself[key] = self.substitute(self[key], interpolators, value)\nreturn self\n
"},{"location":"flat_dict/#chanfig.FlatDict.intersect","title":"intersect(other)
","text":"Intersection of FlatDict
and other
.
Parameters:
Name Type Description Defaultother
Mapping | Iterable | PathStr
required Returns:
Type DescriptionFlatDict
Alias:
inter
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.intersect(n).dict()\n{}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.intersect(l).dict()\n{'c': 3}\n>>> d.merge(l).intersect(\"tests/test.yaml\").dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> d.intersect(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n>>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias\n{}\n
Source code in chanfig/flat_dict.py
Pythondef intersect(self, other: Mapping | Iterable | PathStr) -> FlatDict:\nr\"\"\"\n Intersection of `FlatDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n Returns:\n (FlatDict):\n **Alias**:\n + `inter`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.intersect(n).dict()\n {}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.intersect(l).dict()\n {'c': 3}\n >>> d.merge(l).intersect(\"tests/test.yaml\").dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d.intersect(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n >>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias\n {}\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(**{key: value for key, value in other if key in self and self[key] == value}) # type: ignore\n
"},{"location":"flat_dict/#chanfig.FlatDict.json","title":"json(file, *args, **kwargs)
","text":"Dump FlatDict
to json file.
This method internally calls self.jsons()
to generate json string. You may overwrite jsons
in case something is not json serializable.
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.json(\"tests/test.json\")\n
Source code in chanfig/flat_dict.py
Pythondef json(self, file: File, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Dump `FlatDict` to json file.\n This method internally calls `self.jsons()` to generate json string.\n You may overwrite `jsons` in case something is not json serializable.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.json(\"tests/test.json\")\n \"\"\"\nwith self.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nfp.write(self.jsons(*args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.jsons","title":"jsons(*args, **kwargs)
","text":"Dump FlatDict
to json string.
Returns:
Type Descriptionstr
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.jsons()\n'{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}'\n
Source code in chanfig/flat_dict.py
Pythondef jsons(self, *args: Any, **kwargs: Any) -> str:\nr\"\"\"\n Dump `FlatDict` to json string.\n Returns:\n (str):\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.jsons()\n '{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}'\n \"\"\"\nkwargs.setdefault(\"cls\", JsonEncoder)\nkwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\nreturn json_dumps(self.dict(), *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.load","title":"load(file, method=None, *args, **kwargs)
classmethod
","text":"Load FlatDict
from file.
Parameters:
Name Type Description Defaultfile
File
File to load from.
requiredmethod
str | None
File type, should be in JSON
or YAML
.
None
Returns:
Type DescriptionFlatDict
Raises:
Type DescriptionValueError
If load from IO
and method
is not specified.
TypeError
If dump to unsupported extension.
Examples:
Python Console Session>>> d = FlatDict.load(\"tests/test.yaml\")\n>>> d.dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> d.load(\"tests/test.conf\")\nTraceback (most recent call last):\nTypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"tests/test.yaml\") as f:\n... d.load(f)\nTraceback (most recent call last):\nValueError: `method` must be specified when loading from IO.\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef load( # pylint: disable=W1113\ncls, file: File, method: str | None = None, *args: Any, **kwargs: Any\n) -> FlatDict:\n\"\"\"\n Load `FlatDict` from file.\n Args:\n file: File to load from.\n method: File type, should be in `JSON` or `YAML`.\n Returns:\n (FlatDict):\n Raises:\n ValueError: If load from `IO` and `method` is not specified.\n TypeError: If dump to unsupported extension.\n Examples:\n >>> d = FlatDict.load(\"tests/test.yaml\")\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d.load(\"tests/test.conf\")\n Traceback (most recent call last):\n TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"tests/test.yaml\") as f:\n ... d.load(f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when loading from IO.\n \"\"\"\nif method is None:\nif isinstance(file, (IOBase, IO)):\nraise ValueError(\"`method` must be specified when loading from IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in JSON:\nreturn cls.from_json(file, *args, **kwargs)\nif extension in YAML:\nreturn cls.from_yaml(file, *args, **kwargs)\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.merge","title":"merge(*args, overwrite=True, **kwargs)
","text":"Merge other
into FlatDict
.
Parameters:
Name Type Description Default*args
Any
Mapping
or Sequence
to be merged.
()
overwrite
bool
Whether to overwrite existing values.
True
**kwargs
Any
Mapping
to be merged.
{}
Returns:
Name Type Descriptionself
FlatDict
Alias:
union
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.merge(n).dict()\n{'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.merge(l).dict()\n{'a': 1, 'b': 'b', 'c': 3, 'd': 4}\n>>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias\n{'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d = FlatDict()\n>>> d.merge({1: 1, 2: 2, 3:3}).dict()\n{1: 1, 2: 2, 3: 3}\n>>> d.merge(d.clone()).dict()\n{1: 1, 2: 2, 3: 3}\n>>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()\n{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}\n
Source code in chanfig/flat_dict.py
Pythondef merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Merge `other` into `FlatDict`.\n Args:\n *args: `Mapping` or `Sequence` to be merged.\n overwrite: Whether to overwrite existing values.\n **kwargs: `Mapping` to be merged.\n Returns:\n self:\n **Alias**:\n + `union`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.merge(n).dict()\n {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.merge(l).dict()\n {'a': 1, 'b': 'b', 'c': 3, 'd': 4}\n >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias\n {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d = FlatDict()\n >>> d.merge({1: 1, 2: 2, 3:3}).dict()\n {1: 1, 2: 2, 3: 3}\n >>> d.merge(d.clone()).dict()\n {1: 1, 2: 2, 3: 3}\n >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()\n {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}\n \"\"\"\nif len(args) == 1:\nargs = args[0]\nif isinstance(args, (PathLike, str, bytes)):\nargs = self.load(args) # type: ignore\nwarn(\n\"merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.\",\nPendingDeprecationWarning,\n)\nself._merge(self, args, overwrite=overwrite)\nelif len(args) > 1:\nself._merge(self, args, overwrite=overwrite)\nif kwargs:\nself._merge(self, kwargs, overwrite=overwrite)\nreturn self\n
"},{"location":"flat_dict/#chanfig.FlatDict.merge_from_file","title":"merge_from_file(file, *args, **kwargs)
","text":"Merge content of file
into FlatDict
.
Parameters:
Name Type Description Defaultfile
File
required *args
Any
Passed to load
.
()
**kwargs
Any
Passed to load
.
{}
Returns:
Name Type Descriptionself
FlatDict
Examples:
Python Console Session>>> d = FlatDict(a=1, b=1)\n>>> d.merge_from_file(\"tests/test.yaml\").dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/flat_dict.py
Pythondef merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Merge content of `file` into `FlatDict`.\n Args:\n file (File):\n *args: Passed to [`load`][chanfig.FlatDict.load].\n **kwargs: Passed to [`load`][chanfig.FlatDict.load].\n Returns:\n self:\n Examples:\n >>> d = FlatDict(a=1, b=1)\n >>> d.merge_from_file(\"tests/test.yaml\").dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nreturn self.merge(self.load(file, *args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.open","title":"open(file, *args, encoding='utf-8', **kwargs)
staticmethod
","text":"Open file IO from file path or IO.
This methods extends the ability of built-in open
by allowing it to accept an IOBase
object.
Parameters:
Name Type Description Defaultfile
File
File path or IO.
required*args
Any
Additional arguments passed to open
. Defaults to ().
()
**kwargs
Any
Any Additional keyword arguments passed to open
. Defaults to {}.
{}
Yields:
Type DescriptionGenerator[IOBase | IO, Any, Any]
Examples:
Python Console Session>>> with FlatDict.open(\"tests/test.yaml\") as fp:\n... print(fp.read())\na: 1\nb: 2\nc: 3\n>>> io = open(\"tests/test.yaml\")\n>>> with FlatDict.open(io) as fp:\n... print(fp.read())\na: 1\nb: 2\nc: 3\n>>> with FlatDict.open(123, mode=\"w\") as fp:\n... print(fp.read())\nTraceback (most recent call last):\nTypeError: expected str, bytes, os.PathLike, IO or IOBase, not int\n
Source code in chanfig/flat_dict.py
Python@staticmethod\n@contextmanager\ndef open(file: File, *args: Any, encoding: str = \"utf-8\", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:\nr\"\"\"\n Open file IO from file path or IO.\n This methods extends the ability of built-in `open` by allowing it to accept an `IOBase` object.\n Args:\n file: File path or IO.\n *args: Additional arguments passed to `open`.\n Defaults to ().\n **kwargs: Any\n Additional keyword arguments passed to `open`.\n Defaults to {}.\n Yields:\n (Generator[IOBase | IO, Any, Any]):\n Examples:\n >>> with FlatDict.open(\"tests/test.yaml\") as fp:\n ... print(fp.read())\n a: 1\n b: 2\n c: 3\n <BLANKLINE>\n >>> io = open(\"tests/test.yaml\")\n >>> with FlatDict.open(io) as fp:\n ... print(fp.read())\n a: 1\n b: 2\n c: 3\n <BLANKLINE>\n >>> with FlatDict.open(123, mode=\"w\") as fp:\n ... print(fp.read())\n Traceback (most recent call last):\n TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int\n \"\"\"\nif isinstance(file, (IOBase, IO)):\nyield file\nelif isinstance(file, (PathLike, str, bytes)):\ntry:\nfile = open(file, *args, encoding=encoding, **kwargs) # type: ignore # noqa: SIM115\nyield file # type: ignore\nfinally:\nwith suppress(Exception):\nfile.close() # type: ignore\nelse:\nraise TypeError(f\"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.save","title":"save(file, method=None, *args, **kwargs)
","text":"Save FlatDict
to file.
Raises:
Type DescriptionValueError
If save to IO
and method
is not specified.
TypeError
If save to unsupported extension.
Alias:
save
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.save(\"tests/test.yaml\")\n>>> d.save(\"test.conf\")\nTraceback (most recent call last):\nTypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"test.yaml\", \"w\") as f:\n... d.save(f)\nTraceback (most recent call last):\nValueError: `method` must be specified when saving to IO.\n
Source code in chanfig/flat_dict.py
Pythondef save(self, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Save `FlatDict` to file.\n Raises:\n ValueError: If save to `IO` and `method` is not specified.\n TypeError: If save to unsupported extension.\n **Alias**:\n + `save`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.save(\"tests/test.yaml\")\n >>> d.save(\"test.conf\")\n Traceback (most recent call last):\n TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"test.yaml\", \"w\") as f:\n ... d.save(f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when saving to IO.\n \"\"\"\nif method is None:\nif isinstance(file, (IOBase, IO)):\nraise ValueError(\"`method` must be specified when saving to IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in YAML:\nreturn self.yaml(file=file, *args, **kwargs) # type: ignore\nif extension in JSON:\nreturn self.json(file=file, *args, **kwargs) # type: ignore\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.set","title":"set(name, value)
","text":"Set value of FlatDict
.
Parameters:
Name Type Description Defaultname
Any
required value
Any
required Examples:
Python Console Session>>> d = FlatDict()\n>>> d.set('d', 1013)\n>>> d.get('d')\n1013\n>>> d['n'] = 'chang'\n>>> d.n\n'chang'\n>>> d.n = 'liu'\n>>> d['n']\n'liu'\n
Source code in chanfig/flat_dict.py
Pythondef set(self, name: Any, value: Any) -> None:\nr\"\"\"\n Set value of `FlatDict`.\n Args:\n name:\n value:\n Examples:\n >>> d = FlatDict()\n >>> d.set('d', 1013)\n >>> d.get('d')\n 1013\n >>> d['n'] = 'chang'\n >>> d.n\n 'chang'\n >>> d.n = 'liu'\n >>> d['n']\n 'liu'\n \"\"\"\nif name in self and isinstance(self.get(name), Variable):\nself.get(name).set(value)\nelse:\ndict.__setitem__(self, name, value)\n
"},{"location":"flat_dict/#chanfig.FlatDict.setattr","title":"setattr(name, value)
","text":"Set attribute of FlatDict
.
Note that it won\u2019t alter values in FlatDict
.
Parameters:
Name Type Description Defaultname
str
required value
Any
required Warns:
Type DescriptionRuntimeWarning
If name already exists in FlatDict
.
Examples:
Python Console Session>>> d = FlatDict()\n>>> d.setattr('attr', 'value')\n>>> d.getattr('attr')\n'value'\n>>> d.set('d', 1013)\n>>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict.\n>>> d.get('d')\n1013\n>>> d.d\n1013\n>>> d.getattr('d')\n1031\n
Source code in chanfig/flat_dict.py
Pythondef setattr(self, name: str, value: Any) -> None:\nr\"\"\"\n Set attribute of `FlatDict`.\n Note that it won't alter values in `FlatDict`.\n Args:\n name:\n value:\n Warns:\n RuntimeWarning: If name already exists in `FlatDict`.\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('attr', 'value')\n >>> d.getattr('attr')\n 'value'\n >>> d.set('d', 1013)\n >>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict.\n >>> d.get('d')\n 1013\n >>> d.d\n 1013\n >>> d.getattr('d')\n 1031\n \"\"\"\nif name in self:\nwarn(\nf\"{name} already exists in {self.__class__.__name__}.\\n\"\nf\"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.\",\nRuntimeWarning,\n)\nself.__dict__[name] = value\n
"},{"location":"flat_dict/#chanfig.FlatDict.sort","title":"sort(key=None, reverse=False)
","text":"Sort FlatDict
.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.sort().dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> d = FlatDict(b=2, c=3, a=1)\n>>> d.sort().dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> a = [1]\n>>> d = FlatDict(z=0, a=a)\n>>> a.append(2)\n>>> d.sort().dict()\n{'a': [1, 2], 'z': 0}\n
Source code in chanfig/flat_dict.py
Pythondef sort(self, key: Callable | None = None, reverse: bool = False) -> FlatDict:\nr\"\"\"\n Sort `FlatDict`.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.sort().dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d = FlatDict(b=2, c=3, a=1)\n >>> d.sort().dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> a = [1]\n >>> d = FlatDict(z=0, a=a)\n >>> a.append(2)\n >>> d.sort().dict()\n {'a': [1, 2], 'z': 0}\n \"\"\"\nitems = sorted(self.items(), key=key, reverse=reverse)\nself.clear()\nfor k, v in items:\nself[k] = v\nreturn self\n
"},{"location":"flat_dict/#chanfig.FlatDict.to","title":"to(cls)
","text":"Convert values of FlatDict
to target cls
.
Parameters:
Name Type Description Defaultcls
str | device | dtype
required Returns:
Name Type Descriptionself
FlatDict
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.to(int)\nTraceback (most recent call last):\nTypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.\n
Source code in chanfig/flat_dict.py
Pythondef to(self, cls: str | TorchDevice | TorchDType) -> FlatDict: # pragma: no cover\nr\"\"\"\n Convert values of `FlatDict` to target `cls`.\n Args:\n cls (str | torch.device | torch.dtype):\n Returns:\n self:\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.to(int)\n Traceback (most recent call last):\n TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.\n \"\"\"\n# pylint: disable=C0103\nif isinstance(cls, (str, TorchDevice, TorchDType)):\nfor k, v in self.all_items():\nif hasattr(v, \"to\"):\nself[k] = v.to(cls)\nreturn self\nraise TypeError(f\"to() only support torch.dtype and torch.device, but got {cls}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.tpu","title":"tpu()
","text":"Move all tensors to tpu.
Returns:
Name Type Descriptionself
FlatDict
Alias:
xla
Examples:
Python Console Session>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.tpu().dict()\n{'a': tensor(1, device='xla:0')}\n>>> d.xla().dict() # alias\n{'a': tensor(1, device='xla:0')}\n
Source code in chanfig/flat_dict.py
Pythondef tpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to tpu.\n Returns:\n self:\n **Alias**:\n + `xla`\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.tpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='xla:0')}\n >>> d.xla().dict() # alias # doctest: +SKIP\n {'a': tensor(1, device='xla:0')}\n \"\"\"\nreturn self.to(TorchDevice(\"xla\"))\n
"},{"location":"flat_dict/#chanfig.FlatDict.union","title":"union(*args, **kwargs)
","text":"Alias of merge
.
chanfig/flat_dict.py
Pythondef union(self, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`merge`][chanfig.FlatDict.merge].\n \"\"\"\nreturn self.merge(*args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.validate","title":"validate()
","text":"Validate FlatDict
.
Raises:
Type DescriptionTypeError
If value is not of the type declared in class annotations.
TypeError
If Variable
has invalid type.
ValueError
If Variable
has invalid value.
Examples:
Python Console Session>>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.islower()))\n>>> d = FlatDict(d=Variable(1016, type=str), n=Variable('chang', validator=lambda x: x.islower()))\nTraceback (most recent call last):\nTypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n>>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.isupper()))\nTraceback (most recent call last):\nValueError: 'n' has invalid value. Value chang is not valid.\n
Source code in chanfig/flat_dict.py
Pythondef validate(self) -> None:\nr\"\"\"\n Validate `FlatDict`.\n Raises:\n TypeError: If value is not of the type declared in class annotations.\n TypeError: If `Variable` has invalid type.\n ValueError: If `Variable` has invalid value.\n Examples:\n >>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.islower()))\n >>> d = FlatDict(d=Variable(1016, type=str), n=Variable('chang', validator=lambda x: x.islower()))\n Traceback (most recent call last):\n TypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n >>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.isupper()))\n Traceback (most recent call last):\n ValueError: 'n' has invalid value. Value chang is not valid.\n \"\"\"\nself._validate(self)\n
"},{"location":"flat_dict/#chanfig.FlatDict.xla","title":"xla()
","text":"Alias of tpu
.
chanfig/flat_dict.py
Pythondef xla(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Alias of [`tpu`][chanfig.FlatDict.tpu].\n \"\"\"\nreturn self.tpu()\n
"},{"location":"flat_dict/#chanfig.FlatDict.yaml","title":"yaml(file, *args, **kwargs)
","text":"Dump FlatDict
to yaml file.
This method internally calls self.yamls()
to generate yaml string. You may overwrite yamls
in case something is not yaml serializable.
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.yaml(\"tests/test.yaml\")\n
Source code in chanfig/flat_dict.py
Pythondef yaml(self, file: File, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Dump `FlatDict` to yaml file.\n This method internally calls `self.yamls()` to generate yaml string.\n You may overwrite `yamls` in case something is not yaml serializable.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.yaml(\"tests/test.yaml\")\n \"\"\"\nwith self.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nself.yamls(fp, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.yamls","title":"yamls(*args, **kwargs)
","text":"Dump FlatDict
to yaml string.
Returns:
Type Descriptionstr
Examples:
Python Console Session>>> FlatDict(a=1, b=2, c=3).yamls()\n'a: 1\\nb: 2\\nc: 3\\n'\n
Source code in chanfig/flat_dict.py
Pythondef yamls(self, *args: Any, **kwargs: Any) -> str:\nr\"\"\"\n Dump `FlatDict` to yaml string.\n Returns:\n (str):\n Examples:\n >>> FlatDict(a=1, b=2, c=3).yamls()\n 'a: 1\\nb: 2\\nc: 3\\n'\n \"\"\"\nkwargs.setdefault(\"Dumper\", YamlDumper)\nkwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\nreturn yaml_dump(self.dict(), *args, **kwargs) # type: ignore\n
"},{"location":"functional/","title":"Functional","text":"Convert an object to a dict.
Note that when converting a set
object, it may be converted to a tuple
object if its values is not hashable.
Parameters:
Name Type Description Defaultobj
Any
Object to be converted.
requiredReturns:
Type DescriptionMapping[str, Any]
A dict.
Examples:
Python Console Session>>> to_dict(1)\n1\n>>> to_dict([1, 2, 3])\n[1, 2, 3]\n>>> to_dict((1, 2, 3))\n(1, 2, 3)\n>>> to_dict({1, 2, 3})\n{1, 2, 3}\n>>> to_dict({'a': 1, 'b': 2})\n{'a': 1, 'b': 2}\n>>> to_dict(Variable(1))\n1\n>>> to_dict(FlatDict(a=[[[[[FlatDict(b=1)]]]]]))\n{'a': [[[[[{'b': 1}]]]]]}\n>>> to_dict(FlatDict(a={FlatDict(b=1)}))\n{'a': ({'b': 1},)}\n
Source code in chanfig/flat_dict.py
Pythondef to_dict(obj: Any) -> Mapping[str, Any]: # pylint: disable=R0911\nr\"\"\"\n Convert an object to a dict.\n Note that when converting a `set` object, it may be converted to a `tuple` object if its values is not hashable.\n Args:\n obj: Object to be converted.\n Returns:\n A dict.\n Examples:\n >>> to_dict(1)\n 1\n >>> to_dict([1, 2, 3])\n [1, 2, 3]\n >>> to_dict((1, 2, 3))\n (1, 2, 3)\n >>> to_dict({1, 2, 3})\n {1, 2, 3}\n >>> to_dict({'a': 1, 'b': 2})\n {'a': 1, 'b': 2}\n >>> to_dict(Variable(1))\n 1\n >>> to_dict(FlatDict(a=[[[[[FlatDict(b=1)]]]]]))\n {'a': [[[[[{'b': 1}]]]]]}\n >>> to_dict(FlatDict(a={FlatDict(b=1)}))\n {'a': ({'b': 1},)}\n \"\"\"\nif isinstance(obj, Mapping):\nreturn {k: to_dict(v) for k, v in obj.items()}\nif isinstance(obj, list):\nreturn [to_dict(v) for v in obj] # type: ignore\nif isinstance(obj, tuple):\nreturn tuple(to_dict(v) for v in obj) # type: ignore\nif isinstance(obj, set):\ntry:\nreturn {to_dict(v) for v in obj} # type: ignore\nexcept TypeError:\nreturn tuple(to_dict(v) for v in obj) # type: ignore\nif isinstance(obj, Variable):\nreturn obj.value\nreturn obj\n
options: heading_level: 0
Save FlatDict
to file.
Raises:
Type DescriptionValueError
If save to IO
and method
is not specified.
TypeError
If save to unsupported extension.
Alias:
save
Examples:
Python Console Session>>> obj = {\"a\": 1, \"b\": 2, \"c\": 3}\n>>> save(obj, \"test.yaml\")\n>>> save(obj, \"test.json\")\n>>> save(obj, \"test.conf\")\nTraceback (most recent call last):\nTypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"test.yaml\", \"w\") as f:\n... save(obj, f)\nTraceback (most recent call last):\nValueError: `method` must be specified when saving to IO.\n
Source code in chanfig/functional.py
Pythondef save(obj, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Save `FlatDict` to file.\n Raises:\n ValueError: If save to `IO` and `method` is not specified.\n TypeError: If save to unsupported extension.\n **Alias**:\n + `save`\n Examples:\n >>> obj = {\"a\": 1, \"b\": 2, \"c\": 3}\n >>> save(obj, \"test.yaml\")\n >>> save(obj, \"test.json\")\n >>> save(obj, \"test.conf\")\n Traceback (most recent call last):\n TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"test.yaml\", \"w\") as f:\n ... save(obj, f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when saving to IO.\n \"\"\"\nif isinstance(obj, FlatDict):\nreturn obj.save(file, method, *args, **kwargs)\ndata = to_dict(obj)\nif method is None:\nif isinstance(file, IOBase):\nraise ValueError(\"`method` must be specified when saving to IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in YAML:\nwith FlatDict.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nyaml_dump(data, fp, *args, **kwargs)\nreturn\nif extension in JSON:\nwith FlatDict.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nfp.write(json_dumps(data, *args, **kwargs))\nreturn\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\") # type: ignore\n
options: heading_level: 0
Load a file into a NestedDict
.
This function simply calls NestedDict.load
.
Parameters:
Name Type Description Defaultfile
PathStr
The file to load.
required*args
Any
The arguments to pass to NestedDict.load
.
()
**kwargs
Any
The keyword arguments to pass to NestedDict.load
.
{}
See Also load
Examples:
Python Console Session>>> from chanfig import load\n>>> config = load(\"tests/test.yaml\")\n>>> config\nNestedDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n)\n
Source code in chanfig/functional.py
Pythondef load(file: PathStr, cls: type = NestedDict, *args: Any, **kwargs: Any) -> NestedDict: # pylint: disable=W1113\nr\"\"\"\n Load a file into a `NestedDict`.\n This function simply calls `NestedDict.load`.\n Args:\n file: The file to load.\n *args: The arguments to pass to `NestedDict.load`.\n **kwargs: The keyword arguments to pass to `NestedDict.load`.\n See Also:\n [`load`][chanfig.FlatDict.load]\n Examples:\n >>> from chanfig import load\n >>> config = load(\"tests/test.yaml\")\n >>> config\n NestedDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n \"\"\"\nreturn cls.load(file, *args, **kwargs) # type: ignore\n
options: heading_level: 0
Apply func
to all children of obj
.
Note that this method is meant for non-in-place modification of obj
and should return the original object.
Parameters:
Name Type Description Defaultobj
Any
Object to apply function.
requiredfunc
Callable
Function to be applied.
required*args
Any
Positional arguments to be passed to func
.
()
**kwargs
Any
Keyword arguments to be passed to func
.
{}
Returns:
Type DescriptionAny
Return value of func
.
apply_
: Apply an in-place operation.
chanfig/nested_dict.py
Pythondef apply(obj: Any, func: Callable, *args: Any, **kwargs: Any) -> Any:\nr\"\"\"\n Apply `func` to all children of `obj`.\n Note that this method is meant for non-in-place modification of `obj` and should return the original object.\n Args:\n obj: Object to apply function.\n func: Function to be applied.\n *args: Positional arguments to be passed to `func`.\n **kwargs: Keyword arguments to be passed to `func`.\n Returns:\n (Any): Return value of `func`.\n See Also:\n [`apply_`][chanfig.nested_dict.apply_]: Apply an in-place operation.\n \"\"\"\nif isinstance(obj, NestedDict):\nreturn obj.empty_like(**{k: apply(v, func, *args, **kwargs) for k, v in obj.items()})\nif isinstance(obj, Mapping):\nreturn {k: apply(v, func, *args, **kwargs) for k, v in obj.items()}\nif isinstance(obj, list):\nreturn [apply(v, func, *args, **kwargs) for v in obj]\nif isinstance(obj, tuple):\nreturn tuple(apply(v, func, *args, **kwargs) for v in obj)\nif isinstance(obj, set):\ntry:\nreturn {apply(v, func, *args, **kwargs) for v in obj}\nexcept TypeError:\ntuple(apply(v, func, *args, **kwargs) for v in obj)\nreturn func(*args, **kwargs) if ismethod(func) else func(obj, *args, **kwargs)\n
options: heading_level: 0
Apply func
to all children of obj
.
Note that this method is meant for non-in-place modification of obj
and should return a new object.
Parameters:
Name Type Description Defaultobj
Any
Object to apply function.
requiredfunc
Callable
Function to be applied.
required*args
Any
Positional arguments to be passed to func
.
()
**kwargs
Any
Keyword arguments to be passed to func
.
{}
Returns:
Type DescriptionAny
Return value of func
.
apply_
: Apply a non-in-place operation.
chanfig/nested_dict.py
Pythondef apply_(obj: Any, func: Callable, *args: Any, **kwargs: Any) -> Any:\nr\"\"\"\n Apply `func` to all children of `obj`.\n Note that this method is meant for non-in-place modification of `obj` and should return a new object.\n Args:\n obj: Object to apply function.\n func: Function to be applied.\n *args: Positional arguments to be passed to `func`.\n **kwargs: Keyword arguments to be passed to `func`.\n Returns:\n (Any): Return value of `func`.\n See Also:\n [`apply_`][chanfig.nested_dict.apply]: Apply a non-in-place operation.\n \"\"\"\nif isinstance(obj, Mapping):\nfor v in obj.values():\napply_(v, func, *args, **kwargs)\nif isinstance(obj, (list, tuple, set)):\nfor v in obj:\napply_(v, func, *args, **kwargs)\nreturn func(*args, **kwargs) if ismethod(func) else func(obj, *args, **kwargs)\n
options: heading_level: 0
"},{"location":"nested_dict/","title":"NestedDict","text":" Bases: DefaultDict
NestedDict
further extends DefaultDict
object by introducing a nested structure with delimiter
. By default, delimiter
is .
, but it could be modified in subclass or by calling dict.setattr('delimiter', D)
.
d = NestedDict({\"a.b.c\": 1})
is equivalent to d = NestedDict({\"a\": {\"b\": {\"c\": 1}}})
, and you can access members either by d[\"a.b.c\"]
or more simply by d.a.b.c
.
This behaviour allows you to pass keyword arguments to other functions as easy as func1(**d.func1)
.
Since NestedDict
inherits from DefaultDict
, it also supports default_factory
. With default_factory
, you can assign d.a.b.c = 1
without assign d.a = NestedDict()
in the first place. Note that the constructor of NestedDict
is different from DefaultDict
, default_factory
is not a positional argument, and must be set in a keyword argument.
NestedDict
also introduce all_keys
, all_values
, all_items
methods to get all keys, values, items respectively in the nested structure.
Attributes:
Name Type Descriptionconvert_mapping
bool
bool = False If True
, all new values with type of Mapping
will be converted to default_factory
. If default_factory
is Null
, will create an empty instance via self.empty
as default_factory
.
delimiter
str
str = \u201c.\u201d Delimiter for nested structure.
NotesWhen convert_mapping
specified, all new values with type of Mapping
will be converted to default_factory
. If default_factory
is Null
, will create an empty instance via self.empty
as default_factory
.
convert_mapping
is automatically applied to arguments during initialisation.
Examples:
Python Console Session>>> NestedDict({\"f.n\": \"chang\"})\nNestedDict(\n ('f'): NestedDict(\n ('n'): 'chang'\n )\n)\n>>> d = NestedDict({\"f.n\": \"chang\"}, default_factory=NestedDict)\n>>> d.i.d = 1013\n>>> d['i.d']\n1013\n>>> d.i.d\n1013\n>>> d.dict()\n{'f': {'n': 'chang'}, 'i': {'d': 1013}}\n
Source code in chanfig/nested_dict.py
Pythonclass NestedDict(DefaultDict): # type: ignore # pylint: disable=E1136\nr\"\"\"\n `NestedDict` further extends `DefaultDict` object by introducing a nested structure with `delimiter`.\n By default, `delimiter` is `.`, but it could be modified in subclass or by calling `dict.setattr('delimiter', D)`.\n `d = NestedDict({\"a.b.c\": 1})` is equivalent to `d = NestedDict({\"a\": {\"b\": {\"c\": 1}}})`,\n and you can access members either by `d[\"a.b.c\"]` or more simply by `d.a.b.c`.\n This behaviour allows you to pass keyword arguments to other functions as easy as `func1(**d.func1)`.\n Since `NestedDict` inherits from `DefaultDict`, it also supports `default_factory`.\n With `default_factory`, you can assign `d.a.b.c = 1` without assign `d.a = NestedDict()` in the first place.\n Note that the constructor of `NestedDict` is different from `DefaultDict`, `default_factory` is not a positional\n argument, and must be set in a keyword argument.\n `NestedDict` also introduce `all_keys`, `all_values`, `all_items` methods to get all keys, values, items\n respectively in the nested structure.\n Attributes:\n convert_mapping: bool = False\n If `True`, all new values with type of `Mapping` will be converted to `default_factory`.\n If `default_factory` is `Null`, will create an empty instance via `self.empty` as `default_factory`.\n delimiter: str = \".\"\n Delimiter for nested structure.\n Notes:\n When `convert_mapping` specified, all new values with type of `Mapping` will be converted to `default_factory`.\n If `default_factory` is `Null`, will create an empty instance via `self.empty` as `default_factory`.\n `convert_mapping` is automatically applied to arguments during initialisation.\n Examples:\n >>> NestedDict({\"f.n\": \"chang\"})\n NestedDict(\n ('f'): NestedDict(\n ('n'): 'chang'\n )\n )\n >>> d = NestedDict({\"f.n\": \"chang\"}, default_factory=NestedDict)\n >>> d.i.d = 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.dict()\n {'f': {'n': 'chang'}, 'i': {'d': 1013}}\n \"\"\"\nconvert_mapping: bool = False\ndelimiter: str = \".\"\nfallback: bool = False\ndef __init__(\nself,\n*args: Any,\ndefault_factory: Callable | None = None,\nconvert_mapping: bool | None = None,\nfallback: bool | None = None,\n**kwargs: Any,\n) -> None:\nsuper().__init__(default_factory)\nself.merge(*args, **kwargs)\nif convert_mapping is not None:\nself.setattr(\"convert_mapping\", convert_mapping)\nif fallback is not None:\nself.setattr(\"fallback\", fallback)\ndef all_keys(self) -> Generator:\nr\"\"\"\n Get all keys of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_keys())\n ['a', 'b.c', 'b.d']\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\n@wraps(self.all_keys)\ndef all_keys(self, prefix=\"\"):\nfor key, value in self.items():\nif prefix:\nkey = str(prefix) + str(delimiter) + str(key)\nif isinstance(value, NestedDict):\nyield from all_keys(value, key)\nelse:\nyield key\nreturn all_keys(self)\ndef all_values(self) -> Generator:\nr\"\"\"\n Get all values of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_values())\n [1, 2, 3]\n \"\"\"\nfor value in self.values():\nif isinstance(value, NestedDict):\nyield from value.all_values()\nelse:\nyield value\ndef all_items(self) -> Generator:\nr\"\"\"\n Get all items of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_items())\n [('a', 1), ('b.c', 2), ('b.d', 3)]\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\n@wraps(self.all_items)\ndef all_items(self, prefix=\"\"):\nfor key, value in self.items():\nif prefix:\nkey = str(prefix) + str(delimiter) + str(key)\nif isinstance(value, NestedDict):\nyield from all_items(value, key)\nelse:\nyield key, value\nreturn all_items(self)\ndef apply(self, func: Callable, *args: Any, **kwargs: Any) -> NestedDict:\nr\"\"\"\n Recursively apply a function to `NestedDict` and its children.\n Note:\n This method is meant for non-in-place modification of `obj`, for example, [`to`][chanfig.NestedDict.to].\n Args:\n func (Callable):\n See Also:\n [`apply_`][chanfig.NestedDict.apply_]: Apply an in-place operation.\n [`apply`][chanfig.nested_dict.apply]: Implementation of `apply`.\n tionples:\n >>> def func(d):\n ... if isinstance(d, NestedDict):\n ... d.t = 1\n >>> d = NestedDict()\n >>> d.a = NestedDict()\n >>> d.b = [NestedDict(),]\n >>> d.c = (NestedDict(),)\n >>> d.d = {NestedDict(),}\n >>> d.apply(func).dict()\n {'a': {}, 'b': [{}], 'c': ({},), 'd': ({},)}\n \"\"\"\nreturn apply(self, func, *args, **kwargs)\ndef apply_(self, func: Callable, *args: Any, **kwargs: Any) -> NestedDict:\nr\"\"\"\n Recursively apply a function to `NestedDict` and its children.\n Note:\n This method is meant for in-place modification of `obj`, for example, [`freeze`][chanfig.Config.freeze].\n Args:\n func (Callable):\n See Also:\n [`apply`][chanfig.NestedDict.apply]: Apply a non-in-place operation.\n [`apply_`][chanfig.nested_dict.apply_]: Implementation of `apply_` method.\n Examples:\n >>> def func(d):\n ... if isinstance(d, NestedDict):\n ... d.t = 1\n >>> d = NestedDict()\n >>> d.a = NestedDict()\n >>> d.b = [NestedDict(),]\n >>> d.c = (NestedDict(),)\n >>> d.d = {NestedDict(),}\n >>> d.apply_(func).dict()\n {'a': {'t': 1}, 'b': [{'t': 1}], 'c': ({'t': 1},), 'd': ({'t': 1},), 't': 1}\n \"\"\"\napply_(self, func, *args, **kwargs)\nreturn self\ndef get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\nr\"\"\"\n Get value from `NestedDict`.\n Note that `default` has higher priority than `default_factory`.\n Args:\n name:\n default:\n Returns:\n value:\n If `NestedDict` does not contain `name`, return `default`.\n If `default` is not specified, return `default_factory()`.\n Raises:\n KeyError: If `NestedDict` does not contain `name` and `default`/`default_factory` is not specified.\n TypeError: If `name` is not hashable.\n Examples:\n >>> d = NestedDict({\"i.d\": 1013}, default_factory=NestedDict)\n >>> d.get('i.d')\n 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.get('i.d', None)\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.get('a.b', None)\n >>> d.f\n NestedDict(<class 'chanfig.nested_dict.NestedDict'>, )\n >>> del d.f\n >>> d = NestedDict({\"i.d\": 1013})\n >>> d.e\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'e'\n >>> d.e = {}\n >>> d.get('e.f', Null)\n Traceback (most recent call last):\n KeyError: 'f'\n >>> d.get('e.f')\n >>> d.get('e.f', 1)\n 1\n >>> d.e.f\n Traceback (most recent call last):\n AttributeError: 'dict' object has no attribute 'f'\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\nif fallback is None:\nfallback = self.getattr(\"fallback\", False)\nfallback_name = name.split(delimiter)[-1] if isinstance(name, str) else name\nfallback_values = []\ntry:\nwhile isinstance(name, str) and delimiter in name:\nif fallback and fallback_name in self:\nfallback_values.append(self.get(fallback_name))\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (KeyError, AttributeError, TypeError):\nif fallback and fallback_values:\nreturn fallback_values[-1]\nif default is not Null:\nreturn default\nraise KeyError(name) from None\nif (fallback and fallback_values) and (not isinstance(self, Iterable) or name not in self):\nreturn fallback_values[-1]\n# if value is a python dict\nif not isinstance(self, NestedDict):\nif name not in self and default is not Null:\nreturn default\nreturn self[name]\nreturn super().get(name, default)\ndef set( # pylint: disable=W0221\nself,\nname: Any,\nvalue: Any,\nconvert_mapping: bool | None = None,\n) -> None:\nr\"\"\"\n Set value of `NestedDict`.\n Args:\n name:\n value:\n convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n Defaults to self.convert_mapping.\n Examples:\n >>> d = NestedDict(default_factory=NestedDict)\n >>> d.set('i.d', 1013)\n >>> d.get('i.d')\n 1013\n >>> d.dict()\n {'i': {'d': 1013}}\n >>> d['f.n'] = 'chang'\n >>> d.f.n\n 'chang'\n >>> d.n.l = 'liu'\n >>> d['n.l']\n 'liu'\n >>> d['f.n.e'] = \"error\"\n Traceback (most recent call last):\n ValueError: Cannot set `f.n.e` to `error`, as `f.n=chang`.\n >>> d['f.n.e.a'] = \"error\"\n Traceback (most recent call last):\n KeyError: 'e'\n >>> d.f.n.e.a = \"error\"\n Traceback (most recent call last):\n AttributeError: 'str' object has no attribute 'e'\n >>> d.setattr('convert_mapping', True)\n >>> d.a.b = {'c': {'d': 1}, 'e.f' : 2}\n >>> d.a.b.c.d\n 1\n >>> d['c.d'] = {'c': {'d': 1}, 'e.f' : 2}\n >>> d.c.d['e.f']\n 2\n >>> d.setattr('convert_mapping', False)\n >>> d.set('e.f', {'c': {'d': 1}, 'e.f' : 2}, convert_mapping=True)\n >>> d['e.f']['c.d']\n 1\n \"\"\"\n# pylint: disable=W0642\nfull_name = name\ndelimiter = self.getattr(\"delimiter\", \".\")\nif convert_mapping is None:\nconvert_mapping = self.getattr(\"convert_mapping\", False)\ndefault_factory = self.getattr(\"default_factory\", self.empty)\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nif name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\nself, name = getattr(self, name), rest\nelif name not in self and isinstance(self, Mapping):\ndefault = (\nself.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n)\nself, name = default, rest\nelse:\nself, name = self[name], rest\nif isinstance(self, NestedDict):\ndefault_factory = self.getattr(\"default_factory\", self.empty)\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\nif (\nconvert_mapping\nand isinstance(value, Mapping)\nand not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\nand not isinstance(value, Variable)\n):\ntry:\nvalue = default_factory(**value)\nexcept TypeError:\nvalue = default_factory(value)\nif isinstance(self, NestedDict):\nsuper().set(name, value)\nelif isinstance(self, Mapping):\ndict.__setitem__(self, name, value)\nelse:\nraise ValueError(\nf\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n)\ndef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `NestedDict`.\n Args:\n name:\n Examples:\n >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\"})\n >>> d.i.d\n 1013\n >>> d.f.n\n 'chang'\n >>> d.delete('i.d')\n >>> d.dict()\n {'i': {}, 'f': {'n': 'chang'}}\n >>> d.i.d\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'd'\n >>> del d.f.n\n >>> d.dict()\n {'i': {}, 'f': {}}\n >>> d.f.n\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'n'\n >>> del d.e\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'e'\n >>> del d['f.n']\n Traceback (most recent call last):\n KeyError: 'n'\n >>> d.e = {'a': {'b': 1}}\n >>> del d['e.a.b']\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\n# if value is a python dict\nif not isinstance(self, NestedDict):\ndel self[name]\nreturn\nsuper().delete(name)\ndef pop(self, name: Any, default: Any = Null) -> Any:\nr\"\"\"\n Pop value from `NestedDict`.\n Args:\n name:\n default:\n Returns:\n value: If `NestedDict` does not contain `name`, return `default`.\n Examples:\n >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1}, default_factory=NestedDict)\n >>> d.pop('i.d')\n 1013\n >>> d.pop('i.d', True)\n True\n >>> d.pop('i.d')\n Traceback (most recent call last):\n KeyError: 'd'\n >>> d.pop('e')\n Traceback (most recent call last):\n KeyError: 'e'\n >>> d.pop('e.f')\n Traceback (most recent call last):\n KeyError: 'f'\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\nif not isinstance(self, dict) or name not in self:\nif default is not Null:\nreturn default\nraise KeyError(name)\nreturn super().pop(name)\ndef validate(self) -> None:\nr\"\"\"\n Validate `NestedDict`.\n Raises:\n TypeError: If `Variable` has invalid type.\n ValueError: If `Variable` has invalid value.\n Examples:\n >>> d = NestedDict({\"i.d\": Variable(1016, type=int, validator=lambda x: x > 0)})\n >>> d = NestedDict({\"i.d\": Variable(1016, type=str, validator=lambda x: x > 0)})\n Traceback (most recent call last):\n TypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n >>> d = NestedDict({\"i.d\": Variable(-1, type=int, validator=lambda x: x > 0)})\n Traceback (most recent call last):\n ValueError: 'd' has invalid value. Value -1 is not valid.\n \"\"\"\nself.apply_(self._validate)\ndef sort(self, key: Callable | None = None, reverse: bool = False, recursive: bool = True) -> NestedDict:\nr\"\"\"\n Sort `NestedDict`.\n Args:\n recursive (bool): Whether to apply `sort` recursively.\n Returns:\n (NestedDict):\n Examples:\n >>> l = [1]\n >>> d = NestedDict({\"a\": 1, \"b\": {\"c\": 2, \"d\": 3}, \"b.e.f\": l})\n >>> d.sort().dict()\n {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n >>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n >>> d.sort().dict()\n {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n >>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n >>> d.sort(recursive=False).dict()\n {'a': 1, 'b': {'e': {'f': [1]}, 'd': 3, 'c': 2}}\n >>> l.append(2)\n >>> d.b.e.f\n [1, 2]\n \"\"\"\nif recursive:\nfor value in self.values():\nif isinstance(value, FlatDict):\nvalue.sort(key=key, reverse=reverse)\nreturn super().sort(key=key, reverse=reverse) # type: ignore\n@staticmethod\ndef _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:\nif not that:\nreturn this\nelif isinstance(that, Mapping):\nthat = that.items()\ncontext = this.converting() if isinstance(this, NestedDict) else nullcontext()\nwith context:\nfor key, value in that:\nif key in this and isinstance(this[key], Mapping):\nif isinstance(value, Mapping):\nNestedDict._merge(this[key], value, overwrite)\nelif overwrite:\nif isinstance(this, NestedDict):\nthis.set(key, value)\nelse:\nthis[key] = value\nelif key in dir(this) and isinstance(getattr(this.__class__, key), (property, cached_property)):\ngetattr(this, key).merge(value, overwrite=overwrite)\nelif overwrite or key not in this:\nif isinstance(this, NestedDict):\nthis.set(key, value)\nelse:\nthis[key] = value\nreturn this\ndef intersect( # pylint: disable=W0221\nself, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> NestedDict:\nr\"\"\"\n Intersection of `NestedDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n recursive (bool):\n Examples:\n >>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n >>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n >>> d.intersect(n).dict()\n {'c': {'d': {'e': 4, 'f': 5}}}\n >>> d.intersect(\"tests/test.yaml\").dict()\n {'a': 1}\n >>> d.intersect(n, recursive=False).dict()\n {}\n >>> l = [('a', 1), ('d', 4)]\n >>> d.intersect(l).dict()\n {'a': 1}\n >>> d.intersect(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(self._intersect(self, other, recursive)) # type: ignore\n@staticmethod\ndef _intersect(this: NestedDict, that: Iterable, recursive: bool = True) -> Mapping:\nret: NestedDict = NestedDict()\nfor key, value in that:\nif key in this:\nif isinstance(this[key], NestedDict) and isinstance(value, Mapping) and recursive:\nintersects = this[key].intersect(value)\nif intersects:\nret[key] = intersects\nelif this[key] == value:\nret[key] = value\nreturn ret\ndef difference( # pylint: disable=W0221, C0103\nself, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> NestedDict:\nr\"\"\"\n Difference between `NestedDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n recursive (bool):\n Examples:\n >>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n >>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n >>> d.difference(n).dict()\n {'b': {'c': 3, 'd': 5}, 'd': 0}\n >>> d.difference(\"tests/test.yaml\").dict()\n {'b': 2, 'c': 3}\n >>> d.difference(n, recursive=False).dict()\n {'b': {'c': 3, 'd': 5}, 'c': {'d': {'e': 4, 'f': 5}}, 'd': 0}\n >>> l = [('a', 1), ('d', 4)]\n >>> d.difference(l).dict()\n {'d': 4}\n >>> d.difference(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(self._difference(self, other, recursive)) # type: ignore\n@staticmethod\ndef _difference(this: NestedDict, that: Iterable, recursive: bool = True) -> Mapping:\nret: NestedDict = NestedDict()\nfor key, value in that:\nif key not in this:\nret[key] = value\nelif isinstance(this[key], NestedDict) and isinstance(value, Mapping) and recursive:\ndifferences = this[key].difference(value)\nif differences:\nret[key] = differences\nelif this[key] != value:\nret[key] = value\nreturn ret\n@contextmanager\ndef converting(self):\nconvert_mapping = self.getattr(\"convert_mapping\", False)\ntry:\nself.setattr(\"convert_mapping\", True)\nyield\nfinally:\nself.setattr(\"convert_mapping\", convert_mapping)\ndef __contains__(self, name: Any) -> bool: # type: ignore\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nif super().__contains__(name):\nself, name = self[name], rest # pylint: disable=W0642\nelse:\nreturn False\nreturn super().__contains__(name)\nexcept (TypeError, KeyError): # TypeError when name is not in self\nreturn False\n
"},{"location":"nested_dict/#chanfig.NestedDict.all_items","title":"all_items()
","text":"Get all items of NestedDict
.
Returns:
Type DescriptionGenerator
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n>>> list(d.all_items())\n[('a', 1), ('b.c', 2), ('b.d', 3)]\n
Source code in chanfig/nested_dict.py
Pythondef all_items(self) -> Generator:\nr\"\"\"\n Get all items of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_items())\n [('a', 1), ('b.c', 2), ('b.d', 3)]\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\n@wraps(self.all_items)\ndef all_items(self, prefix=\"\"):\nfor key, value in self.items():\nif prefix:\nkey = str(prefix) + str(delimiter) + str(key)\nif isinstance(value, NestedDict):\nyield from all_items(value, key)\nelse:\nyield key, value\nreturn all_items(self)\n
"},{"location":"nested_dict/#chanfig.NestedDict.all_keys","title":"all_keys()
","text":"Get all keys of NestedDict
.
Returns:
Type DescriptionGenerator
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n>>> list(d.all_keys())\n['a', 'b.c', 'b.d']\n
Source code in chanfig/nested_dict.py
Pythondef all_keys(self) -> Generator:\nr\"\"\"\n Get all keys of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_keys())\n ['a', 'b.c', 'b.d']\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\n@wraps(self.all_keys)\ndef all_keys(self, prefix=\"\"):\nfor key, value in self.items():\nif prefix:\nkey = str(prefix) + str(delimiter) + str(key)\nif isinstance(value, NestedDict):\nyield from all_keys(value, key)\nelse:\nyield key\nreturn all_keys(self)\n
"},{"location":"nested_dict/#chanfig.NestedDict.all_values","title":"all_values()
","text":"Get all values of NestedDict
.
Returns:
Type DescriptionGenerator
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n>>> list(d.all_values())\n[1, 2, 3]\n
Source code in chanfig/nested_dict.py
Pythondef all_values(self) -> Generator:\nr\"\"\"\n Get all values of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_values())\n [1, 2, 3]\n \"\"\"\nfor value in self.values():\nif isinstance(value, NestedDict):\nyield from value.all_values()\nelse:\nyield value\n
"},{"location":"nested_dict/#chanfig.NestedDict.apply","title":"apply(func, *args, **kwargs)
","text":"Recursively apply a function to NestedDict
and its children.
This method is meant for non-in-place modification of obj
, for example, to
.
Parameters:
Name Type Description Defaultfunc
Callable
required See Also apply_
: Apply an in-place operation. apply
: Implementation of apply
.
def func(d): \u2026 if isinstance(d, NestedDict): \u2026 d.t = 1 d = NestedDict() d.a = NestedDict() d.b = [NestedDict(),] d.c = (NestedDict(),) d.d = {NestedDict(),} d.apply(func).dict() {\u2018a\u2019: {}, \u2018b\u2019: [{}], \u2018c\u2019: ({},), \u2018d\u2019: ({},)}
Source code inchanfig/nested_dict.py
Pythondef apply(self, func: Callable, *args: Any, **kwargs: Any) -> NestedDict:\nr\"\"\"\n Recursively apply a function to `NestedDict` and its children.\n Note:\n This method is meant for non-in-place modification of `obj`, for example, [`to`][chanfig.NestedDict.to].\n Args:\n func (Callable):\n See Also:\n [`apply_`][chanfig.NestedDict.apply_]: Apply an in-place operation.\n [`apply`][chanfig.nested_dict.apply]: Implementation of `apply`.\n tionples:\n >>> def func(d):\n ... if isinstance(d, NestedDict):\n ... d.t = 1\n >>> d = NestedDict()\n >>> d.a = NestedDict()\n >>> d.b = [NestedDict(),]\n >>> d.c = (NestedDict(),)\n >>> d.d = {NestedDict(),}\n >>> d.apply(func).dict()\n {'a': {}, 'b': [{}], 'c': ({},), 'd': ({},)}\n \"\"\"\nreturn apply(self, func, *args, **kwargs)\n
"},{"location":"nested_dict/#chanfig.NestedDict.apply_","title":"apply_(func, *args, **kwargs)
","text":"Recursively apply a function to NestedDict
and its children.
This method is meant for in-place modification of obj
, for example, freeze
.
Parameters:
Name Type Description Defaultfunc
Callable
required See Also apply
: Apply a non-in-place operation. apply_
: Implementation of apply_
method.
Examples:
Python Console Session>>> def func(d):\n... if isinstance(d, NestedDict):\n... d.t = 1\n>>> d = NestedDict()\n>>> d.a = NestedDict()\n>>> d.b = [NestedDict(),]\n>>> d.c = (NestedDict(),)\n>>> d.d = {NestedDict(),}\n>>> d.apply_(func).dict()\n{'a': {'t': 1}, 'b': [{'t': 1}], 'c': ({'t': 1},), 'd': ({'t': 1},), 't': 1}\n
Source code in chanfig/nested_dict.py
Pythondef apply_(self, func: Callable, *args: Any, **kwargs: Any) -> NestedDict:\nr\"\"\"\n Recursively apply a function to `NestedDict` and its children.\n Note:\n This method is meant for in-place modification of `obj`, for example, [`freeze`][chanfig.Config.freeze].\n Args:\n func (Callable):\n See Also:\n [`apply`][chanfig.NestedDict.apply]: Apply a non-in-place operation.\n [`apply_`][chanfig.nested_dict.apply_]: Implementation of `apply_` method.\n Examples:\n >>> def func(d):\n ... if isinstance(d, NestedDict):\n ... d.t = 1\n >>> d = NestedDict()\n >>> d.a = NestedDict()\n >>> d.b = [NestedDict(),]\n >>> d.c = (NestedDict(),)\n >>> d.d = {NestedDict(),}\n >>> d.apply_(func).dict()\n {'a': {'t': 1}, 'b': [{'t': 1}], 'c': ({'t': 1},), 'd': ({'t': 1},), 't': 1}\n \"\"\"\napply_(self, func, *args, **kwargs)\nreturn self\n
"},{"location":"nested_dict/#chanfig.NestedDict.delete","title":"delete(name)
","text":"Delete value from NestedDict
.
Parameters:
Name Type Description Defaultname
Any
required Examples:
Python Console Session>>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\"})\n>>> d.i.d\n1013\n>>> d.f.n\n'chang'\n>>> d.delete('i.d')\n>>> d.dict()\n{'i': {}, 'f': {'n': 'chang'}}\n>>> d.i.d\nTraceback (most recent call last):\nAttributeError: 'NestedDict' object has no attribute 'd'\n>>> del d.f.n\n>>> d.dict()\n{'i': {}, 'f': {}}\n>>> d.f.n\nTraceback (most recent call last):\nAttributeError: 'NestedDict' object has no attribute 'n'\n>>> del d.e\nTraceback (most recent call last):\nAttributeError: 'NestedDict' object has no attribute 'e'\n>>> del d['f.n']\nTraceback (most recent call last):\nKeyError: 'n'\n>>> d.e = {'a': {'b': 1}}\n>>> del d['e.a.b']\n
Source code in chanfig/nested_dict.py
Pythondef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `NestedDict`.\n Args:\n name:\n Examples:\n >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\"})\n >>> d.i.d\n 1013\n >>> d.f.n\n 'chang'\n >>> d.delete('i.d')\n >>> d.dict()\n {'i': {}, 'f': {'n': 'chang'}}\n >>> d.i.d\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'd'\n >>> del d.f.n\n >>> d.dict()\n {'i': {}, 'f': {}}\n >>> d.f.n\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'n'\n >>> del d.e\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'e'\n >>> del d['f.n']\n Traceback (most recent call last):\n KeyError: 'n'\n >>> d.e = {'a': {'b': 1}}\n >>> del d['e.a.b']\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\n# if value is a python dict\nif not isinstance(self, NestedDict):\ndel self[name]\nreturn\nsuper().delete(name)\n
"},{"location":"nested_dict/#chanfig.NestedDict.difference","title":"difference(other, recursive=True)
","text":"Difference between NestedDict
and other
.
Parameters:
Name Type Description Defaultother
Mapping | Iterable | PathStr
required recursive
bool
True
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n>>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n>>> d.difference(n).dict()\n{'b': {'c': 3, 'd': 5}, 'd': 0}\n>>> d.difference(\"tests/test.yaml\").dict()\n{'b': 2, 'c': 3}\n>>> d.difference(n, recursive=False).dict()\n{'b': {'c': 3, 'd': 5}, 'c': {'d': {'e': 4, 'f': 5}}, 'd': 0}\n>>> l = [('a', 1), ('d', 4)]\n>>> d.difference(l).dict()\n{'d': 4}\n>>> d.difference(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n
Source code in chanfig/nested_dict.py
Pythondef difference( # pylint: disable=W0221, C0103\nself, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> NestedDict:\nr\"\"\"\n Difference between `NestedDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n recursive (bool):\n Examples:\n >>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n >>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n >>> d.difference(n).dict()\n {'b': {'c': 3, 'd': 5}, 'd': 0}\n >>> d.difference(\"tests/test.yaml\").dict()\n {'b': 2, 'c': 3}\n >>> d.difference(n, recursive=False).dict()\n {'b': {'c': 3, 'd': 5}, 'c': {'d': {'e': 4, 'f': 5}}, 'd': 0}\n >>> l = [('a', 1), ('d', 4)]\n >>> d.difference(l).dict()\n {'d': 4}\n >>> d.difference(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(self._difference(self, other, recursive)) # type: ignore\n
"},{"location":"nested_dict/#chanfig.NestedDict.get","title":"get(name, default=None, fallback=None)
","text":"Get value from NestedDict
.
Note that default
has higher priority than default_factory
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
None
Returns:
Name Type Descriptionvalue
Any
If NestedDict
does not contain name
, return default
. If default
is not specified, return default_factory()
.
Raises:
Type DescriptionKeyError
If NestedDict
does not contain name
and default
/default_factory
is not specified.
TypeError
If name
is not hashable.
Examples:
Python Console Session>>> d = NestedDict({\"i.d\": 1013}, default_factory=NestedDict)\n>>> d.get('i.d')\n1013\n>>> d['i.d']\n1013\n>>> d.i.d\n1013\n>>> d.get('i.d', None)\n1013\n>>> d.get('f', 2)\n2\n>>> d.get('a.b', None)\n>>> d.f\nNestedDict(<class 'chanfig.nested_dict.NestedDict'>, )\n>>> del d.f\n>>> d = NestedDict({\"i.d\": 1013})\n>>> d.e\nTraceback (most recent call last):\nAttributeError: 'NestedDict' object has no attribute 'e'\n>>> d.e = {}\n>>> d.get('e.f', Null)\nTraceback (most recent call last):\nKeyError: 'f'\n>>> d.get('e.f')\n>>> d.get('e.f', 1)\n1\n>>> d.e.f\nTraceback (most recent call last):\nAttributeError: 'dict' object has no attribute 'f'\n
Source code in chanfig/nested_dict.py
Pythondef get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\nr\"\"\"\n Get value from `NestedDict`.\n Note that `default` has higher priority than `default_factory`.\n Args:\n name:\n default:\n Returns:\n value:\n If `NestedDict` does not contain `name`, return `default`.\n If `default` is not specified, return `default_factory()`.\n Raises:\n KeyError: If `NestedDict` does not contain `name` and `default`/`default_factory` is not specified.\n TypeError: If `name` is not hashable.\n Examples:\n >>> d = NestedDict({\"i.d\": 1013}, default_factory=NestedDict)\n >>> d.get('i.d')\n 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.get('i.d', None)\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.get('a.b', None)\n >>> d.f\n NestedDict(<class 'chanfig.nested_dict.NestedDict'>, )\n >>> del d.f\n >>> d = NestedDict({\"i.d\": 1013})\n >>> d.e\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'e'\n >>> d.e = {}\n >>> d.get('e.f', Null)\n Traceback (most recent call last):\n KeyError: 'f'\n >>> d.get('e.f')\n >>> d.get('e.f', 1)\n 1\n >>> d.e.f\n Traceback (most recent call last):\n AttributeError: 'dict' object has no attribute 'f'\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\nif fallback is None:\nfallback = self.getattr(\"fallback\", False)\nfallback_name = name.split(delimiter)[-1] if isinstance(name, str) else name\nfallback_values = []\ntry:\nwhile isinstance(name, str) and delimiter in name:\nif fallback and fallback_name in self:\nfallback_values.append(self.get(fallback_name))\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (KeyError, AttributeError, TypeError):\nif fallback and fallback_values:\nreturn fallback_values[-1]\nif default is not Null:\nreturn default\nraise KeyError(name) from None\nif (fallback and fallback_values) and (not isinstance(self, Iterable) or name not in self):\nreturn fallback_values[-1]\n# if value is a python dict\nif not isinstance(self, NestedDict):\nif name not in self and default is not Null:\nreturn default\nreturn self[name]\nreturn super().get(name, default)\n
"},{"location":"nested_dict/#chanfig.NestedDict.intersect","title":"intersect(other, recursive=True)
","text":"Intersection of NestedDict
and other
.
Parameters:
Name Type Description Defaultother
Mapping | Iterable | PathStr
required recursive
bool
True
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n>>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n>>> d.intersect(n).dict()\n{'c': {'d': {'e': 4, 'f': 5}}}\n>>> d.intersect(\"tests/test.yaml\").dict()\n{'a': 1}\n>>> d.intersect(n, recursive=False).dict()\n{}\n>>> l = [('a', 1), ('d', 4)]\n>>> d.intersect(l).dict()\n{'a': 1}\n>>> d.intersect(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n
Source code in chanfig/nested_dict.py
Pythondef intersect( # pylint: disable=W0221\nself, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> NestedDict:\nr\"\"\"\n Intersection of `NestedDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n recursive (bool):\n Examples:\n >>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n >>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n >>> d.intersect(n).dict()\n {'c': {'d': {'e': 4, 'f': 5}}}\n >>> d.intersect(\"tests/test.yaml\").dict()\n {'a': 1}\n >>> d.intersect(n, recursive=False).dict()\n {}\n >>> l = [('a', 1), ('d', 4)]\n >>> d.intersect(l).dict()\n {'a': 1}\n >>> d.intersect(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(self._intersect(self, other, recursive)) # type: ignore\n
"},{"location":"nested_dict/#chanfig.NestedDict.pop","title":"pop(name, default=Null)
","text":"Pop value from NestedDict
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
Null
Returns:
Name Type Descriptionvalue
Any
If NestedDict
does not contain name
, return default
.
Examples:
Python Console Session>>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1}, default_factory=NestedDict)\n>>> d.pop('i.d')\n1013\n>>> d.pop('i.d', True)\nTrue\n>>> d.pop('i.d')\nTraceback (most recent call last):\nKeyError: 'd'\n>>> d.pop('e')\nTraceback (most recent call last):\nKeyError: 'e'\n>>> d.pop('e.f')\nTraceback (most recent call last):\nKeyError: 'f'\n
Source code in chanfig/nested_dict.py
Pythondef pop(self, name: Any, default: Any = Null) -> Any:\nr\"\"\"\n Pop value from `NestedDict`.\n Args:\n name:\n default:\n Returns:\n value: If `NestedDict` does not contain `name`, return `default`.\n Examples:\n >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1}, default_factory=NestedDict)\n >>> d.pop('i.d')\n 1013\n >>> d.pop('i.d', True)\n True\n >>> d.pop('i.d')\n Traceback (most recent call last):\n KeyError: 'd'\n >>> d.pop('e')\n Traceback (most recent call last):\n KeyError: 'e'\n >>> d.pop('e.f')\n Traceback (most recent call last):\n KeyError: 'f'\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\nif not isinstance(self, dict) or name not in self:\nif default is not Null:\nreturn default\nraise KeyError(name)\nreturn super().pop(name)\n
"},{"location":"nested_dict/#chanfig.NestedDict.set","title":"set(name, value, convert_mapping=None)
","text":"Set value of NestedDict
.
Parameters:
Name Type Description Defaultname
Any
required value
Any
required convert_mapping
bool | None
Whether to convert Mapping
to NestedDict
. Defaults to self.convert_mapping.
None
Examples:
Python Console Session>>> d = NestedDict(default_factory=NestedDict)\n>>> d.set('i.d', 1013)\n>>> d.get('i.d')\n1013\n>>> d.dict()\n{'i': {'d': 1013}}\n>>> d['f.n'] = 'chang'\n>>> d.f.n\n'chang'\n>>> d.n.l = 'liu'\n>>> d['n.l']\n'liu'\n>>> d['f.n.e'] = \"error\"\nTraceback (most recent call last):\nValueError: Cannot set `f.n.e` to `error`, as `f.n=chang`.\n>>> d['f.n.e.a'] = \"error\"\nTraceback (most recent call last):\nKeyError: 'e'\n>>> d.f.n.e.a = \"error\"\nTraceback (most recent call last):\nAttributeError: 'str' object has no attribute 'e'\n>>> d.setattr('convert_mapping', True)\n>>> d.a.b = {'c': {'d': 1}, 'e.f' : 2}\n>>> d.a.b.c.d\n1\n>>> d['c.d'] = {'c': {'d': 1}, 'e.f' : 2}\n>>> d.c.d['e.f']\n2\n>>> d.setattr('convert_mapping', False)\n>>> d.set('e.f', {'c': {'d': 1}, 'e.f' : 2}, convert_mapping=True)\n>>> d['e.f']['c.d']\n1\n
Source code in chanfig/nested_dict.py
Pythondef set( # pylint: disable=W0221\nself,\nname: Any,\nvalue: Any,\nconvert_mapping: bool | None = None,\n) -> None:\nr\"\"\"\n Set value of `NestedDict`.\n Args:\n name:\n value:\n convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n Defaults to self.convert_mapping.\n Examples:\n >>> d = NestedDict(default_factory=NestedDict)\n >>> d.set('i.d', 1013)\n >>> d.get('i.d')\n 1013\n >>> d.dict()\n {'i': {'d': 1013}}\n >>> d['f.n'] = 'chang'\n >>> d.f.n\n 'chang'\n >>> d.n.l = 'liu'\n >>> d['n.l']\n 'liu'\n >>> d['f.n.e'] = \"error\"\n Traceback (most recent call last):\n ValueError: Cannot set `f.n.e` to `error`, as `f.n=chang`.\n >>> d['f.n.e.a'] = \"error\"\n Traceback (most recent call last):\n KeyError: 'e'\n >>> d.f.n.e.a = \"error\"\n Traceback (most recent call last):\n AttributeError: 'str' object has no attribute 'e'\n >>> d.setattr('convert_mapping', True)\n >>> d.a.b = {'c': {'d': 1}, 'e.f' : 2}\n >>> d.a.b.c.d\n 1\n >>> d['c.d'] = {'c': {'d': 1}, 'e.f' : 2}\n >>> d.c.d['e.f']\n 2\n >>> d.setattr('convert_mapping', False)\n >>> d.set('e.f', {'c': {'d': 1}, 'e.f' : 2}, convert_mapping=True)\n >>> d['e.f']['c.d']\n 1\n \"\"\"\n# pylint: disable=W0642\nfull_name = name\ndelimiter = self.getattr(\"delimiter\", \".\")\nif convert_mapping is None:\nconvert_mapping = self.getattr(\"convert_mapping\", False)\ndefault_factory = self.getattr(\"default_factory\", self.empty)\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nif name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\nself, name = getattr(self, name), rest\nelif name not in self and isinstance(self, Mapping):\ndefault = (\nself.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n)\nself, name = default, rest\nelse:\nself, name = self[name], rest\nif isinstance(self, NestedDict):\ndefault_factory = self.getattr(\"default_factory\", self.empty)\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\nif (\nconvert_mapping\nand isinstance(value, Mapping)\nand not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\nand not isinstance(value, Variable)\n):\ntry:\nvalue = default_factory(**value)\nexcept TypeError:\nvalue = default_factory(value)\nif isinstance(self, NestedDict):\nsuper().set(name, value)\nelif isinstance(self, Mapping):\ndict.__setitem__(self, name, value)\nelse:\nraise ValueError(\nf\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n)\n
"},{"location":"nested_dict/#chanfig.NestedDict.sort","title":"sort(key=None, reverse=False, recursive=True)
","text":"Sort NestedDict
.
Parameters:
Name Type Description Defaultrecursive
bool
Whether to apply sort
recursively.
True
Returns:
Type DescriptionNestedDict
Examples:
Python Console Session>>> l = [1]\n>>> d = NestedDict({\"a\": 1, \"b\": {\"c\": 2, \"d\": 3}, \"b.e.f\": l})\n>>> d.sort().dict()\n{'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n>>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n>>> d.sort().dict()\n{'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n>>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n>>> d.sort(recursive=False).dict()\n{'a': 1, 'b': {'e': {'f': [1]}, 'd': 3, 'c': 2}}\n>>> l.append(2)\n>>> d.b.e.f\n[1, 2]\n
Source code in chanfig/nested_dict.py
Pythondef sort(self, key: Callable | None = None, reverse: bool = False, recursive: bool = True) -> NestedDict:\nr\"\"\"\n Sort `NestedDict`.\n Args:\n recursive (bool): Whether to apply `sort` recursively.\n Returns:\n (NestedDict):\n Examples:\n >>> l = [1]\n >>> d = NestedDict({\"a\": 1, \"b\": {\"c\": 2, \"d\": 3}, \"b.e.f\": l})\n >>> d.sort().dict()\n {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n >>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n >>> d.sort().dict()\n {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n >>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n >>> d.sort(recursive=False).dict()\n {'a': 1, 'b': {'e': {'f': [1]}, 'd': 3, 'c': 2}}\n >>> l.append(2)\n >>> d.b.e.f\n [1, 2]\n \"\"\"\nif recursive:\nfor value in self.values():\nif isinstance(value, FlatDict):\nvalue.sort(key=key, reverse=reverse)\nreturn super().sort(key=key, reverse=reverse) # type: ignore\n
"},{"location":"nested_dict/#chanfig.NestedDict.validate","title":"validate()
","text":"Validate NestedDict
.
Raises:
Type DescriptionTypeError
If Variable
has invalid type.
ValueError
If Variable
has invalid value.
Examples:
Python Console Session>>> d = NestedDict({\"i.d\": Variable(1016, type=int, validator=lambda x: x > 0)})\n>>> d = NestedDict({\"i.d\": Variable(1016, type=str, validator=lambda x: x > 0)})\nTraceback (most recent call last):\nTypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n>>> d = NestedDict({\"i.d\": Variable(-1, type=int, validator=lambda x: x > 0)})\nTraceback (most recent call last):\nValueError: 'd' has invalid value. Value -1 is not valid.\n
Source code in chanfig/nested_dict.py
Pythondef validate(self) -> None:\nr\"\"\"\n Validate `NestedDict`.\n Raises:\n TypeError: If `Variable` has invalid type.\n ValueError: If `Variable` has invalid value.\n Examples:\n >>> d = NestedDict({\"i.d\": Variable(1016, type=int, validator=lambda x: x > 0)})\n >>> d = NestedDict({\"i.d\": Variable(1016, type=str, validator=lambda x: x > 0)})\n Traceback (most recent call last):\n TypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n >>> d = NestedDict({\"i.d\": Variable(-1, type=int, validator=lambda x: x > 0)})\n Traceback (most recent call last):\n ValueError: 'd' has invalid value. Value -1 is not valid.\n \"\"\"\nself.apply_(self._validate)\n
"},{"location":"parser/","title":"ConfigParser","text":" Bases: ArgumentParser
Parser to parse command-line arguments for CHANfiG.
ConfigParser
is a subclass of argparse.ArgumentParser
. It provides a new parse
method to parse command-line arguments to CHANfiG.Config
object.
Different to ArgumentParser.parse_args
, ConfigParser.parse
will try to parse any command-line arguments, even if they are not pre-defined by ArgumentParser.add_argument
. This allows to relief the burden of adding tons of arguments for each tuneable parameter. In the meantime, there is no mechanism to notify you if you made a typo in command-line arguments.
Note that ArgumentParser.parse_args
method is not overridden in ConfigParser
. This is because it is still possible to construct CHANfiG.Config
with ArgumentParser.parse_args
, which has strict checking on command-line arguments.
chanfig/parser.py
Pythonclass ConfigParser(ArgumentParser): # pylint: disable=C0115\nr\"\"\"\n Parser to parse command-line arguments for CHANfiG.\n `ConfigParser` is a subclass of `argparse.ArgumentParser`.\n It provides a new `parse` method to parse command-line arguments to `CHANfiG.Config` object.\n Different to `ArgumentParser.parse_args`, `ConfigParser.parse` will try to parse any command-line arguments,\n even if they are not pre-defined by `ArgumentParser.add_argument`.\n This allows to relief the burden of adding tons of arguments for each tuneable parameter.\n In the meantime, there is no mechanism to notify you if you made a typo in command-line arguments.\n Note that `ArgumentParser.parse_args` method is not overridden in `ConfigParser`.\n This is because it is still possible to construct `CHANfiG.Config` with `ArgumentParser.parse_args`,\n which has strict checking on command-line arguments.\n \"\"\"\ndef __init__(self, *args: Any, **kwargs: Any):\nsuper().__init__(*args, **kwargs)\nself._registries[\"action\"][None] = StoreAction\nself._registries[\"action\"][\"store\"] = StoreAction\ndef parse_args( # type: ignore\nself, args: Sequence[str] | None = None, namespace: Namespace | None = None\n) -> NestedDict:\nparsed = super().parse_args(args, namespace)\nif isinstance(parsed, Namespace):\nparsed = vars(parsed) # type: ignore\nif not isinstance(parsed, NestedDict):\nparsed = NestedDict({key: value for key, value in parsed.items() if value is not Null}) # type: ignore\nfor key, value in parsed.all_items():\nwith suppress(TypeError, ValueError, SyntaxError):\nvalue = literal_eval(value)\nparsed[key] = value\nreturn parsed # type: ignore\ndef parse( # pylint: disable=R0912\nself,\nargs: Sequence[str] | None = None,\nconfig: Config | NestedDict | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\n) -> Config:\nr\"\"\"\n Parse the arguments for `Config`.\n You may optionally specify a name for `default_config`,\n and CHANfiG will read the file under this name.\n There are three levels of config:\n 1. The base `Config` parsed into this method,\n 2. The base config file located at the path of `default_config` (if specified),\n 3. The config specified in arguments.\n Higher levels override lower levels (i.e. 3 > 2 > 1).\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `Config`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n Returns:\n config: The parsed `Config`.\n Raises:\n ValueError: If `default_config` is specified but not found in args,\n and `no_default_config_action` is neither `warn` nor `ignore`.\n ValueError: If `no_default_config_action` is not in `raise`, `warn` and `ignore`.\n See Also:\n [`parse_config`][chanfig.ConfigParser.parse_config]: Only parse valid config arguments.\n Examples:\n Note that all examples uses NestedDict instead of Config for avoiding circular import.\n >>> p = ConfigParser()\n >>> p.parse(['--i.d', '1013', '--f.n', 'chang']).dict()\n {'i': {'d': 1013}, 'f': {'n': 'chang'}}\n Values in command line overrides values in `default_config` file.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2', '--config', 'tests/test.yaml'], default_config='config').dict()\n {'a': 2, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n Values in `default_config` file overrides values in `Config` object.\n >>> p = ConfigParser()\n >>> p.parse(['--config', 'tests/test.yaml'], config=NestedDict(a=2), default_config='config').dict()\n {'a': 1, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n ValueError will be raised when `default_config` is specified but not presented in command line.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config').dict()\n Traceback (most recent call last):\n RuntimeError: default_config is set to config, but not found in args.\n ValueError will be suppressed when `default_config` is specified bug not presented in command line,\n and `no_default_config_action` is set to `ignore` or `warn`.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config', no_default_config_action='ignore').dict()\n {'a': 2}\n ValueError will be raised when `no_default_config_action` is not in `raise`, `ignore`, and `warn`.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config', no_default_config_action='suppress').dict()\n Traceback (most recent call last):\n ValueError: no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got suppress\n \"\"\"\nif args is None:\nargs = sys.argv[1:]\nif config is None:\nfrom .config import Config # pylint: disable=C0415\nconfig = Config()\nelse:\nself.add_config_arguments(config)\nif no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\nraise ValueError(\nf\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n)\n# add the command-line arguments\nkey_value_args = []\nfor arg in args:\nif args == \"--\":\nbreak\nif arg.startswith(\"--\"):\nkey_value_args.append(arg.split(\"=\", maxsplit=1))\nelse:\nif not key_value_args:\ncontinue\nkey_value_args[-1].append(arg)\nfor key_value in key_value_args:\nif key_value[0] not in self:\nif len(key_value) > 2:\nself.add_argument(key_value[0], nargs=\"+\")\nelse:\nself.add_argument(key_value[0])\nparsed = self.parse_args(args)\n# parse the default config file\nif default_config is not None:\nparsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n# parse the command-line arguments\nreturn config.merge(parsed) # type: ignore\ndef parse_config( # pylint: disable=R0912\nself,\nargs: Sequence[str] | None = None,\nconfig: Config | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\n) -> Config:\nr\"\"\"\n Parse the arguments for `Config`.\n You may optionally specify a name for `default_config`,\n and CHANfiG will read the file under this name.\n There are three levels of config:\n 1. The base `Config` parsed into this method,\n 2. The base config file located at the path of `default_config` (if specified),\n 3. The config specified in arguments.\n Higher levels override lower levels (i.e. 3 > 2 > 1).\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `Config`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n Returns:\n config: The parsed `Config`.\n Raises:\n ValueError: If `default_config` is specified but not found in args,\n and `no_default_config_action` is neither `warn` nor `ignore`.\n ValueError: If `no_default_config_action` is not in `raise`, `warn` and `ignore`.\n See Also:\n [`parse`][chanfig.ConfigParser.parse]: Parse all command-line arguments.\n Examples:\n Note that all examples uses NestedDict instead of Config for avoiding circular import.\n >>> p = ConfigParser()\n >>> p.parse_config(['--a', '1'], config=NestedDict(a=2)).dict()\n {'a': 1}\n You can only parse argument that is defined in `Config`.\n error: unrecognized arguments: --b 1\n >>> p = ConfigParser()\n >>> p.parse_config(['--b', '1'], config=NestedDict(a=2)).dict() # doctest: +SKIP\n Traceback (most recent call last):\n SystemExit: 2\n \"\"\"\nif args is None:\nargs = sys.argv[1:]\nif config is None:\nraise ValueError(\"config must be specified\")\nself.add_config_arguments(config)\nif no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\nraise ValueError(\nf\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n)\nparsed = self.parse_args(args)\n# parse the default config file\nif default_config is not None:\nparsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n# parse the command-line arguments\nreturn config.merge(parsed) # type: ignore\ndef add_config_arguments(self, config):\nfor key, value in config.all_items():\nif isinstance(value, Variable):\ndtype = value._type or value.dtype # pylint: disable=W0212\nelif value is not None:\ndtype = type(value)\nelse:\ndtype = None\nname = \"--\" + key\nif name not in self:\nhelp = value._help if isinstance(value, Variable) else None # pylint: disable=W0212,W0622\nif isinstance(value, (list, tuple, dict, set)):\nself.add_argument(name, type=dtype, nargs=\"+\", help=help, dest=key)\nelse:\nself.add_argument(name, type=dtype, help=help, dest=key)\ndef merge_default_config(self, parsed, default_config: str, no_default_config_action: str = \"raise\") -> NestedDict:\nmessage = f\"default_config is set to {default_config}, but not found in args.\"\nif default_config in parsed:\npath = parsed[default_config]\nwarn(f\"Config has 'default_config={path}' specified, its values will override values in Config\")\nreturn NestedDict.load(path).merge(parsed) # type: ignore\nif no_default_config_action == \"ignore\":\npass\nelif no_default_config_action == \"warn\":\nwarn(message, category=RuntimeWarning, stacklevel=2)\nelse:\nraise RuntimeError(message)\nreturn parsed\n@staticmethod\ndef identity(string):\nr\"\"\"\n https://stackoverflow.com/questions/69896931/cant-pickle-local-object-argumentparser-init-locals-identity\n \"\"\"\nreturn string\ndef __contains__(self, name: str):\nif name in self._option_string_actions:\nreturn True\nreturn False\n
"},{"location":"parser/#chanfig.ConfigParser.identity","title":"identity(string)
staticmethod
","text":"Source code in chanfig/parser.py
Python@staticmethod\ndef identity(string):\nr\"\"\"\n https://stackoverflow.com/questions/69896931/cant-pickle-local-object-argumentparser-init-locals-identity\n \"\"\"\nreturn string\n
"},{"location":"parser/#chanfig.ConfigParser.parse","title":"parse(args=None, config=None, default_config=None, no_default_config_action='raise')
","text":"Parse the arguments for Config
.
You may optionally specify a name for default_config
, and CHANfiG will read the file under this name.
There are three levels of config:
Config
parsed into this method,default_config
(if specified),Higher levels override lower levels (i.e. 3 > 2 > 1).
Parameters:
Name Type Description Defaultargs
Iterable[str] | None
Command-line arguments. Defaults to None
.
None
default_config
str | None
Path to default config file. Defaults to Config
.
None
no_default_config_action
str
Action when default_config
is not found. Can be one of [\"raise\", \"warn\", \"ignore\"]
. Defaults to \"raise\"
.
'raise'
Returns:
Name Type Descriptionconfig
Config
The parsed Config
.
Raises:
Type DescriptionValueError
If default_config
is specified but not found in args, and no_default_config_action
is neither warn
nor ignore
.
ValueError
If no_default_config_action
is not in raise
, warn
and ignore
.
parse_config
: Only parse valid config arguments.
Examples:
Note that all examples uses NestedDict instead of Config for avoiding circular import.
Python Console Session>>> p = ConfigParser()\n>>> p.parse(['--i.d', '1013', '--f.n', 'chang']).dict()\n{'i': {'d': 1013}, 'f': {'n': 'chang'}}\n
Values in command line overrides values in default_config
file.
>>> p = ConfigParser()\n>>> p.parse(['--a', '2', '--config', 'tests/test.yaml'], default_config='config').dict()\n{'a': 2, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n
Values in default_config
file overrides values in Config
object.
>>> p = ConfigParser()\n>>> p.parse(['--config', 'tests/test.yaml'], config=NestedDict(a=2), default_config='config').dict()\n{'a': 1, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n
ValueError will be raised when default_config
is specified but not presented in command line.
>>> p = ConfigParser()\n>>> p.parse(['--a', '2'], default_config='config').dict()\nTraceback (most recent call last):\nRuntimeError: default_config is set to config, but not found in args.\n
ValueError will be suppressed when default_config
is specified bug not presented in command line, and no_default_config_action
is set to ignore
or warn
.
>>> p = ConfigParser()\n>>> p.parse(['--a', '2'], default_config='config', no_default_config_action='ignore').dict()\n{'a': 2}\n
ValueError will be raised when no_default_config_action
is not in raise
, ignore
, and warn
.
>>> p = ConfigParser()\n>>> p.parse(['--a', '2'], default_config='config', no_default_config_action='suppress').dict()\nTraceback (most recent call last):\nValueError: no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got suppress\n
Source code in chanfig/parser.py
Pythondef parse( # pylint: disable=R0912\nself,\nargs: Sequence[str] | None = None,\nconfig: Config | NestedDict | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\n) -> Config:\nr\"\"\"\n Parse the arguments for `Config`.\n You may optionally specify a name for `default_config`,\n and CHANfiG will read the file under this name.\n There are three levels of config:\n 1. The base `Config` parsed into this method,\n 2. The base config file located at the path of `default_config` (if specified),\n 3. The config specified in arguments.\n Higher levels override lower levels (i.e. 3 > 2 > 1).\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `Config`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n Returns:\n config: The parsed `Config`.\n Raises:\n ValueError: If `default_config` is specified but not found in args,\n and `no_default_config_action` is neither `warn` nor `ignore`.\n ValueError: If `no_default_config_action` is not in `raise`, `warn` and `ignore`.\n See Also:\n [`parse_config`][chanfig.ConfigParser.parse_config]: Only parse valid config arguments.\n Examples:\n Note that all examples uses NestedDict instead of Config for avoiding circular import.\n >>> p = ConfigParser()\n >>> p.parse(['--i.d', '1013', '--f.n', 'chang']).dict()\n {'i': {'d': 1013}, 'f': {'n': 'chang'}}\n Values in command line overrides values in `default_config` file.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2', '--config', 'tests/test.yaml'], default_config='config').dict()\n {'a': 2, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n Values in `default_config` file overrides values in `Config` object.\n >>> p = ConfigParser()\n >>> p.parse(['--config', 'tests/test.yaml'], config=NestedDict(a=2), default_config='config').dict()\n {'a': 1, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n ValueError will be raised when `default_config` is specified but not presented in command line.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config').dict()\n Traceback (most recent call last):\n RuntimeError: default_config is set to config, but not found in args.\n ValueError will be suppressed when `default_config` is specified bug not presented in command line,\n and `no_default_config_action` is set to `ignore` or `warn`.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config', no_default_config_action='ignore').dict()\n {'a': 2}\n ValueError will be raised when `no_default_config_action` is not in `raise`, `ignore`, and `warn`.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config', no_default_config_action='suppress').dict()\n Traceback (most recent call last):\n ValueError: no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got suppress\n \"\"\"\nif args is None:\nargs = sys.argv[1:]\nif config is None:\nfrom .config import Config # pylint: disable=C0415\nconfig = Config()\nelse:\nself.add_config_arguments(config)\nif no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\nraise ValueError(\nf\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n)\n# add the command-line arguments\nkey_value_args = []\nfor arg in args:\nif args == \"--\":\nbreak\nif arg.startswith(\"--\"):\nkey_value_args.append(arg.split(\"=\", maxsplit=1))\nelse:\nif not key_value_args:\ncontinue\nkey_value_args[-1].append(arg)\nfor key_value in key_value_args:\nif key_value[0] not in self:\nif len(key_value) > 2:\nself.add_argument(key_value[0], nargs=\"+\")\nelse:\nself.add_argument(key_value[0])\nparsed = self.parse_args(args)\n# parse the default config file\nif default_config is not None:\nparsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n# parse the command-line arguments\nreturn config.merge(parsed) # type: ignore\n
"},{"location":"parser/#chanfig.ConfigParser.parse_config","title":"parse_config(args=None, config=None, default_config=None, no_default_config_action='raise')
","text":"Parse the arguments for Config
.
You may optionally specify a name for default_config
, and CHANfiG will read the file under this name.
There are three levels of config:
Config
parsed into this method,default_config
(if specified),Higher levels override lower levels (i.e. 3 > 2 > 1).
Parameters:
Name Type Description Defaultargs
Iterable[str] | None
Command-line arguments. Defaults to None
.
None
default_config
str | None
Path to default config file. Defaults to Config
.
None
no_default_config_action
str
Action when default_config
is not found. Can be one of [\"raise\", \"warn\", \"ignore\"]
. Defaults to \"raise\"
.
'raise'
Returns:
Name Type Descriptionconfig
Config
The parsed Config
.
Raises:
Type DescriptionValueError
If default_config
is specified but not found in args, and no_default_config_action
is neither warn
nor ignore
.
ValueError
If no_default_config_action
is not in raise
, warn
and ignore
.
parse
: Parse all command-line arguments.
Examples:
Note that all examples uses NestedDict instead of Config for avoiding circular import.
Python Console Session>>> p = ConfigParser()\n>>> p.parse_config(['--a', '1'], config=NestedDict(a=2)).dict()\n{'a': 1}\n
You can only parse argument that is defined in Config
. error: unrecognized arguments: \u2013b 1
>>> p = ConfigParser()\n>>> p.parse_config(['--b', '1'], config=NestedDict(a=2)).dict()\nTraceback (most recent call last):\nSystemExit: 2\n
Source code in chanfig/parser.py
Pythondef parse_config( # pylint: disable=R0912\nself,\nargs: Sequence[str] | None = None,\nconfig: Config | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\n) -> Config:\nr\"\"\"\n Parse the arguments for `Config`.\n You may optionally specify a name for `default_config`,\n and CHANfiG will read the file under this name.\n There are three levels of config:\n 1. The base `Config` parsed into this method,\n 2. The base config file located at the path of `default_config` (if specified),\n 3. The config specified in arguments.\n Higher levels override lower levels (i.e. 3 > 2 > 1).\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `Config`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n Returns:\n config: The parsed `Config`.\n Raises:\n ValueError: If `default_config` is specified but not found in args,\n and `no_default_config_action` is neither `warn` nor `ignore`.\n ValueError: If `no_default_config_action` is not in `raise`, `warn` and `ignore`.\n See Also:\n [`parse`][chanfig.ConfigParser.parse]: Parse all command-line arguments.\n Examples:\n Note that all examples uses NestedDict instead of Config for avoiding circular import.\n >>> p = ConfigParser()\n >>> p.parse_config(['--a', '1'], config=NestedDict(a=2)).dict()\n {'a': 1}\n You can only parse argument that is defined in `Config`.\n error: unrecognized arguments: --b 1\n >>> p = ConfigParser()\n >>> p.parse_config(['--b', '1'], config=NestedDict(a=2)).dict() # doctest: +SKIP\n Traceback (most recent call last):\n SystemExit: 2\n \"\"\"\nif args is None:\nargs = sys.argv[1:]\nif config is None:\nraise ValueError(\"config must be specified\")\nself.add_config_arguments(config)\nif no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\nraise ValueError(\nf\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n)\nparsed = self.parse_args(args)\n# parse the default config file\nif default_config is not None:\nparsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n# parse the command-line arguments\nreturn config.merge(parsed) # type: ignore\n
"},{"location":"registry/","title":"Registry","text":" Bases: NestedDict
Registry
for components.
Registry provides 3 core functionalities:
To facilitate the usage scenario, registry
is designed to be a decorator. You could register a component by simply calling registry.register
, and it will be registered with its name. You may also specify the name of the component by calling registry.register(name=\"ComponentName\")
.
build
makes it easy to construct a component from a configuration. build
automatically determines the component to construct by the name
field in the configuration. So you could either call registry.build(config)
or registry.build(**config)
. Beyond this, build
is just a syntax sugar for registry.init(registry.lookup(name), *args, **kwargs)
.
lookup
is used to lookup for a component by its name. By default, lookup
internally calls NestedDict.get
, but you may override it to provide more functionalities.
init
is used to construct a component. By default, init
internally calls cls(*args, **kwargs)
, but you may override it to provide more functionalities.
Registry
inherits from NestedDict
.
Therefore, Registry
comes in a nested structure by nature. You could create a sub-registry by simply calling registry.sub_registry = Registry
, and access through registry.sub_registry.register()
.
Examples:
Python Console Session>>> registry = Registry()\n>>> @registry.register\n... @registry.register(\"Module1\")\n... class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> module = registry.register(Module, \"Module2\")\n>>> registry\nRegistry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n)\n>>> module = registry.register(Module, \"Module\")\nTraceback (most recent call last):\nValueError: Component with name Module already registered.\n>>> registry.lookup(\"Module\")\n<class 'chanfig.registry.Module'>\n>>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n>>> # registry.register(Module)\n>>> module = registry.build(config[\"module\"])\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a\n1\n>>> module.b\n2\n
Source code in chanfig/registry.py
Pythonclass Registry(NestedDict): # type: ignore\n\"\"\"\n `Registry` for components.\n Registry provides 3 core functionalities:\n - Register a new component.\n - Lookup for a component.\n - Build a component.\n To facilitate the usage scenario, `registry` is designed to be a decorator.\n You could register a component by simply calling `registry.register`, and it will be registered with its name.\n You may also specify the name of the component by calling `registry.register(name=\"ComponentName\")`.\n `build` makes it easy to construct a component from a configuration.\n `build` automatically determines the component to construct by the `name` field in the configuration.\n So you could either call `registry.build(config)` or `registry.build(**config)`.\n Beyond this, `build` is just a syntax sugar for `registry.init(registry.lookup(name), *args, **kwargs)`.\n `lookup` is used to lookup for a component by its name.\n By default, `lookup` internally calls `NestedDict.get`, but you may override it to provide more functionalities.\n `init` is used to construct a component.\n By default, `init` internally calls `cls(*args, **kwargs)`, but you may override it to provide more functionalities.\n Notes:\n `Registry` inherits from `NestedDict`.\n Therefore, `Registry` comes in a nested structure by nature.\n You could create a sub-registry by simply calling `registry.sub_registry = Registry`,\n and access through `registry.sub_registry.register()`.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... @registry.register(\"Module1\")\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> module = registry.register(Module, \"Module2\")\n >>> registry\n Registry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n )\n >>> module = registry.register(Module, \"Module\")\n Traceback (most recent call last):\n ValueError: Component with name Module already registered.\n >>> registry.lookup(\"Module\")\n <class 'chanfig.registry.Module'>\n >>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n >>> # registry.register(Module)\n >>> module = registry.build(config[\"module\"])\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n \"\"\"\noverride: bool = False\ndef __init__(self, override: bool | None = None, fallback: bool | None = None):\nsuper().__init__(fallback=fallback)\nif override is not None:\nself.setattr(\"override\", override)\ndef register(self, component: Any = None, name: Any | None = None) -> Callable:\nr\"\"\"\n Register a new component.\n Args:\n component: The component to register.\n name: The name of the component.\n Returns:\n component: The registered component.\n Registered component are expected to be `Callable`.\n Raises:\n ValueError: If the component with the same name already registered and `Registry.override=False`.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... @registry.register(\"Module1\")\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> module = registry.register(Module, \"Module2\")\n >>> registry\n Registry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n )\n \"\"\"\nif name in self and not self.override:\nraise ValueError(f\"Component with name {name} already registered.\")\n# Registry.register()\nif name is not None:\nself.set(name, component)\nreturn component # type: ignore\n# @Registry.register\nif callable(component) and name is None:\nself.set(component.__name__, component)\nreturn component\n# @Registry.register()\ndef decorator(name: Any | None = None):\n@wraps(self.register)\ndef wrapper(component):\nif name is None:\nself.set(component.__name__, component)\nelse:\nself.set(name, component)\nreturn component\nreturn wrapper\nreturn decorator(component)\ndef lookup(self, name: str) -> Any:\nr\"\"\"\n Lookup for a component.\n Args:\n name:\n Returns:\n (Any): The component.\n Raises:\n KeyError: If the component is not registered.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> registry.lookup(\"Module\")\n <class 'chanfig.registry.Module'>\n \"\"\"\nreturn self[name]\n@staticmethod\ndef init(cls: Callable, *args: Any, **kwargs: Any) -> Any: # type: ignore # pylint: disable=W0211\nr\"\"\"\n Constructor of component.\n Args:\n cls: The component to construct.\n *args: The arguments to pass to the component.\n **kwargs: The keyword arguments to pass to the component.\n Returns:\n (Any):\n Examples:\n >>> class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> kwargs = {\"a\": 1, \"b\": 2}\n >>> module = Registry.init(Module, **kwargs)\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n \"\"\"\nreturn cls(*args, **kwargs)\ndef build(self, name: str | Mapping, *args: Any, **kwargs: Any) -> Any:\nr\"\"\"\n Build a component.\n Args:\n name (str | Mapping):\n If its a `Mapping`, it must contain `\"name\"` as a member, the rest will be treated as `**kwargs`.\n Note that values in `kwargs` will override values in `name` if its a `Mapping`.\n *args: The arguments to pass to the component.\n **kwargs: The keyword arguments to pass to the component.\n Returns:\n (Any):\n Raises:\n KeyError: If the component is not registered.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n >>> # registry.register(Module)\n >>> module = registry.build(**config[\"module\"])\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n >>> module = registry.build(config[\"module\"], a=2)\n >>> module.a\n 2\n \"\"\"\nif isinstance(name, Mapping):\nname = deepcopy(name)\nname, kwargs = name.pop(\"name\"), dict(name, **kwargs) # type: ignore\nreturn self.init(self.lookup(name), *args, **kwargs) # type: ignore\n
"},{"location":"registry/#chanfig.Registry.build","title":"build(name, *args, **kwargs)
","text":"Build a component.
Parameters:
Name Type Description Defaultname
str | Mapping
If its a Mapping
, it must contain \"name\"
as a member, the rest will be treated as **kwargs
. Note that values in kwargs
will override values in name
if its a Mapping
.
*args
Any
The arguments to pass to the component.
()
**kwargs
Any
The keyword arguments to pass to the component.
{}
Returns:
Type DescriptionAny
Raises:
Type DescriptionKeyError
If the component is not registered.
Examples:
Python Console Session>>> registry = Registry()\n>>> @registry.register\n... class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n>>> # registry.register(Module)\n>>> module = registry.build(**config[\"module\"])\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a\n1\n>>> module.b\n2\n>>> module = registry.build(config[\"module\"], a=2)\n>>> module.a\n2\n
Source code in chanfig/registry.py
Pythondef build(self, name: str | Mapping, *args: Any, **kwargs: Any) -> Any:\nr\"\"\"\n Build a component.\n Args:\n name (str | Mapping):\n If its a `Mapping`, it must contain `\"name\"` as a member, the rest will be treated as `**kwargs`.\n Note that values in `kwargs` will override values in `name` if its a `Mapping`.\n *args: The arguments to pass to the component.\n **kwargs: The keyword arguments to pass to the component.\n Returns:\n (Any):\n Raises:\n KeyError: If the component is not registered.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n >>> # registry.register(Module)\n >>> module = registry.build(**config[\"module\"])\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n >>> module = registry.build(config[\"module\"], a=2)\n >>> module.a\n 2\n \"\"\"\nif isinstance(name, Mapping):\nname = deepcopy(name)\nname, kwargs = name.pop(\"name\"), dict(name, **kwargs) # type: ignore\nreturn self.init(self.lookup(name), *args, **kwargs) # type: ignore\n
"},{"location":"registry/#chanfig.Registry.init","title":"init(*args, **kwargs)
staticmethod
","text":"Constructor of component.
Parameters:
Name Type Description Defaultcls
Callable
The component to construct.
required*args
Any
The arguments to pass to the component.
()
**kwargs
Any
The keyword arguments to pass to the component.
{}
Returns:
Type DescriptionAny
Examples:
Python Console Session>>> class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> kwargs = {\"a\": 1, \"b\": 2}\n>>> module = Registry.init(Module, **kwargs)\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a\n1\n>>> module.b\n2\n
Source code in chanfig/registry.py
Python@staticmethod\ndef init(cls: Callable, *args: Any, **kwargs: Any) -> Any: # type: ignore # pylint: disable=W0211\nr\"\"\"\n Constructor of component.\n Args:\n cls: The component to construct.\n *args: The arguments to pass to the component.\n **kwargs: The keyword arguments to pass to the component.\n Returns:\n (Any):\n Examples:\n >>> class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> kwargs = {\"a\": 1, \"b\": 2}\n >>> module = Registry.init(Module, **kwargs)\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n \"\"\"\nreturn cls(*args, **kwargs)\n
"},{"location":"registry/#chanfig.Registry.lookup","title":"lookup(name)
","text":"Lookup for a component.
Parameters:
Name Type Description Defaultname
str
required Returns:
Type DescriptionAny
The component.
Raises:
Type DescriptionKeyError
If the component is not registered.
Examples:
Python Console Session>>> registry = Registry()\n>>> @registry.register\n... class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> registry.lookup(\"Module\")\n<class 'chanfig.registry.Module'>\n
Source code in chanfig/registry.py
Pythondef lookup(self, name: str) -> Any:\nr\"\"\"\n Lookup for a component.\n Args:\n name:\n Returns:\n (Any): The component.\n Raises:\n KeyError: If the component is not registered.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> registry.lookup(\"Module\")\n <class 'chanfig.registry.Module'>\n \"\"\"\nreturn self[name]\n
"},{"location":"registry/#chanfig.Registry.register","title":"register(component=None, name=None)
","text":"Register a new component.
Parameters:
Name Type Description Defaultcomponent
Any
The component to register.
None
name
Any | None
The name of the component.
None
Returns:
Name Type Descriptioncomponent
Callable
The registered component. Registered component are expected to be Callable
.
Raises:
Type DescriptionValueError
If the component with the same name already registered and Registry.override=False
.
Examples:
Python Console Session>>> registry = Registry()\n>>> @registry.register\n... @registry.register(\"Module1\")\n... class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> module = registry.register(Module, \"Module2\")\n>>> registry\nRegistry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n)\n
Source code in chanfig/registry.py
Pythondef register(self, component: Any = None, name: Any | None = None) -> Callable:\nr\"\"\"\n Register a new component.\n Args:\n component: The component to register.\n name: The name of the component.\n Returns:\n component: The registered component.\n Registered component are expected to be `Callable`.\n Raises:\n ValueError: If the component with the same name already registered and `Registry.override=False`.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... @registry.register(\"Module1\")\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> module = registry.register(Module, \"Module2\")\n >>> registry\n Registry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n )\n \"\"\"\nif name in self and not self.override:\nraise ValueError(f\"Component with name {name} already registered.\")\n# Registry.register()\nif name is not None:\nself.set(name, component)\nreturn component # type: ignore\n# @Registry.register\nif callable(component) and name is None:\nself.set(component.__name__, component)\nreturn component\n# @Registry.register()\ndef decorator(name: Any | None = None):\n@wraps(self.register)\ndef wrapper(component):\nif name is None:\nself.set(component.__name__, component)\nelse:\nself.set(name, component)\nreturn component\nreturn wrapper\nreturn decorator(component)\n
"},{"location":"utils/","title":"Utilities","text":" Bases: type
Metaclass for Singleton Classes.
Source code inchanfig/utils.py
Pythonclass Singleton(type):\nr\"\"\"\n Metaclass for Singleton Classes.\n \"\"\"\n__instances__: Mapping[type, object] = {}\ndef __call__(cls, *args: Any, **kwargs: Any):\nif cls not in cls.__instances__:\ncls.__instances__[cls] = super().__call__(*args, **kwargs) # type: ignore\nreturn cls.__instances__[cls]\n
options: heading_level: 0
"},{"location":"utils/#chanfigutilsnull","title":"chanfig.utils.Null","text":"Null
is an instance of NULL
.
Since the metaclass of NULL
is Singleton
, it is advised to use obj is Null
to determine if obj
is Null
.
NULL class.
get
method in CHANfiG may accept None
or Ellipse
(...
) as value of default
. Therefore, it is mandatory to have a different default value for default
.
Null
is an instance of NULL
and is recommended to be used as obj is Null
.
chanfig/utils.py
Pythonclass NULL(metaclass=Singleton):\nr\"\"\"\n NULL class.\n `get` method in CHANfiG may accept `None` or `Ellipse`(`...`) as value of `default`.\n Therefore, it is mandatory to have a different default value for `default`.\n `Null` is an instance of `NULL` and is recommended to be used as `obj is Null`.\n \"\"\"\ndef __repr__(self):\nreturn \"Null\"\ndef __nonzero__(self):\nreturn False\ndef __len__(self):\nreturn 0\ndef __call__(self, *args: Any, **kwargs: Any):\nreturn self\ndef __contains__(self, name):\nreturn False\ndef __iter__(self):\nreturn self\ndef __next__(self):\nraise StopIteration\ndef __getattr__(self, name):\nreturn self\ndef __getitem__(self, index):\nreturn self\n
options: heading_level: 0
Bases: JSONEncoder
JSON encoder for Config.
Source code inchanfig/utils.py
Pythonclass JsonEncoder(JSONEncoder):\nr\"\"\"\n JSON encoder for Config.\n \"\"\"\ndef default(self, o: Any) -> Any:\nif hasattr(o, \"__json__\"):\nreturn o.__json__()\nreturn super().default(o)\n
options: heading_level: 0
Bases: SafeDumper
YAML Dumper for Config.
Source code inchanfig/utils.py
Pythonclass YamlDumper(SafeDumper): # pylint: disable=R0903\nr\"\"\"\n YAML Dumper for Config.\n \"\"\"\ndef increase_indent(self, flow: bool = False, indentless: bool = False): # pylint: disable=W0235\nreturn super().increase_indent(flow, indentless)\n
options: heading_level: 0
Bases: SafeLoader
YAML Loader for Config.
Source code inchanfig/utils.py
Pythonclass YamlLoader(SafeLoader): # pylint: disable=R0901,R0903\nr\"\"\"\n YAML Loader for Config.\n \"\"\"\n
options: heading_level: 0
"},{"location":"variable/","title":"Variable","text":" Bases: Generic[V]
Mutable wrapper for immutable objects.
Parameters:
Name Type Description Defaultvalue
Any
The value to wrap.
Null
type
type | None
Desired type of the value.
None
choices
list | None
Possible values of the value.
None
validator
Callable | None
Callable
that validates the value.
None
required
bool
Whether the value is required.
False
help
str | None
Help message of the value.
None
Raises:
Type DescriptionRuntimeError
If required
is True
and value
is Null
.
TypeError
If type
is specified and value
is not an instance of type
.
ValueError
| If choices
is specified and value
is not in choices
. If validator
is specified and validator
returns False
.
Attributes:
Name Type Descriptionvalue
Any
The wrapped value.
dtype
type
The type of the wrapped value.
NotesVariable
by default wrap the instance type to type of the wrapped object. Therefore, isinstance(Variable(1), int)
will return True
.
To temporarily disable this behaviour, you can call context manager with Variable.unwrapped()
.
To permanently disable this behaviour, you can call Variable.unwrap()
.
Examples:
Python Console Session>>> v = Variable(1)\n>>> n = v\n>>> v, n\n(1, 1)\n>>> v += 1\n>>> v, n\n(2, 2)\n>>> v.value = 3\n>>> v, n\n(3, 3)\n>>> n.set(4)\n>>> v, n\n(4, 4)\n>>> n = 5\n>>> v, n\n(4, 5)\n>>> f'{v} < {n}'\n'4 < 5'\n>>> isinstance(v, int)\nTrue\n>>> type(v)\n<class 'chanfig.variable.Variable'>\n>>> v.dtype\n<class 'int'>\n>>> with v.unwrapped():\n... isinstance(v, int)\nFalse\n>>> v = Variable('hello')\n>>> f'{v}, world!'\n'hello, world!'\n>>> v += ', world!'\n>>> v\n'hello, world!'\n>>> \"hello\" in v\nTrue\n
Source code in chanfig/variable.py
Pythonclass Variable(Generic[V]): # pylint: disable=R0902\nr\"\"\"\n Mutable wrapper for immutable objects.\n Args:\n value: The value to wrap.\n type: Desired type of the value.\n choices: Possible values of the value.\n validator: `Callable` that validates the value.\n required: Whether the value is required.\n help: Help message of the value.\n Raises:\n RuntimeError: If `required` is `True` and `value` is `Null`.\n TypeError: If `type` is specified and `value` is not an instance of `type`.\n ValueError: |\n If `choices` is specified and `value` is not in `choices`.\n If `validator` is specified and `validator` returns `False`.\n Attributes:\n value: The wrapped value.\n dtype: The type of the wrapped value.\n Notes:\n `Variable` by default wrap the instance type to type of the wrapped object.\n Therefore, `isinstance(Variable(1), int)` will return `True`.\n To temporarily disable this behaviour, you can call context manager `with Variable.unwrapped()`.\n To permanently disable this behaviour, you can call `Variable.unwrap()`.\n Examples:\n >>> v = Variable(1)\n >>> n = v\n >>> v, n\n (1, 1)\n >>> v += 1\n >>> v, n\n (2, 2)\n >>> v.value = 3\n >>> v, n\n (3, 3)\n >>> n.set(4)\n >>> v, n\n (4, 4)\n >>> n = 5\n >>> v, n\n (4, 5)\n >>> f'{v} < {n}'\n '4 < 5'\n >>> isinstance(v, int)\n True\n >>> type(v)\n <class 'chanfig.variable.Variable'>\n >>> v.dtype\n <class 'int'>\n >>> with v.unwrapped():\n ... isinstance(v, int)\n False\n >>> v = Variable('hello')\n >>> f'{v}, world!'\n 'hello, world!'\n >>> v += ', world!'\n >>> v\n 'hello, world!'\n >>> \"hello\" in v\n True\n \"\"\"\nwrap_type: bool = True\n_storage: List[Any]\n_type: Optional[type] = None\n_choices: Optional[list] = None\n_validator: Optional[Callable] = None\n_required: bool = False\n_help: Optional[str] = None\ndef __init__( # pylint: disable=R0913\nself,\nvalue: Any = Null,\ntype: type | None = None, # pylint: disable=W0622\nchoices: list | None = None,\nvalidator: Callable | None = None,\nrequired: bool = False,\nhelp: str | None = None, # pylint: disable=W0622\n) -> None:\nself._storage = [value]\nself._type = type\nself._choices = choices\nself._validator = validator\nself._required = required\nself._help = help\n@property # type: ignore\ndef __class__(self) -> type:\nreturn self.value.__class__ if self.wrap_type else type(self)\n@property\ndef value(self) -> Any:\nr\"\"\"\n Fetch the object wrapped in `Variable`.\n \"\"\"\nreturn self._storage[0]\n@value.setter\ndef value(self, value) -> None:\nr\"\"\"\n Assign value to the object wrapped in `Variable`.\n \"\"\"\nself.validate(value)\nself._storage[0] = self._get_value(value)\n@property\ndef dtype(self) -> type:\nr\"\"\"\n Data type of the object wrapped in `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> type(id)\n <class 'chanfig.variable.Variable'>\n >>> id.dtype\n <class 'int'>\n >>> issubclass(id.dtype, int)\n True\n \"\"\"\nreturn self.value.__class__\n@property\ndef storage(self) -> list[Any]:\nr\"\"\"\n Storage of `Variable`.\n \"\"\"\nreturn self._storage\n@property\ndef type(self) -> type | None:\nreturn self._type\n@property\ndef choices(self) -> list | None:\nreturn self._choices\n@property\ndef validator(self) -> Callable | None:\nreturn self._validator\n@property\ndef required(self) -> bool:\nreturn self._required\n@property\ndef help(self) -> str:\nreturn self._help or \"\"\ndef validate(self, *args) -> None:\nr\"\"\"\n Validate if the value is valid.\n \"\"\"\nif len(args) == 0:\nvalue = self.value\nelif len(args) == 1:\nvalue = args[0]\nelse:\nraise ValueError(\"Too many arguments.\")\nif self._required and value is Null:\nraise RuntimeError(\"Value is required.\")\nif self._type is not None and not isinstance(value, self._type):\nraise TypeError(f\"Value {value} is not of type {self._type}.\")\nif self._choices is not None and value not in self._choices:\nraise ValueError(f\"Value {value} is not in choices {self._choices}.\")\nif self._validator is not None and not self._validator(value):\nraise ValueError(f\"Value {value} is not valid.\")\ndef get(self) -> Any:\nr\"\"\"\n Fetch the object wrapped in `Variable`.\n \"\"\"\nreturn self.value\ndef set(self, value) -> None:\nr\"\"\"\n Assign value to the object wrapped in `Variable`.\n `Variable.set` is extremely useful when you want to change the value without changing the reference.\n In `FlatDict.set`, all assignments of `Variable` calls `Variable.set` Internally.\n \"\"\"\nself.value = value\ndef __get__(self, obj, objtype=None):\nreturn self\ndef __set__(self, obj, value):\nself.value = value\ndef to(self, cls: Callable) -> Any: # pylint: disable=C0103\nr\"\"\"\n Convert the object wrapped in `Variable` to target `cls`.\n Args:\n cls: The type to convert to.\n Examples:\n >>> id = Variable(1013)\n >>> id.to(float)\n 1013.0\n >>> id.to(str)\n '1013.0'\n \"\"\"\nself.value = cls(self.value)\nreturn self\ndef int(self) -> int:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `int`.\n Examples:\n >>> id = Variable(1013.0)\n >>> id.int()\n 1013\n \"\"\"\nreturn self.to(int)\ndef float(self) -> float:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `float`.\n Examples:\n >>> id = Variable(1013)\n >>> id.float()\n 1013.0\n \"\"\"\nreturn self.to(float)\ndef str(self) -> str:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `float`.\n Examples:\n >>> id = Variable(1013)\n >>> id.str()\n '1013'\n \"\"\"\nreturn self.to(str)\ndef wrap(self) -> None:\nr\"\"\"\n Wrap the type of `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> id.unwrap()\n >>> isinstance(id, int)\n False\n >>> id.wrap()\n >>> isinstance(id, int)\n True\n \"\"\"\nself.wrap_type = True\ndef unwrap(self) -> None:\nr\"\"\"\n Unwrap the type of `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> id.unwrap()\n >>> isinstance(id, int)\n False\n \"\"\"\nself.wrap_type = False\n@contextmanager\ndef unwrapped(self):\nr\"\"\"\n Context manager which temporarily unwrap the `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> isinstance(id, int)\n True\n >>> with id.unwrapped():\n ... isinstance(id, int)\n False\n \"\"\"\nwrap_type = self.wrap_type\nself.wrap_type = False\ntry:\nyield self\nfinally:\nself.wrap_type = wrap_type\n@staticmethod\ndef _get_value(obj) -> Any:\nif isinstance(obj, Variable):\nreturn obj.value\nreturn obj\ndef __getattr__(self, attr) -> Any:\nreturn getattr(self.value, attr)\ndef __lt__(self, other) -> bool:\nreturn self.value < self._get_value(other)\ndef __le__(self, other) -> bool:\nreturn self.value <= self._get_value(other)\ndef __eq__(self, other) -> bool:\nreturn self.value == self._get_value(other)\ndef __ne__(self, other) -> bool:\nreturn self.value != self._get_value(other)\ndef __ge__(self, other) -> bool:\nreturn self.value >= self._get_value(other)\ndef __gt__(self, other) -> bool:\nreturn self.value > self._get_value(other)\ndef __index__(self):\nreturn self.value.__index__()\ndef __invert__(self):\nreturn ~self.value\ndef __abs__(self):\nreturn abs(self.value)\ndef __add__(self, other):\nreturn Variable(self.value + self._get_value(other))\ndef __radd__(self, other):\nreturn Variable(self._get_value(other) + self.value)\ndef __iadd__(self, other):\nself.value += self._get_value(other)\nreturn self\ndef __and__(self, other):\nreturn Variable(self.value & self._get_value(other))\ndef __rand__(self, other):\nreturn Variable(self._get_value(other) & self.value)\ndef __iand__(self, other):\nself.value &= self._get_value(other)\nreturn self\ndef __floordiv__(self, other):\nreturn Variable(self.value // self._get_value(other))\ndef __rfloordiv__(self, other):\nreturn Variable(self._get_value(other) // self.value)\ndef __ifloordiv__(self, other):\nself.value //= self._get_value(other)\nreturn self\ndef __mod__(self, other):\nreturn Variable(self.value % self._get_value(other))\ndef __rmod__(self, other):\nreturn Variable(self._get_value(other) % self.value)\ndef __imod__(self, other):\nself.value %= self._get_value(other)\nreturn self\ndef __mul__(self, other):\nreturn Variable(self.value * self._get_value(other))\ndef __rmul__(self, other):\nreturn Variable(self._get_value(other) * self.value)\ndef __imul__(self, other):\nself.value *= self._get_value(other)\nreturn self\ndef __matmul__(self, other):\nreturn Variable(self.value @ self._get_value(other))\ndef __rmatmul__(self, other):\nreturn Variable(self._get_value(other) @ self.value)\ndef __imatmul__(self, other):\nself.value @= self._get_value(other)\nreturn self\ndef __pow__(self, other):\nreturn Variable(self.value ** self._get_value(other))\ndef __rpow__(self, other):\nreturn Variable(self._get_value(other) ** self.value)\ndef __ipow__(self, other):\nself.value **= self._get_value(other)\nreturn self\ndef __truediv__(self, other):\nreturn Variable(self.value / self._get_value(other))\ndef __rtruediv__(self, other):\nreturn Variable(self._get_value(other) / self.value)\ndef __itruediv__(self, other):\nself.value /= self._get_value(other)\nreturn self\ndef __sub__(self, other):\nreturn Variable(self.value - self._get_value(other))\ndef __rsub__(self, other):\nreturn Variable(self._get_value(other) - self.value)\ndef __isub__(self, other):\nself.value -= self._get_value(other)\nreturn self\ndef __copy__(self):\nreturn Variable(self.value)\ndef __deepcopy__(self, memo: Mapping | None = None):\nreturn Variable(copy(self.value))\ndef __format__(self, format_spec):\nreturn self.value if isinstance(self, str) else format(self.value, format_spec)\ndef __iter__(self):\nreturn iter(self.value)\ndef __next__(self):\nreturn next(self.value)\ndef __hash__(self):\nreturn hash(self.value)\ndef __repr__(self):\nreturn repr(self.value)\ndef __str__(self):\nreturn self.value if isinstance(self, str) else str(self.value)\ndef __json__(self):\nreturn self.value\ndef __contains__(self, name):\nreturn name in self.value\n
"},{"location":"variable/#chanfig.Variable.dtype","title":"dtype: type
property
","text":"Data type of the object wrapped in Variable
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> type(id)\n<class 'chanfig.variable.Variable'>\n>>> id.dtype\n<class 'int'>\n>>> issubclass(id.dtype, int)\nTrue\n
"},{"location":"variable/#chanfig.Variable.storage","title":"storage: list[Any]
property
","text":"Storage of Variable
.
value: Any
property
writable
","text":"Fetch the object wrapped in Variable
.
float()
","text":"Convert the object wrapped in Variable
to python float
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> id.float()\n1013.0\n
Source code in chanfig/variable.py
Pythondef float(self) -> float:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `float`.\n Examples:\n >>> id = Variable(1013)\n >>> id.float()\n 1013.0\n \"\"\"\nreturn self.to(float)\n
"},{"location":"variable/#chanfig.Variable.get","title":"get()
","text":"Fetch the object wrapped in Variable
.
chanfig/variable.py
Pythondef get(self) -> Any:\nr\"\"\"\n Fetch the object wrapped in `Variable`.\n \"\"\"\nreturn self.value\n
"},{"location":"variable/#chanfig.Variable.int","title":"int()
","text":"Convert the object wrapped in Variable
to python int
.
Examples:
Python Console Session>>> id = Variable(1013.0)\n>>> id.int()\n1013\n
Source code in chanfig/variable.py
Pythondef int(self) -> int:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `int`.\n Examples:\n >>> id = Variable(1013.0)\n >>> id.int()\n 1013\n \"\"\"\nreturn self.to(int)\n
"},{"location":"variable/#chanfig.Variable.set","title":"set(value)
","text":"Assign value to the object wrapped in Variable
.
Variable.set
is extremely useful when you want to change the value without changing the reference.
In FlatDict.set
, all assignments of Variable
calls Variable.set
Internally.
chanfig/variable.py
Pythondef set(self, value) -> None:\nr\"\"\"\n Assign value to the object wrapped in `Variable`.\n `Variable.set` is extremely useful when you want to change the value without changing the reference.\n In `FlatDict.set`, all assignments of `Variable` calls `Variable.set` Internally.\n \"\"\"\nself.value = value\n
"},{"location":"variable/#chanfig.Variable.str","title":"str()
","text":"Convert the object wrapped in Variable
to python float
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> id.str()\n'1013'\n
Source code in chanfig/variable.py
Pythondef str(self) -> str:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `float`.\n Examples:\n >>> id = Variable(1013)\n >>> id.str()\n '1013'\n \"\"\"\nreturn self.to(str)\n
"},{"location":"variable/#chanfig.Variable.to","title":"to(cls)
","text":"Convert the object wrapped in Variable
to target cls
.
Parameters:
Name Type Description Defaultcls
Callable
The type to convert to.
requiredExamples:
Python Console Session>>> id = Variable(1013)\n>>> id.to(float)\n1013.0\n>>> id.to(str)\n'1013.0'\n
Source code in chanfig/variable.py
Pythondef to(self, cls: Callable) -> Any: # pylint: disable=C0103\nr\"\"\"\n Convert the object wrapped in `Variable` to target `cls`.\n Args:\n cls: The type to convert to.\n Examples:\n >>> id = Variable(1013)\n >>> id.to(float)\n 1013.0\n >>> id.to(str)\n '1013.0'\n \"\"\"\nself.value = cls(self.value)\nreturn self\n
"},{"location":"variable/#chanfig.Variable.unwrap","title":"unwrap()
","text":"Unwrap the type of Variable
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> id.unwrap()\n>>> isinstance(id, int)\nFalse\n
Source code in chanfig/variable.py
Pythondef unwrap(self) -> None:\nr\"\"\"\n Unwrap the type of `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> id.unwrap()\n >>> isinstance(id, int)\n False\n \"\"\"\nself.wrap_type = False\n
"},{"location":"variable/#chanfig.Variable.unwrapped","title":"unwrapped()
","text":"Context manager which temporarily unwrap the Variable
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> isinstance(id, int)\nTrue\n>>> with id.unwrapped():\n... isinstance(id, int)\nFalse\n
Source code in chanfig/variable.py
Python@contextmanager\ndef unwrapped(self):\nr\"\"\"\n Context manager which temporarily unwrap the `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> isinstance(id, int)\n True\n >>> with id.unwrapped():\n ... isinstance(id, int)\n False\n \"\"\"\nwrap_type = self.wrap_type\nself.wrap_type = False\ntry:\nyield self\nfinally:\nself.wrap_type = wrap_type\n
"},{"location":"variable/#chanfig.Variable.validate","title":"validate(*args)
","text":"Validate if the value is valid.
Source code inchanfig/variable.py
Pythondef validate(self, *args) -> None:\nr\"\"\"\n Validate if the value is valid.\n \"\"\"\nif len(args) == 0:\nvalue = self.value\nelif len(args) == 1:\nvalue = args[0]\nelse:\nraise ValueError(\"Too many arguments.\")\nif self._required and value is Null:\nraise RuntimeError(\"Value is required.\")\nif self._type is not None and not isinstance(value, self._type):\nraise TypeError(f\"Value {value} is not of type {self._type}.\")\nif self._choices is not None and value not in self._choices:\nraise ValueError(f\"Value {value} is not in choices {self._choices}.\")\nif self._validator is not None and not self._validator(value):\nraise ValueError(f\"Value {value} is not valid.\")\n
"},{"location":"variable/#chanfig.Variable.wrap","title":"wrap()
","text":"Wrap the type of Variable
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> id.unwrap()\n>>> isinstance(id, int)\nFalse\n>>> id.wrap()\n>>> isinstance(id, int)\nTrue\n
Source code in chanfig/variable.py
Pythondef wrap(self) -> None:\nr\"\"\"\n Wrap the type of `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> id.unwrap()\n >>> isinstance(id, int)\n False\n >>> id.wrap()\n >>> isinstance(id, int)\n True\n \"\"\"\nself.wrap_type = True\n
"},{"location":"blog/","title":"CHANfiG","text":""},{"location":"zh/#_1","title":"\u4ecb\u7ecd","text":"CHANfiG \u200b\u5e0c\u671b\u200b\u80fd\u200b\u8ba9\u200b\u4f60\u200b\u7684\u200b\u914d\u7f6e\u200b\u66f4\u52a0\u200b\u7b80\u5355\u200b\u3002
\u200b\u8bad\u7ec3\u200b\u4e00\u4e2a\u200b\u673a\u5668\u200b\u5b66\u4e60\u200b\u6a21\u578b\u200b\u6709\u200b\u65e0\u6570\u4e2a\u200b\u53ef\u200b\u8c03\u8282\u200b\u7684\u200b\u53c2\u6570\u200b\u3002 \u200b\u4e3a\u4e86\u200b\u8c03\u8282\u200b\u6240\u6709\u200b\u53c2\u6570\u200b\uff0c\u200b\u7814\u7a76\u5458\u200b\u4eec\u200b\u5e38\u5e38\u200b\u9700\u8981\u200b\u64b0\u5199\u200b\u5de8\u5927\u200b\u7684\u200b\u914d\u7f6e\u6587\u4ef6\u200b\uff0c\u200b\u6709\u65f6\u200b\u751a\u81f3\u200b\u957f\u200b\u8fbe\u200b\u6570\u5343\u200b\u884c\u200b\u3002 \u200b\u5927\u591a\u6570\u200b\u53c2\u6570\u200b\u53ea\u662f\u200b\u65b9\u6cd5\u200b\u9ed8\u8ba4\u200b\u53c2\u6570\u200b\u7684\u200b\u7b80\u5355\u200b\u91cd\u590d\u200b\uff0c\u200b\u8fd9\u200b\u5bfc\u81f4\u200b\u4e86\u200b\u5f88\u591a\u200b\u4e0d\u5fc5\u8981\u200b\u7684\u200b\u58f0\u660e\u200b\u3002 \u200b\u6b64\u5916\u200b\uff0c\u200b\u8c03\u8282\u200b\u8fd9\u4e9b\u200b\u53c2\u6570\u200b\u540c\u6837\u200b\u5f88\u200b\u7e41\u7410\u200b\uff0c\u200b\u9700\u8981\u200b\u5b9a\u4f4d\u200b\u5e76\u200b\u6253\u5f00\u200b\u914d\u7f6e\u6587\u4ef6\u200b\uff0c\u200b\u4f5c\u51fa\u200b\u4fee\u6539\u200b\uff0c\u200b\u7136\u540e\u200b\u4fdd\u5b58\u200b\u5173\u95ed\u200b\u3002 \u200b\u8fd9\u4e2a\u200b\u8fc7\u7a0b\u200b\u6d6a\u8d39\u200b\u4e86\u200b\u65e0\u6570\u200b\u7684\u200b\u5b9d\u8d35\u65f6\u95f4\u200b \u200b\u8fd9\u200b\u65e0\u7591\u200b\u662f\u200b\u4e00\u79cd\u200b\u72af\u7f6a\u200b \u3002 \u200b\u4f7f\u7528\u200bargparse
\u200b\u53ef\u4ee5\u200b\u5728\u200b\u4e00\u5b9a\u200b\u7a0b\u5ea6\u200b\u4e0a\u200b\u7f13\u89e3\u200b\u8c03\u53c2\u200b\u7684\u200b\u4e0d\u53d8\u200b\uff0c\u200b\u4f46\u662f\u200b\uff0c\u200b\u8981\u200b\u8ba9\u200b\u4ed6\u200b\u548c\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u9002\u914d\u200b\u4f9d\u7136\u200b\u9700\u8981\u200b\u5f88\u591a\u200b\u5de5\u4f5c\u200b\uff0c\u200b\u5e76\u4e14\u200b\u7f3a\u4e4f\u200b\u5d4c\u5957\u200b\u4e5f\u200b\u9650\u5236\u200b\u4e86\u200b\u4ed6\u200b\u7684\u200b\u6f5c\u529b\u200b\u3002
CHANfiG \u200b\u65e8\u5728\u200b\u5e26\u6765\u200b\u6539\u53d8\u200b\u3002
\u200b\u4f60\u200b\u53ea\u200b\u9700\u8981\u200b\u5728\u200b\u547d\u4ee4\u884c\u200b\u4e2d\u200b\u8f93\u5165\u200b\u4f60\u200b\u7684\u200b\u4fee\u6539\u200b\uff0c\u200b\u7136\u540e\u200b\u628a\u200b\u5269\u4e0b\u200b\u7684\u200b\u4ea4\u7ed9\u200b CHANfiG\u3002
CHANfiG \u200b\u5f88\u5927\u200b\u7a0b\u5ea6\u200b\u4e0a\u200b\u542f\u53d1\u200b\u81ea\u200bYACS\u3002 \u200b\u4e0d\u540c\u4e8e\u200b YACS \u200b\u7684\u200b\u8303\u5f0f\u200b\uff08\u200b\u4ee3\u7801\u200b + \u200b\u5b9e\u9a8c\u200bE\u200b\u7684\u200bYACS\u200b\u914d\u7f6e\u6587\u4ef6\u200b (+ \u200b\u5916\u90e8\u200b\u4f9d\u8d56\u200b + \u200b\u786c\u4ef6\u200b + \u200b\u5176\u4ed6\u200b\u4ee4\u4eba\u8ba8\u538c\u200b\u7684\u200b\u672f\u8bed\u200b ...) = \u200b\u53ef\u200b\u91cd\u590d\u200b\u7684\u200b\u5b9e\u9a8c\u200bE
\uff09\uff0c CHANfiG \u200b\u7684\u200b\u8303\u5f0f\u200b\u662f\u200b\uff1a
\u200b\u4ee3\u7801\u200b + \u200b\u547d\u4ee4\u884c\u200b\u53c2\u6570\u200b (+ \u200b\u53ef\u9009\u200b\u7684\u200bCHANfiG\u200b\u914d\u7f6e\u6587\u4ef6\u200b + \u200b\u5916\u90e8\u200b\u4f9d\u8d56\u200b + \u200b\u786c\u4ef6\u200b + \u200b\u5176\u4ed6\u200b\u4ee4\u4eba\u8ba8\u538c\u200b\u7684\u200b\u672f\u8bed\u200b ...) = \u200b\u53ef\u200b\u91cd\u590d\u200b\u7684\u200b\u5b9e\u9a8c\u200bE (+ \u200b\u53ef\u9009\u200b\u7684\u200bCHANfiG\u200b\u914d\u7f6e\u6587\u4ef6\u200b)
\u200b\u4e00\u4e2a\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u53ef\u4ee5\u200b\u88ab\u200b\u770b\u4f5c\u200b\u4e00\u4e2a\u200b\u5d4c\u5957\u200b\u7684\u200b\u5b57\u5178\u200b\u7ed3\u6784\u200b\u3002
\u200b\u4f46\u662f\u200b\uff0c\u200b\u9ed8\u8ba4\u200b\u7684\u200b Python \u200b\u5b57\u5178\u200b\u5341\u5206\u200b\u96be\u4ee5\u200b\u64cd\u4f5c\u200b\u3002
\u200b\u8bbf\u95ee\u200b\u5b57\u5178\u200b\u6210\u5458\u200b\u7684\u200b\u552f\u4e00\u200b\u65b9\u5f0f\u200b\u662f\u200bdict['name']
\uff0c\u200b\u8fd9\u200b\u65e0\u7591\u200b\u662f\u200b\u6781\u5176\u200b\u7e41\u7410\u200b\u7684\u200b\u3002 \u200b\u66f4\u200b\u7cdf\u7cd5\u200b\u7684\u200b\uff0c\u200b\u5982\u679c\u200b\u8fd9\u4e2a\u200b\u5b57\u5178\u200b\u548c\u200b\u914d\u7f6e\u200b\u4e00\u6837\u200b\u662f\u200b\u4e00\u4e2a\u200b\u5d4c\u5957\u200b\u7ed3\u6784\u200b\uff0c\u200b\u8bbf\u95ee\u200b\u6210\u5458\u200b\u5c06\u4f1a\u200b\u53d8\u6210\u200b\u7c7b\u4f3c\u200b\u4e8e\u200bdict['parent']['children']['name']
\u200b\u7684\u200b\u6837\u5b50\u200b\u3002
\u200b\u591f\u200b\u4e86\u200b\u5c31\u662f\u200b\u591f\u200b\u4e86\u200b\uff0c\u200b\u662f\u200b\u65f6\u5019\u200b\u505a\u51fa\u200b\u6539\u53d8\u200b\u4e86\u200b\u3002
\u200b\u6211\u4eec\u200b\u9700\u8981\u200b\u5c5e\u6027\u200b\u65b9\u5f0f\u200b\u7684\u200b\u8bbf\u95ee\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6211\u4eec\u200b\u73b0\u5728\u200b\u5c31\u200b\u9700\u8981\u200b\u3002 dict.name
\u200b\u548c\u200bdict.parent.children.name
\u200b\u662f\u200b\u6240\u6709\u200b\u4f60\u200b\u9700\u8981\u200b\u7684\u200b\u3002
\u200b\u5c3d\u7ba1\u200b\u6b64\u524d\u200b\u5df2\u7ecf\u200b\u6709\u200b\u5de5\u4f5c\u200b\u6765\u200b\u5b9e\u73b0\u200b\u7c7b\u4f3c\u200b\u7684\u200b\u5bf9\u200b\u5b57\u5178\u200b\u6210\u5458\u200b\u7684\u200b\u5c5e\u6027\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u3002\u200b\u4f46\u662f\u200b\u4ed6\u4eec\u200b\u7684\u200b Config \u200b\u5bf9\u8c61\u200b\u8981\u4e48\u200b\u4f7f\u7528\u200b\u4e00\u4e2a\u200b\u72ec\u7acb\u200b\u7684\u200b\u5b57\u5178\u200b\u6765\u200b\u5b58\u50a8\u200b\u5c5e\u6027\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u7684\u200b\u4fe1\u606f\u200b\uff08EasyDict\uff09\uff0c\u200b\u800c\u200b\u8fd9\u200b\u53ef\u80fd\u200b\u5bfc\u81f4\u200b\u5c5e\u6027\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u548c\u200b\u5b57\u5178\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u7684\u200b\u4e0d\u200b\u4e00\u81f4\u200b\uff1b\u200b\u8981\u4e48\u200b\u91cd\u65b0\u200b\u4f7f\u7528\u200b\u65e2\u6709\u200b\u7684\u200b__dict__
\u200b\u7136\u540e\u200b\u5bf9\u200b\u5b57\u5178\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u8fdb\u884c\u200b\u91cd\u5b9a\u5411\u200b\uff08ml_collections\uff09\uff0c\u200b\u800c\u200b\u8fd9\u200b\u53ef\u80fd\u200b\u5bfc\u81f4\u200b\u5c5e\u6027\u200b\u548c\u200b\u5b57\u5178\u200b\u6210\u5458\u200b\u5b58\u5728\u200b\u51b2\u7a81\u200b\u3002
\u200b\u4e3a\u4e86\u200b\u89e3\u51b3\u200b\u4e0a\u8ff0\u200b\u9650\u5236\u200b\uff0c\u200b\u6211\u4eec\u200b\u7ee7\u627f\u200b\u4e86\u200b Python \u200b\u5185\u7f6e\u200b\u7684\u200bdict
\u200b\u6765\u200b\u521b\u5efa\u200bFlatDict
\u3001DefaultDict
\u3001NestedDict
\u3001Config
\u200b\u548c\u200bRegistry
\u3002 \u200b\u6211\u4eec\u200b\u540c\u65f6\u200b\u4ecb\u7ecd\u200b\u4e86\u200bVariable
\u200b\u6765\u200b\u5728\u200b\u591a\u4e2a\u200b\u4f4d\u7f6e\u200b\u5171\u4eab\u200b\u503c\u200b\uff0c\u200b\u548c\u200bConfigParser
\u200b\u6765\u200b\u89e3\u6790\u200b\u547d\u4ee4\u884c\u200b\u53c2\u6570\u200b\u3002
FlatDict
\u200b\u5728\u200b\u4e09\u4e2a\u200b\u65b9\u9762\u200b\u5bf9\u200b\u9ed8\u8ba4\u200b\u7684\u200bdict
\u200b\u505a\u51fa\u200b\u6539\u8fdb\u200b\u3002
FlatDict
\u200b\u652f\u6301\u200b\u53d8\u91cf\u200b\u63d2\u503c\u200b\u3002 \u200b\u5c06\u200b\u4e00\u4e2a\u200b\u6210\u5458\u200b\u7684\u200b\u503c\u200b\u8bbe\u7f6e\u200b\u4e3a\u200b${}
\u200b\u5305\u88f9\u200b\u7684\u200b\u53e6\u200b\u4e00\u4e2a\u200b\u6210\u5458\u200b\u540d\u200b\uff0c\u200b\u7136\u540e\u200b\u8c03\u7528\u200binterpolate
\u200b\u65b9\u6cd5\u200b\u3002 \u200b\u8fd9\u4e2a\u200b\u6210\u5458\u200b\u7684\u200b\u503c\u200b\u5c06\u200b\u4f1a\u200b\u81ea\u52a8\u200b\u66ff\u6362\u200b\u4e3a\u200b\u53e6\u200b\u4e00\u4e2a\u200b\u6210\u5458\u200b\u7684\u200b\u503c\u200b\u3002
Python \u200b\u7684\u200bdict
\u200b\u81ea\u200b Python 3.7 \u200b\u4e4b\u540e\u200b\u5c31\u662f\u200b\u6709\u5e8f\u200b\u7684\u200b\uff0c\u200b\u4f46\u662f\u200b\u5e76\u200b\u6ca1\u6709\u200b\u4e00\u4e2a\u200b\u5185\u7f6e\u200b\u7684\u200b\u65b9\u6cd5\u200b\u6765\u200b\u5e2e\u52a9\u200b\u4f60\u200b\u5bf9\u200b\u4e00\u4e2a\u200bdict
\u200b\u8fdb\u884c\u200b\u6392\u5e8f\u200b\u3002FlatDict
\u200b\u652f\u6301\u200bsort
\u200b\u6765\u200b\u5e2e\u52a9\u200b\u4f60\u200b\u7ba1\u7406\u200b\u4f60\u200b\u7684\u200b\u5b57\u5178\u200b\u3002
FlatDict
\u200b\u5305\u62ec\u200b\u4e86\u200b\u4e00\u4e2a\u200bmerge
\u200b\u65b9\u6cd5\u200b\uff0c\u200b\u4ed6\u200b\u4f7f\u200b\u4f60\u200b\u80fd\u200b\u5c06\u200b\u4e00\u4e2a\u200bMapping
\u3001Iterable
\u200b\u6216\u8005\u200b\u4e00\u4e2a\u200b\u8def\u5f84\u200b\u5408\u5e76\u200b\u8fdb\u5165\u200b\u4e00\u4e2a\u200bFlatDict
\u3002 \u200b\u4e0e\u200bupdate
\u200b\u65b9\u6cd5\u200b\u4e0d\u540c\u200b\uff0cmerge
\u200b\u65b9\u6cd5\u200b\u662f\u200b\u8d4b\u503c\u200b\u800c\u200b\u4e0d\u662f\u200b\u66ff\u6362\u200b\uff0c\u200b\u8fd9\u200b\u4f7f\u5f97\u200b\u4ed6\u200b\u80fd\u200b\u66f4\u597d\u200b\u7684\u200b\u4e0e\u200bDefaultDict
\u200b\u914d\u5408\u200b\u4f7f\u7528\u200b\u3002
\u200b\u6b64\u5916\u200b\uff0cFlatDict
\u200b\u5f15\u5165\u200b\u4e86\u200bdifference
\u200b\u548c\u200bintersect
\uff0c\u200b\u8fd9\u4e9b\u200b\u4f7f\u200b\u5176\u200b\u53ef\u4ee5\u200b\u975e\u5e38\u7b80\u5355\u200b\u7684\u200b\u5c06\u200bFlatDict
\u200b\u548c\u200b\u5176\u4ed6\u200bMapping
\u3001Iterable
\u200b\u6216\u8005\u200b\u4e00\u4e2a\u200b\u8def\u5f84\u200b\u8fdb\u884c\u200b\u5bf9\u6bd4\u200b\u3002
FlatDict
\u200b\u652f\u6301\u200b\u4e0e\u200b Pytorch Tensor \u200b\u7c7b\u4f3c\u200b\u7684\u200bto
\u200b\u65b9\u6cd5\u200b\u3002 \u200b\u4f60\u200b\u53ef\u4ee5\u200b\u5f88\u200b\u7b80\u5355\u200b\u7684\u200b\u901a\u8fc7\u200b\u76f8\u540c\u200b\u7684\u200b\u65b9\u5f0f\u200b\u5c06\u200b\u6240\u6709\u200bFlatDict
\u200b\u7684\u200b\u6210\u5458\u200b\u503c\u200b\u8f6c\u6362\u200b\u4e3a\u200b\u67d0\u79cd\u200b\u7c7b\u578b\u200b\u6216\u8005\u200b\u8f6c\u79fb\u200b\u5230\u200b\u67d0\u4e2a\u200b\u8bbe\u5907\u200b\u4e0a\u200b\u3002
FlatDict
\u200b\u540c\u65f6\u200b\u96c6\u6210\u200b\u4e86\u200bcpu
\u3001gpu
(cuda
)\u3001tpu
(xla
)\u200b\u65b9\u6cd5\u200b\u6765\u200b\u63d0\u4f9b\u200b\u66f4\u200b\u4fbf\u6377\u200b\u7684\u200b\u8bbf\u95ee\u200b\u3002
FlatDict
\u200b\u652f\u6301\u200bjson
\u3001jsons
\u3001yaml
\u200b\u548c\u200byamls
\u200b\u65b9\u6cd5\u200b\u6765\u200b\u5c06\u200bFlatDict
\u200b\u5b58\u50a8\u200b\u5230\u200b\u6587\u4ef6\u200b\u6216\u8005\u200b\u8f6c\u6362\u6210\u200b\u5b57\u7b26\u4e32\u200b\u3002 \u200b\u5b83\u200b\u8fd8\u200b\u63d0\u4f9b\u200b\u4e86\u200bfrom_json
\u3001from_jsons
\u3001from_yaml
\u200b\u548c\u200bfrom_yamls
\u200b\u6765\u200b\u4ece\u200b\u4e00\u4e2a\u200b\u5b57\u7b26\u4e32\u200b\u6216\u8005\u200b\u6587\u4ef6\u200b\u4e2d\u200b\u6784\u5efa\u200bFlatDict
\u3002
FlatDict
\u200b\u4e5f\u200b\u5305\u62ec\u200b\u4e86\u200bdump
\u200b\u548c\u200bload
\u200b\u65b9\u6cd5\u200b\uff0c\u200b\u4ed6\u4eec\u200b\u53ef\u4ee5\u200b\u4ece\u6587\u4ef6\u200b\u6269\u5c55\u540d\u200b\u4e2d\u200b\u81ea\u52a8\u200b\u63a8\u65ad\u200b\u7c7b\u578b\u200b\u7136\u540e\u200b\u5c06\u200bFlatDict
\u200b\u5b58\u50a8\u200b\u5230\u200b\u6587\u4ef6\u200b\u4e2d\u200b/\u200b\u4ece\u6587\u4ef6\u200b\u4e2d\u200b\u52a0\u8f7d\u200bFlatDict
\u3002
\u200b\u4e3a\u4e86\u200b\u6ee1\u8db3\u200b\u9ed8\u8ba4\u503c\u200b\u7684\u200b\u9700\u8981\u200b\uff0c\u200b\u6211\u4eec\u200b\u5305\u62ec\u200b\u4e86\u200b\u4e00\u4e2a\u200bDefaultDict
\uff0c\u200b\u4ed6\u200b\u63a5\u53d7\u200bdefault_factory
\u200b\u53c2\u6570\u200b\uff0c\u200b\u5e76\u200b\u548c\u200bcollections.defaultdict
\u200b\u4e00\u6837\u200b\u5de5\u4f5c\u200b\u3002
\u200b\u7531\u4e8e\u200b\u5927\u591a\u6570\u200b\u914d\u7f6e\u200b\u90fd\u200b\u662f\u200b\u4e00\u4e2a\u200b\u5d4c\u5957\u200b\u7684\u200b\u7ed3\u6784\u200b\uff0c\u200b\u6211\u4eec\u200b\u8fdb\u4e00\u6b65\u200b\u63d0\u51fa\u200b\u4e86\u200bNestedDict
\u3002
\u200b\u57fa\u4e8e\u200bDefaultDict
\uff0cNestedDict
\u200b\u63d0\u4f9b\u200b\u4e86\u200ball_keys
\u3001all_values
\u3001all_items
\u200b\u65b9\u6cd5\u200b\u6765\u200b\u5141\u8bb8\u200b\u4e00\u6b21\u6027\u200b\u904d\u5386\u200b\u6574\u4e2a\u200b\u5d4c\u5957\u200b\u7ed3\u6784\u200b\u3002
NestedDict
\u200b\u540c\u65f6\u200b\u63d0\u4f9b\u200b\u4e86\u200bapply
\u200b\u548c\u200bapply_
\u200b\u65b9\u6cd5\u200b\uff0c\u200b\u5b83\u200b\u53ef\u4ee5\u200b\u4f7f\u200b\u64cd\u7eb5\u200b\u5d4c\u5957\u200b\u7ed3\u6784\u200b\u66f4\u52a0\u200b\u5bb9\u6613\u200b\u3002
Config
\u200b\u901a\u8fc7\u200b\u4e24\u4e2a\u200b\u65b9\u9762\u200b\u6765\u200b\u8fdb\u4e00\u6b65\u200b\u63d0\u5347\u200b\u529f\u80fd\u6027\u200b\uff1a \u200b\u652f\u6301\u200bfreeze
\u200b\u6765\u200b\u51bb\u7ed3\u200b\u548c\u200bdefrost
\u200b\u89e3\u51bb\u200b\u5b57\u5178\u200b\u548c\u200b \u200b\u52a0\u5165\u200b\u5185\u7f6e\u200b\u7684\u200bConfigParser
\u200b\u6765\u200b\u89e3\u6790\u200b\u547d\u4ee4\u884c\u200b\u8bed\u53e5\u200b\u3002
\u200b\u6ce8\u610f\u200bConfig
\u200b\u9ed8\u8ba4\u8bbe\u7f6e\u200bdefault_factory=Config()
\u200b\u6765\u200b\u63d0\u4f9b\u200b\u4fbf\u5229\u200b\u3002
Registry
\u200b\u7ee7\u627f\u200b\u81ea\u200bNestedDict
\uff0c\u200b\u5e76\u4e14\u200b\u63d0\u4f9b\u200bregister
\u3001lookup
\u200b\u548c\u200bbuild
\u200b\u6765\u200b\u5e2e\u52a9\u200b\u4f60\u200b\u6ce8\u518c\u200b\u6784\u9020\u51fd\u6570\u200b\u5e76\u200b\u4ece\u200bConfig
\u200b\u6765\u200b\u521b\u5efa\u5bf9\u8c61\u200b\u3002
\u200b\u6709\u200b\u4e00\u4e2a\u200b\u503c\u200b\u5728\u200b\u591a\u4e2a\u200b\u5730\u65b9\u200b\u4ee5\u200b\u591a\u4e2a\u200b\u540d\u5b57\u200b\u51fa\u73b0\u200b\uff1f\u200b\u6211\u4eec\u200b\u7ed9\u200b\u4f60\u200b\u63d0\u4f9b\u200b\u63a9\u62a4\u200b\u3002
\u200b\u53ea\u8981\u200b\u5c06\u503c\u200b\u4ee5\u200bVariable
\u200b\u5305\u88c5\u200b\uff0c\u200b\u7136\u540e\u200b\u6bcf\u5904\u200b\u66f4\u6539\u200b\u90fd\u200b\u4f1a\u200b\u5728\u200b\u5904\u5904\u200b\u4f53\u73b0\u200b\u3002
Variable
\u200b\u540c\u65f6\u200b\u652f\u6301\u200btype
\u3001choices
\u3001validator
\u3001required
\u200b\u6765\u200b\u786e\u4fdd\u200b\u503c\u200b\u7684\u200b\u6b63\u786e\u6027\u200b\u3002
\u200b\u4e3a\u4e86\u200b\u66f4\u52a0\u200b\u7b80\u5355\u200b\uff0cVariable
\u200b\u8fd8\u200b\u652f\u6301\u200bhelp
\u200b\u6765\u200b\u5728\u200b\u4f7f\u7528\u200bConfigParser
\u200b\u65f6\u200b\u63d0\u4f9b\u200b\u63cf\u8ff0\u200b\u3002
ConfigParser
\u200b\u5728\u200bArgumentParser
\u200b\u7684\u200b\u57fa\u7840\u200b\u4e4b\u4e0a\u200b\uff0c\u200b\u63d0\u4f9b\u200b\u4e86\u200bparse
\u200b\u548c\u200bparse_config
\u200b\u6765\u200b\u89e3\u6790\u200b\u547d\u4ee4\u884c\u200b\u53c2\u6570\u200b\u5e76\u200b\u521b\u5efa\u200b/\u200b\u66f4\u65b0\u200bConfig
\u3002
CHANfiG \u200b\u6709\u7740\u200b\u5f3a\u5927\u200b\u7684\u200b\u524d\u200b\u5411\u200b\u517c\u5bb9\u200b\u80fd\u529b\u200b\uff0c\u200b\u80fd\u591f\u200b\u826f\u597d\u200b\u7684\u200b\u517c\u5bb9\u200b\u4ee5\u5f80\u200b\u57fa\u4e8e\u200b yaml \u200b\u548c\u200b json \u200b\u7684\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u3002
\u200b\u5982\u679c\u200b\u4f60\u200b\u6b64\u524d\u200b\u4f7f\u7528\u200b yacs\uff0c\u200b\u53ea\u200b\u9700\u200b\u7b80\u5355\u200b\u5c06\u200bCfgNode
\u200b\u66ff\u6362\u200b\u4e3a\u200bConfig
\u200b\u4fbf\u200b\u53ef\u4ee5\u200b\u4eab\u53d7\u200b\u6240\u6709\u200b CHANfiG \u200b\u6240\u200b\u63d0\u4f9b\u200b\u7684\u200b\u4fbf\u5229\u200b\u3002
\u200b\u66f4\u8fdb\u4e00\u6b65\u200b\u7684\u200b\uff0c\u200b\u5982\u679c\u200b\u4f60\u200b\u53d1\u73b0\u200bConfig
\u200b\u4e2d\u200b\u7684\u200b\u540d\u5b57\u200b\u5bf9\u4e8e\u200b\u547d\u4ee4\u884c\u200b\u6765\u8bf4\u200b\u8fc7\u957f\u200b\uff0c\u200b\u4f60\u200b\u53ef\u4ee5\u200b\u7b80\u5355\u200b\u7684\u200b\u8c03\u7528\u200bself.add_argument
\u200b\u5e76\u200b\u8bbe\u7f6e\u200b\u6070\u5f53\u200b\u7684\u200bdest
\u200b\u6765\u200b\u5728\u200b\u547d\u4ee4\u884c\u200b\u4e2d\u200b\u4f7f\u7528\u200b\u66f4\u200b\u77ed\u200b\u7684\u200b\u540d\u5b57\u200b\uff0c\u200b\u6b63\u5982\u200bargparse
\u200b\u6240\u200b\u505a\u200b\u7684\u200b\u90a3\u6837\u200b\u3002
from chanfig import Config, Variable\nclass Model:\ndef __init__(self, encoder, dropout=0.1, activation='ReLU'):\nself.encoder = Encoder(**encoder)\nself.dropout = Dropout(dropout)\nself.activation = getattr(Activation, activation)\ndef main(config):\nmodel = Model(**config.model)\noptimizer = Optimizer(**config.optimizer)\nscheduler = Scheduler(**config.scheduler)\ndataset = Dataset(**config.dataset)\ndataloader = Dataloader(**config.dataloader)\nclass TestConfig(Config):\ndef __init__(self):\nsuper().__init__()\ndropout = Variable(0.1)\nself.name = \"CHANfiG\"\nself.seed = 1013\nself.data.batch_size = 64\nself.model.encoder.num_layers = 6\nself.model.decoder.num_layers = 6\nself.model.dropout = dropout\nself.model.encoder.dropout = dropout\nself.model.decoder.dropout = dropout\nself.activation = \"GELU\"\nself.optim.lr = 1e-3\nself.add_argument(\"--batch_size\", dest=\"data.batch_size\")\nself.add_argument(\"--lr\", dest=\"optim.lr\")\ndef post(self):\nself.id = f\"{self.name}_{self.seed}\"\nif __name__ == '__main__':\n# config = Config.load('config.yaml') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u8bfb\u53d6\u200b\u4e00\u4e2a\u200b yaml\n# config = Config.load('config.json') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u8bfb\u53d6\u200b\u4e00\u4e2a\u200b json\n# existing_configs = {'data.batch_size': 64, 'model.encoder.num_layers': 8}\n# config = Config(**existing_configs) # \u200b\u5982\u679c\u200b\u4f60\u200b\u6709\u4e9b\u200bconfig\u200b\u9700\u8981\u200b\u8bfb\u53d6\u200b\nconfig = TestConfig()\nconfig = config.parse()\n# config.merge('dataset.yaml') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u5408\u5e76\u200b\u4e00\u4e2a\u200b yaml\n# config.merge('dataset.json') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u5408\u5e76\u200b\u4e00\u4e2a\u200b json\n# \u200b\u6ce8\u610f\u200b\u88ab\u200b\u5408\u5e76\u200b\u7684\u200b\u503c\u200b\u5177\u6709\u200b\u66f4\u200b\u9ad8\u200b\u7684\u200b\u4f18\u5148\u7ea7\u200b\nconfig.model.decoder.num_layers = 8\nconfig.freeze()\nprint(config)\n# main(config)\n# config.yaml('config.yaml') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u4fdd\u5b58\u200b\u4e00\u4e2a\u200b yaml\n# config.json('config.json') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u4fdd\u5b58\u200b\u4e00\u4e2a\u200b json\n
\u200b\u6240\u6709\u200b\u4f60\u200b\u9700\u8981\u200b\u505a\u200b\u7684\u200b\u4ec5\u4ec5\u200b\u662f\u200b\u8fd0\u884c\u200b\u4e00\u884c\u200b\uff1a
Bashpython main.py --model.encoder.num_layers 8 --model.dropout=0.2 --lr 5e-3\n
\u200b\u5f53\u7136\u200b\uff0c\u200b\u4f60\u200b\u4e5f\u200b\u53ef\u4ee5\u200b\u8bfb\u53d6\u200b\u4e00\u4e2a\u200b\u9ed8\u8ba4\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u7136\u540e\u200b\u5728\u200b\u4ed6\u200b\u57fa\u7840\u200b\u4e0a\u200b\u4fee\u6539\u200b\uff1a
\u200b\u6ce8\u610f\u200b\uff0c\u200b\u4f60\u200b\u5fc5\u987b\u200b\u6307\u5b9a\u200bconfig.parse(default_config='config')
\u200b\u6765\u200b\u6b63\u786e\u200b\u8bfb\u53d6\u200b\u9ed8\u8ba4\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u3002
python main.py --config meow.yaml --model.encoder.num_layers 8 --model.dropout=0.2 --lr 5e-3\n
\u200b\u5982\u679c\u200b\u4f60\u200b\u4fdd\u5b58\u200b\u4e86\u200b\u914d\u7f6e\u6587\u4ef6\u200b\uff0c\u200b\u90a3\u200b\u4ed6\u200b\u5e94\u8be5\u200b\u770b\u8d77\u6765\u200b\u50cf\u200b\u8fd9\u6837\u200b\uff1a
YAMLactivation: GELU\ndata:\nbatch_size: 64\nid: CHANfiG_1013\nmodel:\ndecoder:\ndropout: 0.1\nnum_layers: 6\ndropout: 0.1\nencoder:\ndropout: 0.1\nnum_layers: 6\nname: CHANfiG\noptim:\nlr: 0.005\nseed: 1013\n
JSON{\n\"name\": \"CHANfiG\",\n\"seed\": 1013,\n\"data\": {\n\"batch_size\": 64\n},\n\"model\": {\n\"encoder\": {\n\"num_layers\": 6,\n\"dropout\": 0.1\n},\n\"decoder\": {\n\"num_layers\": 6,\n\"dropout\": 0.1\n},\n\"dropout\": 0.1\n},\n\"activation\": \"GELU\",\n\"optim\": {\n\"lr\": 0.005\n},\n\"id\": \"CHANfiG_1013\"\n}\n
\u200b\u5728\u200b\u65b9\u6cd5\u200b\u4e2d\u200b\u5b9a\u4e49\u200b\u9ed8\u8ba4\u200b\u53c2\u6570\u200b\uff0c\u200b\u5728\u200b\u547d\u4ee4\u884c\u200b\u4e2d\u200b\u4fee\u6539\u200b\uff0c\u200b\u7136\u540e\u200b\u5c06\u200b\u5269\u4e0b\u200b\u7684\u200b\u4ea4\u7ed9\u200b CHANfiG\u3002
"},{"location":"zh/#_6","title":"\u5b89\u88c5","text":"\u200b\u5b89\u88c5\u200b pypi \u200b\u4e0a\u200b\u6700\u8fd1\u200b\u7684\u200b\u7a33\u5b9a\u200b\u7248\u672c\u200b\uff1a
Bashpip install chanfig\n
\u200b\u4ece\u200b\u6e90\u7801\u200b\u5b89\u88c5\u200b\u6700\u65b0\u200b\u7684\u200b\u7248\u672c\u200b\uff1a
Bashpip install git+https://github.com/ZhiyuanChen/CHANfiG\n
\u200b\u4ed6\u200b\u672c\u8be5\u5982\u6b64\u200b\u5de5\u4f5c\u200b\u3002
"},{"location":"zh/#_7","title":"\u6388\u6743","text":"CHANfiG \u200b\u4f9d\u636e\u200b\u4e0b\u5217\u200b\u8bb8\u53ef\u8bc1\u200b\u8fdb\u884c\u200b\u591a\u91cd\u200b\u6388\u6743\u200b\uff1a
\u200b\u5982\u679c\u200b\u4f60\u200b\u4f7f\u7528\u200b\u672c\u200b\u5de5\u4f5c\u200b\uff0c\u200b\u4f60\u200b\u53ef\u4ee5\u200b\u4ece\u4e2d\u200b\u4efb\u9009\u200b\uff08\u200b\u4e00\u4e2a\u200b\u6216\u8005\u200b\u591a\u4e2a\u200b\uff09\u200b\u8bb8\u53ef\u8bc1\u200b\u3002
SPDX-License-Identifier: Unlicense OR AGPL-3.0-or-later OR GPL-2.0-or-later OR BSD-4-Clause OR MIT OR Apache-2.0
CHANfiG aims to make your configuration easier.
There are tons of configurable parameters in training a Machine Learning model. To configure all these parameters, researchers usually need to write gigantic config files, sometimes even thousands of lines. Most of the configs are just replicates of the default arguments of certain functions, resulting in many unnecessary declarations. It is also very hard to alter the configurations. One needs to navigate and open the right configuration file, make changes, save and exit. These had wasted an uncountable1 amount of precious time and are no doubt a crime. Using argparse
could relieve the burdens to some extent. However, it takes a lot of work to make it compatible with existing config files, and its lack of nesting limits its potential.
CHANfiG would like to make a change.
You just type the alternations in the command line, and leave everything else to CHANfiG.
CHANfiG is highly inspired by YACS. Different from the paradigm of YACS( your code + a YACS config for experiment E (+ external dependencies + hardware + other nuisance terms ...) = reproducible experiment E
), The paradigm of CHANfiG is:
your code + command line arguments (+ optional CHANfiG config + external dependencies + hardware + other nuisance terms ...) = reproducible experiment E (+ optional CHANfiG config for experiment E)
A Config is basically a nested dict structure.
However, the default Python dict is hard to manipulate.
The only way to access a dict member is through dict['name']
, which is obviously extremely complex. Even worse, if the dict is nested like a config, member access could be something like dict['parent']['children']['name']
.
Enough is enough, it is time to make a change.
We need attribute-style access, and we need it now. dict.name
and dict.parent.children.name
is all you need.
Although there have been some other works that achieve a similar functionality of attribute-style access to dict members. Their Config object either uses a separate dict to store information from attribute-style access (EasyDict), which may lead to inconsistency between attribute-style access and dict-style access; or re-use the existing __dict__
and redirect dict-style access (ml_collections), which may result in confliction between attributes and members of Config.
To overcome the aforementioned limitations, we inherit the Python built-in dict
to create FlatDict
, DefaultDict
, NestedDict
, Config
, and Registry
. We also introduce Variable
to allow sharing a value across multiple places, and ConfigParser
to parse command line arguments.
FlatDict
improves the default dict
in 3 aspects.
FlatDict
incorporates a merge
method that allows you to merge a Mapping
, an Iterable
, or a path to the FlatDict
. Different from built-in update
, merge
assign values instead of replace, which makes it works better with DefaultDict
.
dict
in Python is ordered since Python 3.7, but there isn\u2019t a built-in method to help you sort a dict
. FlatDict
supportssort
to help you manage your dict.
Moreover, FlatDict
comes with difference
and intersect
, which makes it very easy to compare a FlatDict
with other Mapping
, Iterable
, or a path.
FlatDict
supports to
method similar to PyTorch Tensors. You can simply convert all member values of FlatDict
to a certain type or pass to a device in the same way.
FlatDict
also integrates cpu
, gpu
(cuda
), and tpu
(xla
) methods for easier access.
FlatDict
provides json
, jsons
, yaml
and yamls
methods to dump FlatDict
to a file or string. It also provides from_json
, from_jsons
, from_yaml
and from_yamls
methods to build a FlatDict
from a string or file.
FlatDict
also includes dump
and load
methods which determines the type by its extension and dump/load FlatDict
to/from a file.
To facility the needs of default values, we incorporate DefaultDict
which accepts default_factory
and works just like a collections.defaultdict
.
Since most Configs are in a nested structure, we further propose a NestedDict
.
Based on DefaultDict
, NestedDict
provides all_keys
, all_values
, and all_items
methods to allow iterating over the whole nested structure at once.
NestedDict
also comes with apply
and apply_
methods, which made it easier to manipulate the nested structure.
Config
extends the functionality by supporting freeze
and defrost
, and by adding a built-in ConfigParser
to pare command line arguments.
Note that Config
also has default_factory=Config()
by default for convenience.
Registry
extends the NestedDict
and supports register
, lookup
, and build
to help you register constructors and build objects from a Config
.
Have one value for multiple names at multiple places? We got you covered.
Just wrap the value with Variable
, and one alteration will be reflected everywhere.
Variable
also supports type
, choices
, validator
, and required
to ensure the correctness of the value.
To make it even easier, Variable
also supports help
to provide a description when using ConfigParser
.
ConfigParser
extends ArgumentParser
and provides parse
and parse_config
to parse command line arguments.
CHANfiG has great backward compatibility with previous configs.
No matter if your old config is json or yaml, you could directly read from them.
And if you are using yacs, just replace CfgNode
with Config
and enjoy all the additional benefits that CHANfiG provides.
Moreover, if you find a name in the config is too long for command-line, you could simply call self.add_argument
with proper dest
to use a shorter name in command-line, as you do with argparse
.
from chanfig import Config, Variable\nclass Model:\ndef __init__(self, encoder, dropout=0.1, activation='ReLU'):\nself.encoder = Encoder(**encoder)\nself.dropout = Dropout(dropout)\nself.activation = getattr(Activation, activation)\ndef main(config):\nmodel = Model(**config.model)\noptimizer = Optimizer(**config.optimizer)\nscheduler = Scheduler(**config.scheduler)\ndataset = Dataset(**config.dataset)\ndataloader = Dataloader(**config.dataloader)\nclass TestConfig(Config):\ndef __init__(self):\nsuper().__init__()\ndropout = Variable(0.1)\nself.name = \"CHANfiG\"\nself.seed = 1013\nself.data.batch_size = 64\nself.model.encoder.num_layers = 6\nself.model.decoder.num_layers = 6\nself.model.dropout = dropout\nself.model.encoder.dropout = dropout\nself.model.decoder.dropout = dropout\nself.activation = \"GELU\"\nself.optim.lr = 1e-3\nself.add_argument(\"--batch_size\", dest=\"data.batch_size\")\nself.add_argument(\"--lr\", dest=\"optim.lr\")\ndef post(self):\nself.id = f\"{self.name}_{self.seed}\"\nif __name__ == '__main__':\n# config = Config.load('config.yaml') # in case you want to read from a yaml\n# config = Config.load('config.json') # in case you want to read from a json\n# existing_configs = {'data.batch_size': 64, 'model.encoder.num_layers': 8}\n# config = Config(**existing_configs) # in case you have some config in dict to load\nconfig = TestConfig()\nconfig = config.parse()\n# config.merge('dataset.yaml') # in case you want to merge a yaml\n# config.merge('dataset.json') # in case you want to merge a json\n# note that the value of merge will override current values\nconfig.model.decoder.num_layers = 8\nconfig.freeze()\nprint(config)\n# main(config)\n# config.yaml('config.yaml') # in case you want to save a yaml\n# config.json('config.json') # in case you want to save a json\n
All you need to do is just run a line:
Bashpython main.py --model.encoder.num_layers 8 --model.dropout=0.2 --lr 5e-3\n
You could also load a default configure file and make changes based on it:
Note, you must specify config.parse(default_config='config')
to correctly load the default config.
python main.py --config meow.yaml --model.encoder.num_layers 8 --model.dropout=0.2 --lr 5e-3\n
If you have made it dump current configurations, this should be in the written file:
YAMLactivation: GELU\ndata:\nbatch_size: 64\nid: CHANfiG_1013\nmodel:\ndecoder:\ndropout: 0.1\nnum_layers: 6\ndropout: 0.1\nencoder:\ndropout: 0.1\nnum_layers: 6\nname: CHANfiG\noptim:\nlr: 0.005\nseed: 1013\n
JSON{\n\"name\": \"CHANfiG\",\n\"seed\": 1013,\n\"data\": {\n\"batch_size\": 64\n},\n\"model\": {\n\"encoder\": {\n\"num_layers\": 6,\n\"dropout\": 0.1\n},\n\"decoder\": {\n\"num_layers\": 6,\n\"dropout\": 0.1\n},\n\"dropout\": 0.1\n},\n\"activation\": \"GELU\",\n\"optim\": {\n\"lr\": 0.005\n},\n\"id\": \"CHANfiG_1013\"\n}\n
Define the default arguments in function, put alterations in CLI, and leave the rest to CHANfiG.
"},{"location":"#installation","title":"Installation","text":"Install the most recent stable version on pypi:
Bashpip install chanfig\n
Install the latest version from source:
Bashpip install git+https://github.com/ZhiyuanChen/CHANfiG\n
It works the way it should have worked.
"},{"location":"#license","title":"License","text":"CHANfiG is multi-licensed under the following licenses:
You can choose any (one or more) of these licenses if you use this work.
SPDX-License-Identifier: Unlicense OR AGPL-3.0-or-later OR GPL-2.0-or-later OR BSD-4-Clause OR MIT OR Apache-2.0
fun fact: time is always uncountable.\u00a0\u21a9
Config
","text":" Bases: NestedDict
Config
is an extension of NestedDict
.
The differences between Config
and NestedDict
lies in 3 aspects:
Config
has default_factory
set to Config
and convert_mapping
set to True
by default.Config
has a frozen
attribute, which can be toggled with freeze
(lock
) & defrost
(unlock
) or temporarily changed with locked
& unlocked
.Config
has a ConfigParser
built-in, and supports add_argument
and parse
.Config also features a post
method and a boot
method to support lazy-initilisation. This is useful when you want to perform some post-processing on the config. For example, some values may be a combination of other values, and you may define them in post
.
boot
is introduced to call all post
methods in the nested structure of Config
object. By default, boot
will be called to after Config
is parsed.
You could also manually call boot
if you you don\u2019t parse command-line arguments.
Since Config
has default_factory
set to Config
, accessing anything that does not exist will create a new empty Config sub-attribute.
A frozen Config
does not have this behaviour and will raises KeyError
when accessing anything that does not exist.
It is recommended to call config.freeze()
or config.to(NestedDict)
to avoid this behaviour.
Attributes:
Name Type Descriptionparser
ConfigParser
Parser for command-line arguments.
frozen
bool
If True
, the config is frozen and cannot be altered.
Examples:
Python Console Session>>> c = Config(**{\"f.n\": \"chang\"})\n>>> c.i.d = 1013\n>>> c.i.d\n1013\n>>> c.d.i\nConfig(<class 'chanfig.config.Config'>, )\n>>> c.freeze().dict()\n{'f': {'n': 'chang'}, 'i': {'d': 1013}, 'd': {'i': {}}}\n>>> c.d.i = 1013\nTraceback (most recent call last):\nValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n>>> c.d.e\nTraceback (most recent call last):\nAttributeError: 'Config' object has no attribute 'e'\n>>> with c.unlocked():\n... del c.d\n>>> c.dict()\n{'f': {'n': 'chang'}, 'i': {'d': 1013}}\n
Source code in chanfig/config.py
Pythonclass Config(NestedDict): # type: ignore\nr\"\"\"\n `Config` is an extension of `NestedDict`.\n The differences between `Config` and `NestedDict` lies in 3 aspects:\n 1. `Config` has `default_factory` set to `Config` and `convert_mapping` set to `True` by default.\n 2. `Config` has a `frozen` attribute, which can be toggled with `freeze`(`lock`) & `defrost`(`unlock`)\n or temporarily changed with `locked` & `unlocked`.\n 3. `Config` has a `ConfigParser` built-in, and supports `add_argument` and `parse`.\n Config also features a `post` method and a `boot` method to support lazy-initilisation.\n This is useful when you want to perform some post-processing on the config.\n For example, some values may be a combination of other values, and you may define them in `post`.\n `boot` is introduced to call all `post` methods in the nested structure of `Config` object.\n By default, `boot` will be called to after `Config` is parsed.\n You could also manually call `boot` if you you don't parse command-line arguments.\n Notes:\n Since `Config` has `default_factory` set to `Config`,\n accessing anything that does not exist will create a new empty Config sub-attribute.\n A **frozen** `Config` does not have this behaviour and\n will raises `KeyError` when accessing anything that does not exist.\n It is recommended to call `config.freeze()` or `config.to(NestedDict)` to avoid this behaviour.\n Attributes:\n parser (ConfigParser): Parser for command-line arguments.\n frozen (bool): If `True`, the config is frozen and cannot be altered.\n Examples:\n >>> c = Config(**{\"f.n\": \"chang\"})\n >>> c.i.d = 1013\n >>> c.i.d\n 1013\n >>> c.d.i\n Config(<class 'chanfig.config.Config'>, )\n >>> c.freeze().dict()\n {'f': {'n': 'chang'}, 'i': {'d': 1013}, 'd': {'i': {}}}\n >>> c.d.i = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.d.e\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'e'\n >>> with c.unlocked():\n ... del c.d\n >>> c.dict()\n {'f': {'n': 'chang'}, 'i': {'d': 1013}}\n \"\"\"\nparser: None # ConfigParser, Python 3.7 does not support forward reference\nfrozen: bool = False\ndef __init__(self, *args: Any, default_factory: Callable | None = None, **kwargs: Any):\nif default_factory is None:\ndefault_factory = Config\nsuper().__init__(*args, default_factory=default_factory, **kwargs)\ndef post(self) -> Config | None:\nr\"\"\"\n Post process of `Config`.\n Some `Config` may need to do some post process after `Config` is initialised.\n `post` is provided for this lazy-initialisation purpose.\n By default, `post` calls `interpolate` to perform variable interpolation.\n Note that you should always call `boot` to apply `post` rather than calling `post` directly,\n as `boot` recursively call `post` on sub-configs.\n See Also: [`boot`][chanfig.Config.boot]\n Returns:\n self:\n Examples:\n >>> class PostConfig(Config):\n ... def post(self):\n ... if isinstance(self.data, str):\n ... self.data = Config(feature=self.data, label=self.data)\n ... return self\n >>> c = PostConfig(data=\"path\")\n >>> c.post()\n PostConfig(<class 'chanfig.config.Config'>,\n ('data'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n \"\"\"\nself.interpolate()\nself.validate()\nreturn self\ndef boot(self) -> Config:\nr\"\"\"\n Apply `post` recursively.\n Sub-config may have their own `post` method.\n `boot` is provided to apply `post` recursively.\n By default, `boot` is called after `Config` is parsed.\n If you don't need to parse command-line arguments, you should call `boot` manually.\n See Also: [`post`][chanfig.Config.post]\n Returns:\n self:\n Examples:\n >>> class DataConfig(Config):\n ... def post(self):\n ... if isinstance(self.path, str):\n ... self.path = Config(feature=self.path, label=self.path)\n ... return self\n >>> class BootConfig(Config):\n ... def __init__(self, *args, **kwargs):\n ... super().__init__(*args, **kwargs)\n ... self.dataset = DataConfig(path=\"path\")\n ... def post(self):\n ... if isinstance(self.id, str):\n ... self.id += \"_id\"\n ... return self\n >>> c = BootConfig(id=\"boot\")\n >>> c.boot()\n BootConfig(<class 'chanfig.config.Config'>,\n ('id'): 'boot_id'\n ('dataset'): DataConfig(<class 'chanfig.config.Config'>,\n ('path'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n )\n \"\"\"\nfor value in self.values():\nif isinstance(value, Config):\nvalue.boot()\nself.post()\nreturn self\ndef parse(\nself,\nargs: Iterable[str] | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\nboot: bool = True,\n) -> Config:\nr\"\"\"\n Parse command-line arguments with `ConfigParser`.\n `parse` will try to parse all command-line arguments,\n you don't need to pre-define them but typos may cause trouble.\n By default, this method internally calls `Config.boot()`.\n To disable this behaviour, set `boot` to `False`.\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `None`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n boot (bool, optional): If `True`, call `Config.boot()` after parsing. Defaults to `True`.\n See Also:\n [`chanfig.ConfigParser.parse`][chanfig.ConfigParser.parse]: Implementation of `parse`.\n [`parse_config`][chanfig.Config.parse_config]: Only parse valid config arguments.\n Examples:\n >>> c = Config(a=0)\n >>> c.dict()\n {'a': 0}\n >>> c.parse(['--a', '1', '--b', '2', '--c', '3']).dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nself.getattr(\"parser\").parse(args, self, default_config, no_default_config_action)\nif boot:\nself.boot()\nreturn self\ndef parse_config(\nself,\nargs: Iterable[str] | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\nboot: bool = True,\n) -> Config:\nr\"\"\"\n Parse command-line arguments with `ConfigParser`.\n `parse_config` only parse command-line arguments that is in defined in `Config`.\n By default, this method internally calls `Config.boot()`.\n To disable this behaviour, set `boot` to `False`.\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `None`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n boot (bool, optional): If `True`, call `Config.boot()` after parsing. Defaults to `True`.\n See Also:\n [`chanfig.ConfigParser.parse_config`][chanfig.ConfigParser.parse_config]: Implementation of `parse_config`.\n [`parse`][chanfig.Config.parse]: Parse all command-line arguments.\n Examples:\n >>> c = Config(a=0, b=0, c=0)\n >>> c.dict()\n {'a': 0, 'b': 0, 'c': 0}\n >>> c.parse_config(['--a', '1', '--b', '2', '--c', '3']).dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nself.getattr(\"parser\").parse_config(args, self, default_config, no_default_config_action)\nif boot:\nself.boot()\nreturn self\ndef add_argument(self, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Add an argument to `ConfigParser`.\n Note that value defined in `Config` will override the default value defined in `add_argument`.\n Examples:\n >>> c = Config(a=0, c=1)\n >>> arg = c.add_argument(\"--a\", type=int, default=1)\n >>> arg = c.add_argument(\"--b\", type=int, default=2)\n >>> c.parse(['--c', '4']).dict()\n {'a': 1, 'c': 4, 'b': 2}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nreturn self.getattr(\"parser\").add_argument(*args, **kwargs)\ndef freeze(self, recursive: bool = True) -> Config:\nr\"\"\"\n Freeze `Config`.\n Args:\n recursive:\n **Alias**:\n + `lock`\n Examples:\n >>> c = Config(**{'i.d': 1013})\n >>> c.getattr('frozen')\n False\n >>> c.freeze(recursive=False).dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n True\n >>> c.i.getattr('frozen')\n False\n >>> c.lock().dict() # alias\n {'i': {'d': 1013}}\n >>> c.i.getattr('frozen')\n True\n \"\"\"\n@wraps(self.freeze)\ndef freeze(config: Config) -> None:\nif isinstance(config, Config):\nconfig.setattr(\"frozen\", True)\nif recursive:\nself.apply_(freeze)\nelse:\nfreeze(self)\nreturn self\ndef lock(self, recursive: bool = True) -> Config:\nr\"\"\"\n Alias of [`freeze`][chanfig.Config.freeze].\n \"\"\"\nreturn self.freeze(recursive=recursive)\n@contextmanager\ndef locked(self):\n\"\"\"\n Context manager which temporarily locks `Config`.\n Examples:\n >>> c = Config()\n >>> with c.locked():\n ... c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.i.d = 1013\n >>> c.dict()\n {'i': {'d': 1013}}\n \"\"\"\nwas_frozen = self.getattr(\"frozen\", False)\ntry:\nself.freeze()\nyield self\nfinally:\nif not was_frozen:\nself.defrost()\ndef defrost(self, recursive: bool = True) -> Config:\nr\"\"\"\n Defrost `Config`.\n Args:\n recursive:\n **Alias**:\n + `unlock`\n Examples:\n >>> c = Config(**{'i.d': 1013})\n >>> c.getattr('frozen')\n False\n >>> c.freeze().dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n True\n >>> c.defrost(recursive=False).dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n False\n >>> c.i.getattr('frozen')\n True\n >>> c.unlock().dict() # alias\n {'i': {'d': 1013}}\n >>> c.i.getattr('frozen')\n False\n \"\"\"\n@wraps(self.defrost)\ndef defrost(config: Config) -> None:\nif isinstance(config, Config):\nconfig.setattr(\"frozen\", False)\nif recursive:\nself.apply_(defrost)\nelse:\ndefrost(self)\nreturn self\ndef unlock(self, recursive: bool = True) -> Config:\nr\"\"\"\n Alias of [`defrost`][chanfig.Config.defrost].\n \"\"\"\nreturn self.defrost(recursive=recursive)\n@contextmanager\ndef unlocked(self):\n\"\"\"\n Context manager which temporarily unlocks `Config`.\n Examples:\n >>> c = Config()\n >>> c.freeze().dict()\n {}\n >>> with c.unlocked():\n ... c['i.d'] = 1013\n >>> c.defrost().dict()\n {'i': {'d': 1013}}\n \"\"\"\nwas_frozen = self.getattr(\"frozen\", False)\ntry:\nself.defrost()\nyield self\nfinally:\nif was_frozen:\nself.freeze()\ndef get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\nr\"\"\"\n Get value from `Config`.\n Note that `default` has higher priority than `default_factory`.\n Args:\n name:\n default:\n Returns:\n value:\n If `Config` does not contain `name`, return `default`.\n If `default` is not specified, return `default_factory()`.\n Raises:\n KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.\n Examples:\n >>> d = Config(**{\"i.d\": 1013})\n >>> d.get('i.d')\n 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.f\n Config(<class 'chanfig.config.Config'>, )\n >>> del d.f\n >>> d.freeze()\n Config(<class 'chanfig.config.Config'>,\n ('i'): Config(<class 'chanfig.config.Config'>,\n ('d'): 1013\n )\n )\n >>> d.f\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'f'\n >>> d[\"f.n\"]\n Traceback (most recent call last):\n KeyError: 'f.n'\n \"\"\"\nif not self.hasattr(\"default_factory\"): # did not call super().__init__() in sub-class\nself.setattr(\"default_factory\", Config)\nif name in self or not self.getattr(\"frozen\", False):\nreturn super().get(name, default, fallback)\nraise KeyError(name)\n@frozen_check\ndef set(\nself,\nname: Any,\nvalue: Any,\nconvert_mapping: bool | None = None,\n) -> None:\nr\"\"\"\n Set value of `Config`.\n Args:\n name:\n value:\n convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n Defaults to self.convert_mapping.\n Raises:\n ValueError: If `Config` is frozen.\n Examples:\n >>> c = Config()\n >>> c['i.d'] = 1013\n >>> c.i.d\n 1013\n >>> c.freeze().dict()\n {'i': {'d': 1013}}\n >>> c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.defrost().dict()\n {'i': {'d': 1013}}\n >>> c['i.d'] = 1013\n >>> c.i.d\n 1013\n \"\"\"\nreturn super().set(name, value, convert_mapping)\n@frozen_check\ndef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `Config`.\n Args:\n name:\n Examples:\n >>> d = Config(**{\"i.d\": 1013, \"f.n\": \"chang\"})\n >>> d.i.d\n 1013\n >>> d.f.n\n 'chang'\n >>> d.delete('i.d')\n >>> \"i.d\" in d\n False\n >>> d.i.d\n Config(<class 'chanfig.config.Config'>, )\n >>> \"i.d\" in d\n True\n >>> del d.f.n\n >>> d.f.n\n Config(<class 'chanfig.config.Config'>, )\n >>> del d.c\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'c'\n \"\"\"\nsuper().delete(name)\n@frozen_check\ndef pop(self, name: Any, default: Any = Null) -> Any:\nr\"\"\"\n Pop value from `Config`.\n Args:\n name:\n default:\n Returns:\n value: If `Config` does not contain `name`, return `default`.\n Examples:\n >>> c = Config()\n >>> c['i.d'] = 1013\n >>> c.pop('i.d')\n 1013\n >>> c.pop('i.d', True)\n True\n >>> c.freeze().dict()\n {'i': {}}\n >>> c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.defrost().dict()\n {'i': {}}\n >>> c['i.d'] = 1013\n >>> c.pop('i.d')\n 1013\n \"\"\"\nreturn super().pop(name, default)\n
"},{"location":"config/#chanfig.config.Config.add_argument","title":"add_argument(*args, **kwargs)
","text":"Add an argument to ConfigParser
.
Note that value defined in Config
will override the default value defined in add_argument
.
Examples:
Python Console Session>>> c = Config(a=0, c=1)\n>>> arg = c.add_argument(\"--a\", type=int, default=1)\n>>> arg = c.add_argument(\"--b\", type=int, default=2)\n>>> c.parse(['--c', '4']).dict()\n{'a': 1, 'c': 4, 'b': 2}\n
Source code in chanfig/config.py
Pythondef add_argument(self, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Add an argument to `ConfigParser`.\n Note that value defined in `Config` will override the default value defined in `add_argument`.\n Examples:\n >>> c = Config(a=0, c=1)\n >>> arg = c.add_argument(\"--a\", type=int, default=1)\n >>> arg = c.add_argument(\"--b\", type=int, default=2)\n >>> c.parse(['--c', '4']).dict()\n {'a': 1, 'c': 4, 'b': 2}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nreturn self.getattr(\"parser\").add_argument(*args, **kwargs)\n
"},{"location":"config/#chanfig.config.Config.boot","title":"boot()
","text":"Apply post
recursively.
Sub-config may have their own post
method. boot
is provided to apply post
recursively.
By default, boot
is called after Config
is parsed. If you don\u2019t need to parse command-line arguments, you should call boot
manually.
See Also: post
Returns:
Name Type Descriptionself
Config
Examples:
Python Console Session>>> class DataConfig(Config):\n... def post(self):\n... if isinstance(self.path, str):\n... self.path = Config(feature=self.path, label=self.path)\n... return self\n>>> class BootConfig(Config):\n... def __init__(self, *args, **kwargs):\n... super().__init__(*args, **kwargs)\n... self.dataset = DataConfig(path=\"path\")\n... def post(self):\n... if isinstance(self.id, str):\n... self.id += \"_id\"\n... return self\n>>> c = BootConfig(id=\"boot\")\n>>> c.boot()\nBootConfig(<class 'chanfig.config.Config'>,\n ('id'): 'boot_id'\n ('dataset'): DataConfig(<class 'chanfig.config.Config'>,\n ('path'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n)\n
Source code in chanfig/config.py
Pythondef boot(self) -> Config:\nr\"\"\"\n Apply `post` recursively.\n Sub-config may have their own `post` method.\n `boot` is provided to apply `post` recursively.\n By default, `boot` is called after `Config` is parsed.\n If you don't need to parse command-line arguments, you should call `boot` manually.\n See Also: [`post`][chanfig.Config.post]\n Returns:\n self:\n Examples:\n >>> class DataConfig(Config):\n ... def post(self):\n ... if isinstance(self.path, str):\n ... self.path = Config(feature=self.path, label=self.path)\n ... return self\n >>> class BootConfig(Config):\n ... def __init__(self, *args, **kwargs):\n ... super().__init__(*args, **kwargs)\n ... self.dataset = DataConfig(path=\"path\")\n ... def post(self):\n ... if isinstance(self.id, str):\n ... self.id += \"_id\"\n ... return self\n >>> c = BootConfig(id=\"boot\")\n >>> c.boot()\n BootConfig(<class 'chanfig.config.Config'>,\n ('id'): 'boot_id'\n ('dataset'): DataConfig(<class 'chanfig.config.Config'>,\n ('path'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n )\n \"\"\"\nfor value in self.values():\nif isinstance(value, Config):\nvalue.boot()\nself.post()\nreturn self\n
"},{"location":"config/#chanfig.config.Config.defrost","title":"defrost(recursive=True)
","text":"Defrost Config
.
Parameters:
Name Type Description Defaultrecursive
bool
True
Alias:
unlock
Examples:
Python Console Session>>> c = Config(**{'i.d': 1013})\n>>> c.getattr('frozen')\nFalse\n>>> c.freeze().dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nTrue\n>>> c.defrost(recursive=False).dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nFalse\n>>> c.i.getattr('frozen')\nTrue\n>>> c.unlock().dict() # alias\n{'i': {'d': 1013}}\n>>> c.i.getattr('frozen')\nFalse\n
Source code in chanfig/config.py
Pythondef defrost(self, recursive: bool = True) -> Config:\nr\"\"\"\n Defrost `Config`.\n Args:\n recursive:\n **Alias**:\n + `unlock`\n Examples:\n >>> c = Config(**{'i.d': 1013})\n >>> c.getattr('frozen')\n False\n >>> c.freeze().dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n True\n >>> c.defrost(recursive=False).dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n False\n >>> c.i.getattr('frozen')\n True\n >>> c.unlock().dict() # alias\n {'i': {'d': 1013}}\n >>> c.i.getattr('frozen')\n False\n \"\"\"\n@wraps(self.defrost)\ndef defrost(config: Config) -> None:\nif isinstance(config, Config):\nconfig.setattr(\"frozen\", False)\nif recursive:\nself.apply_(defrost)\nelse:\ndefrost(self)\nreturn self\n
"},{"location":"config/#chanfig.config.Config.delete","title":"delete(name)
","text":"Delete value from Config
.
Parameters:
Name Type Description Defaultname
Any
required Examples:
Python Console Session>>> d = Config(**{\"i.d\": 1013, \"f.n\": \"chang\"})\n>>> d.i.d\n1013\n>>> d.f.n\n'chang'\n>>> d.delete('i.d')\n>>> \"i.d\" in d\nFalse\n>>> d.i.d\nConfig(<class 'chanfig.config.Config'>, )\n>>> \"i.d\" in d\nTrue\n>>> del d.f.n\n>>> d.f.n\nConfig(<class 'chanfig.config.Config'>, )\n>>> del d.c\nTraceback (most recent call last):\nAttributeError: 'Config' object has no attribute 'c'\n
Source code in chanfig/config.py
Python@frozen_check\ndef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `Config`.\n Args:\n name:\n Examples:\n >>> d = Config(**{\"i.d\": 1013, \"f.n\": \"chang\"})\n >>> d.i.d\n 1013\n >>> d.f.n\n 'chang'\n >>> d.delete('i.d')\n >>> \"i.d\" in d\n False\n >>> d.i.d\n Config(<class 'chanfig.config.Config'>, )\n >>> \"i.d\" in d\n True\n >>> del d.f.n\n >>> d.f.n\n Config(<class 'chanfig.config.Config'>, )\n >>> del d.c\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'c'\n \"\"\"\nsuper().delete(name)\n
"},{"location":"config/#chanfig.config.Config.freeze","title":"freeze(recursive=True)
","text":"Freeze Config
.
Parameters:
Name Type Description Defaultrecursive
bool
True
Alias:
lock
Examples:
Python Console Session>>> c = Config(**{'i.d': 1013})\n>>> c.getattr('frozen')\nFalse\n>>> c.freeze(recursive=False).dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nTrue\n>>> c.i.getattr('frozen')\nFalse\n>>> c.lock().dict() # alias\n{'i': {'d': 1013}}\n>>> c.i.getattr('frozen')\nTrue\n
Source code in chanfig/config.py
Pythondef freeze(self, recursive: bool = True) -> Config:\nr\"\"\"\n Freeze `Config`.\n Args:\n recursive:\n **Alias**:\n + `lock`\n Examples:\n >>> c = Config(**{'i.d': 1013})\n >>> c.getattr('frozen')\n False\n >>> c.freeze(recursive=False).dict()\n {'i': {'d': 1013}}\n >>> c.getattr('frozen')\n True\n >>> c.i.getattr('frozen')\n False\n >>> c.lock().dict() # alias\n {'i': {'d': 1013}}\n >>> c.i.getattr('frozen')\n True\n \"\"\"\n@wraps(self.freeze)\ndef freeze(config: Config) -> None:\nif isinstance(config, Config):\nconfig.setattr(\"frozen\", True)\nif recursive:\nself.apply_(freeze)\nelse:\nfreeze(self)\nreturn self\n
"},{"location":"config/#chanfig.config.Config.get","title":"get(name, default=None, fallback=None)
","text":"Get value from Config
.
Note that default
has higher priority than default_factory
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
None
Returns:
Name Type Descriptionvalue
Any
If Config
does not contain name
, return default
. If default
is not specified, return default_factory()
.
Raises:
Type DescriptionKeyError
If Config
does not contain name
and default
/default_factory
is not specified.
Examples:
Python Console Session>>> d = Config(**{\"i.d\": 1013})\n>>> d.get('i.d')\n1013\n>>> d['i.d']\n1013\n>>> d.i.d\n1013\n>>> d.get('f', 2)\n2\n>>> d.f\nConfig(<class 'chanfig.config.Config'>, )\n>>> del d.f\n>>> d.freeze()\nConfig(<class 'chanfig.config.Config'>,\n ('i'): Config(<class 'chanfig.config.Config'>,\n ('d'): 1013\n )\n)\n>>> d.f\nTraceback (most recent call last):\nAttributeError: 'Config' object has no attribute 'f'\n>>> d[\"f.n\"]\nTraceback (most recent call last):\nKeyError: 'f.n'\n
Source code in chanfig/config.py
Pythondef get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\nr\"\"\"\n Get value from `Config`.\n Note that `default` has higher priority than `default_factory`.\n Args:\n name:\n default:\n Returns:\n value:\n If `Config` does not contain `name`, return `default`.\n If `default` is not specified, return `default_factory()`.\n Raises:\n KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.\n Examples:\n >>> d = Config(**{\"i.d\": 1013})\n >>> d.get('i.d')\n 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.f\n Config(<class 'chanfig.config.Config'>, )\n >>> del d.f\n >>> d.freeze()\n Config(<class 'chanfig.config.Config'>,\n ('i'): Config(<class 'chanfig.config.Config'>,\n ('d'): 1013\n )\n )\n >>> d.f\n Traceback (most recent call last):\n AttributeError: 'Config' object has no attribute 'f'\n >>> d[\"f.n\"]\n Traceback (most recent call last):\n KeyError: 'f.n'\n \"\"\"\nif not self.hasattr(\"default_factory\"): # did not call super().__init__() in sub-class\nself.setattr(\"default_factory\", Config)\nif name in self or not self.getattr(\"frozen\", False):\nreturn super().get(name, default, fallback)\nraise KeyError(name)\n
"},{"location":"config/#chanfig.config.Config.lock","title":"lock(recursive=True)
","text":"Alias of freeze
.
chanfig/config.py
Pythondef lock(self, recursive: bool = True) -> Config:\nr\"\"\"\n Alias of [`freeze`][chanfig.Config.freeze].\n \"\"\"\nreturn self.freeze(recursive=recursive)\n
"},{"location":"config/#chanfig.config.Config.locked","title":"locked()
","text":"Context manager which temporarily locks Config
.
Examples:
Python Console Session>>> c = Config()\n>>> with c.locked():\n... c['i.d'] = 1013\nTraceback (most recent call last):\nValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n>>> c.i.d = 1013\n>>> c.dict()\n{'i': {'d': 1013}}\n
Source code in chanfig/config.py
Python@contextmanager\ndef locked(self):\n\"\"\"\n Context manager which temporarily locks `Config`.\n Examples:\n >>> c = Config()\n >>> with c.locked():\n ... c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.i.d = 1013\n >>> c.dict()\n {'i': {'d': 1013}}\n \"\"\"\nwas_frozen = self.getattr(\"frozen\", False)\ntry:\nself.freeze()\nyield self\nfinally:\nif not was_frozen:\nself.defrost()\n
"},{"location":"config/#chanfig.config.Config.parse","title":"parse(args=None, default_config=None, no_default_config_action='raise', boot=True)
","text":"Parse command-line arguments with ConfigParser
.
parse
will try to parse all command-line arguments, you don\u2019t need to pre-define them but typos may cause trouble.
By default, this method internally calls Config.boot()
. To disable this behaviour, set boot
to False
.
Parameters:
Name Type Description Defaultargs
Iterable[str] | None
Command-line arguments. Defaults to None
.
None
default_config
str | None
Path to default config file. Defaults to None
.
None
no_default_config_action
str
Action when default_config
is not found. Can be one of [\"raise\", \"warn\", \"ignore\"]
. Defaults to \"raise\"
.
'raise'
boot
bool
If True
, call Config.boot()
after parsing. Defaults to True
.
True
See Also chanfig.ConfigParser.parse
: Implementation of parse
. parse_config
: Only parse valid config arguments.
Examples:
Python Console Session>>> c = Config(a=0)\n>>> c.dict()\n{'a': 0}\n>>> c.parse(['--a', '1', '--b', '2', '--c', '3']).dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/config.py
Pythondef parse(\nself,\nargs: Iterable[str] | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\nboot: bool = True,\n) -> Config:\nr\"\"\"\n Parse command-line arguments with `ConfigParser`.\n `parse` will try to parse all command-line arguments,\n you don't need to pre-define them but typos may cause trouble.\n By default, this method internally calls `Config.boot()`.\n To disable this behaviour, set `boot` to `False`.\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `None`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n boot (bool, optional): If `True`, call `Config.boot()` after parsing. Defaults to `True`.\n See Also:\n [`chanfig.ConfigParser.parse`][chanfig.ConfigParser.parse]: Implementation of `parse`.\n [`parse_config`][chanfig.Config.parse_config]: Only parse valid config arguments.\n Examples:\n >>> c = Config(a=0)\n >>> c.dict()\n {'a': 0}\n >>> c.parse(['--a', '1', '--b', '2', '--c', '3']).dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nself.getattr(\"parser\").parse(args, self, default_config, no_default_config_action)\nif boot:\nself.boot()\nreturn self\n
"},{"location":"config/#chanfig.config.Config.parse_config","title":"parse_config(args=None, default_config=None, no_default_config_action='raise', boot=True)
","text":"Parse command-line arguments with ConfigParser
.
parse_config
only parse command-line arguments that is in defined in Config
.
By default, this method internally calls Config.boot()
. To disable this behaviour, set boot
to False
.
Parameters:
Name Type Description Defaultargs
Iterable[str] | None
Command-line arguments. Defaults to None
.
None
default_config
str | None
Path to default config file. Defaults to None
.
None
no_default_config_action
str
Action when default_config
is not found. Can be one of [\"raise\", \"warn\", \"ignore\"]
. Defaults to \"raise\"
.
'raise'
boot
bool
If True
, call Config.boot()
after parsing. Defaults to True
.
True
See Also chanfig.ConfigParser.parse_config
: Implementation of parse_config
. parse
: Parse all command-line arguments.
Examples:
Python Console Session>>> c = Config(a=0, b=0, c=0)\n>>> c.dict()\n{'a': 0, 'b': 0, 'c': 0}\n>>> c.parse_config(['--a', '1', '--b', '2', '--c', '3']).dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/config.py
Pythondef parse_config(\nself,\nargs: Iterable[str] | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\nboot: bool = True,\n) -> Config:\nr\"\"\"\n Parse command-line arguments with `ConfigParser`.\n `parse_config` only parse command-line arguments that is in defined in `Config`.\n By default, this method internally calls `Config.boot()`.\n To disable this behaviour, set `boot` to `False`.\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `None`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n boot (bool, optional): If `True`, call `Config.boot()` after parsing. Defaults to `True`.\n See Also:\n [`chanfig.ConfigParser.parse_config`][chanfig.ConfigParser.parse_config]: Implementation of `parse_config`.\n [`parse`][chanfig.Config.parse]: Parse all command-line arguments.\n Examples:\n >>> c = Config(a=0, b=0, c=0)\n >>> c.dict()\n {'a': 0, 'b': 0, 'c': 0}\n >>> c.parse_config(['--a', '1', '--b', '2', '--c', '3']).dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nif not self.hasattr(\"parser\"):\nself.setattr(\"parser\", ConfigParser())\nself.getattr(\"parser\").parse_config(args, self, default_config, no_default_config_action)\nif boot:\nself.boot()\nreturn self\n
"},{"location":"config/#chanfig.config.Config.pop","title":"pop(name, default=Null)
","text":"Pop value from Config
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
Null
Returns:
Name Type Descriptionvalue
Any
If Config
does not contain name
, return default
.
Examples:
Python Console Session>>> c = Config()\n>>> c['i.d'] = 1013\n>>> c.pop('i.d')\n1013\n>>> c.pop('i.d', True)\nTrue\n>>> c.freeze().dict()\n{'i': {}}\n>>> c['i.d'] = 1013\nTraceback (most recent call last):\nValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n>>> c.defrost().dict()\n{'i': {}}\n>>> c['i.d'] = 1013\n>>> c.pop('i.d')\n1013\n
Source code in chanfig/config.py
Python@frozen_check\ndef pop(self, name: Any, default: Any = Null) -> Any:\nr\"\"\"\n Pop value from `Config`.\n Args:\n name:\n default:\n Returns:\n value: If `Config` does not contain `name`, return `default`.\n Examples:\n >>> c = Config()\n >>> c['i.d'] = 1013\n >>> c.pop('i.d')\n 1013\n >>> c.pop('i.d', True)\n True\n >>> c.freeze().dict()\n {'i': {}}\n >>> c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.defrost().dict()\n {'i': {}}\n >>> c['i.d'] = 1013\n >>> c.pop('i.d')\n 1013\n \"\"\"\nreturn super().pop(name, default)\n
"},{"location":"config/#chanfig.config.Config.post","title":"post()
","text":"Post process of Config
.
Some Config
may need to do some post process after Config
is initialised. post
is provided for this lazy-initialisation purpose.
By default, post
calls interpolate
to perform variable interpolation.
Note that you should always call boot
to apply post
rather than calling post
directly, as boot
recursively call post
on sub-configs.
See Also: boot
Returns:
Name Type Descriptionself
Config | None
Examples:
Python Console Session>>> class PostConfig(Config):\n... def post(self):\n... if isinstance(self.data, str):\n... self.data = Config(feature=self.data, label=self.data)\n... return self\n>>> c = PostConfig(data=\"path\")\n>>> c.post()\nPostConfig(<class 'chanfig.config.Config'>,\n ('data'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n)\n
Source code in chanfig/config.py
Pythondef post(self) -> Config | None:\nr\"\"\"\n Post process of `Config`.\n Some `Config` may need to do some post process after `Config` is initialised.\n `post` is provided for this lazy-initialisation purpose.\n By default, `post` calls `interpolate` to perform variable interpolation.\n Note that you should always call `boot` to apply `post` rather than calling `post` directly,\n as `boot` recursively call `post` on sub-configs.\n See Also: [`boot`][chanfig.Config.boot]\n Returns:\n self:\n Examples:\n >>> class PostConfig(Config):\n ... def post(self):\n ... if isinstance(self.data, str):\n ... self.data = Config(feature=self.data, label=self.data)\n ... return self\n >>> c = PostConfig(data=\"path\")\n >>> c.post()\n PostConfig(<class 'chanfig.config.Config'>,\n ('data'): Config(<class 'chanfig.config.Config'>,\n ('feature'): 'path'\n ('label'): 'path'\n )\n )\n \"\"\"\nself.interpolate()\nself.validate()\nreturn self\n
"},{"location":"config/#chanfig.config.Config.set","title":"set(name, value, convert_mapping=None)
","text":"Set value of Config
.
Parameters:
Name Type Description Defaultname
Any
required value
Any
required convert_mapping
bool | None
Whether to convert Mapping
to NestedDict
. Defaults to self.convert_mapping.
None
Raises:
Type DescriptionValueError
If Config
is frozen.
Examples:
Python Console Session>>> c = Config()\n>>> c['i.d'] = 1013\n>>> c.i.d\n1013\n>>> c.freeze().dict()\n{'i': {'d': 1013}}\n>>> c['i.d'] = 1013\nTraceback (most recent call last):\nValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n>>> c.defrost().dict()\n{'i': {'d': 1013}}\n>>> c['i.d'] = 1013\n>>> c.i.d\n1013\n
Source code in chanfig/config.py
Python@frozen_check\ndef set(\nself,\nname: Any,\nvalue: Any,\nconvert_mapping: bool | None = None,\n) -> None:\nr\"\"\"\n Set value of `Config`.\n Args:\n name:\n value:\n convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n Defaults to self.convert_mapping.\n Raises:\n ValueError: If `Config` is frozen.\n Examples:\n >>> c = Config()\n >>> c['i.d'] = 1013\n >>> c.i.d\n 1013\n >>> c.freeze().dict()\n {'i': {'d': 1013}}\n >>> c['i.d'] = 1013\n Traceback (most recent call last):\n ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.\n >>> c.defrost().dict()\n {'i': {'d': 1013}}\n >>> c['i.d'] = 1013\n >>> c.i.d\n 1013\n \"\"\"\nreturn super().set(name, value, convert_mapping)\n
"},{"location":"config/#chanfig.config.Config.unlock","title":"unlock(recursive=True)
","text":"Alias of defrost
.
chanfig/config.py
Pythondef unlock(self, recursive: bool = True) -> Config:\nr\"\"\"\n Alias of [`defrost`][chanfig.Config.defrost].\n \"\"\"\nreturn self.defrost(recursive=recursive)\n
"},{"location":"config/#chanfig.config.Config.unlocked","title":"unlocked()
","text":"Context manager which temporarily unlocks Config
.
Examples:
Python Console Session>>> c = Config()\n>>> c.freeze().dict()\n{}\n>>> with c.unlocked():\n... c['i.d'] = 1013\n>>> c.defrost().dict()\n{'i': {'d': 1013}}\n
Source code in chanfig/config.py
Python@contextmanager\ndef unlocked(self):\n\"\"\"\n Context manager which temporarily unlocks `Config`.\n Examples:\n >>> c = Config()\n >>> c.freeze().dict()\n {}\n >>> with c.unlocked():\n ... c['i.d'] = 1013\n >>> c.defrost().dict()\n {'i': {'d': 1013}}\n \"\"\"\nwas_frozen = self.getattr(\"frozen\", False)\ntry:\nself.defrost()\nyield self\nfinally:\nif was_frozen:\nself.freeze()\n
"},{"location":"config/#chanfig.config.frozen_check","title":"frozen_check(func)
","text":"Decorator check if the object is frozen.
Source code inchanfig/config.py
Pythondef frozen_check(func: Callable):\nr\"\"\"\n Decorator check if the object is frozen.\n \"\"\"\n@wraps(func)\ndef decorator(self, *args: Any, **kwargs: Any):\nif self.getattr(\"frozen\", False):\nraise ValueError(\"Attempting to alter a frozen config. Run config.defrost() to defrost first.\")\nreturn func(self, *args, **kwargs)\nreturn decorator\n
"},{"location":"default_dict/","title":"DefaultDict","text":" Bases: FlatDict
DefaultDict
inherits from FlatDict
and incorporates support of default_factory
in the same manner as collections.defaultdict
. If default_factory is not None
, the value will be set to default_factory()
when you access a key that does not exist in DefaultDict
.
You may specify DefaultDict(default_factory=FlatDict)
when creating DefaultDict
or by calling dict.setattr('default_factory', FlatDict)
for existing DefaultDict
objects.
Note that just like collections.defaultdict
, default_factory()
is called without any arguments.
Attributes:
Name Type Descriptiondefault_factory
Optional[Callable]
Default factory for defaultdict behaviour.
Raises:
Type DescriptionTypeError
If default_factory
is not callable.
Unlike collections.defaultdict
, DefaultDict
will not automatically create entries when name starts and ends with __
. This is to avoid conflicts with Python magic methods. You can still creates them manually with DefaultDict.fromkeys
or DefaultDict.add
.
Examples:
Python Console Session>>> d = DefaultDict(list)\n>>> d.a.append(1)\n>>> d.a\n[1]\n>>> d = DefaultDict([])\nTraceback (most recent call last):\nTypeError: `default_factory=[]` must be Callable, but got <class 'list'>.\n
Source code in chanfig/default_dict.py
Pythonclass DefaultDict(FlatDict): # type: ignore\nr\"\"\"\n `DefaultDict` inherits from `FlatDict` and incorporates support of `default_factory`\n in the same manner as `collections.defaultdict`.\n If `default_factory is not None`, the value will be set to `default_factory()`\n when you access a key that does not exist in `DefaultDict`.\n You may specify `DefaultDict(default_factory=FlatDict)` when creating `DefaultDict` or\n by calling `dict.setattr('default_factory', FlatDict)` for existing `DefaultDict` objects.\n Note that just like `collections.defaultdict`, `default_factory()` is called without any arguments.\n Attributes:\n default_factory: Default factory for defaultdict behaviour.\n Raises:\n TypeError: If `default_factory` is not callable.\n Notes:\n Unlike `collections.defaultdict`, `DefaultDict` will not automatically create entries when name starts and ends\n with `__`. This is to avoid conflicts with Python magic methods.\n You can still creates them manually with `DefaultDict.fromkeys` or `DefaultDict.add`.\n Examples:\n >>> d = DefaultDict(list)\n >>> d.a.append(1)\n >>> d.a\n [1]\n >>> d = DefaultDict([])\n Traceback (most recent call last):\n TypeError: `default_factory=[]` must be Callable, but got <class 'list'>.\n \"\"\"\ndefault_factory: Optional[Callable] = None\ndef __init__( # pylint: disable=W1113\nself, default_factory: Callable | None = None, *args: Any, **kwargs: Any\n) -> None:\nsuper().__init__(*args, **kwargs)\nif default_factory is not None:\nif callable(default_factory):\nself.setattr(\"default_factory\", default_factory)\nelse:\nraise TypeError(\nf\"`default_factory={default_factory}` must be Callable, but got {type(default_factory)}.\"\n)\ndef __missing__(self, name: Any, default=Null) -> Any: # pylint: disable=R1710\nif default is Null:\nif self.getattr(\"default_factory\", None) in (None, Null) or (name.startswith(\"__\") and name.endswith(\"__\")):\nraise KeyError(name) from None\ndefault = self.getattr(\"default_factory\")()\nif isinstance(default, FlatDict):\ndefault.__dict__.update(self.__dict__)\nsuper().set(name, default)\nreturn default\ndef __repr__(self) -> str:\nif self.default_factory is None:\nreturn super().__repr__()\nsuper_repr = super().__repr__()[len(self.__class__.__name__) :] # noqa: E203\nif len(super_repr) == 2:\nreturn f\"{self.__class__.__name__}({self.default_factory}, )\"\nreturn f\"{self.__class__.__name__}({self.default_factory},\" + super_repr[1:]\ndef add(self, name: Any):\nr\"\"\"\n Add a new default factory to the dictionary.\n Args:\n name:\n Raises:\n ValueError: If `default_factory` is None.\n Examples:\n >>> d = DefaultDict(default_factory=DefaultDict)\n >>> d.add('d')\n DefaultDict()\n >>> d.get('d')\n DefaultDict()\n >>> d['n'] = 'chang'\n >>> d.n\n 'chang'\n >>> d.n = 'liu'\n >>> d['n']\n 'liu'\n >>> d = DefaultDict()\n >>> d.add('a')\n Traceback (most recent call last):\n ValueError: Cannot add to a DefaultDict with no default_factory\n \"\"\"\nif self.default_factory is None:\nraise ValueError(\"Cannot add to a DefaultDict with no default_factory\")\nself.set(name, self.default_factory()) # pylint: disable=E1102\nreturn self.get(name)\n
"},{"location":"default_dict/#chanfig.DefaultDict.add","title":"add(name)
","text":"Add a new default factory to the dictionary.
Parameters:
Name Type Description Defaultname
Any
required Raises:
Type DescriptionValueError
If default_factory
is None.
Examples:
Python Console Session>>> d = DefaultDict(default_factory=DefaultDict)\n>>> d.add('d')\nDefaultDict()\n>>> d.get('d')\nDefaultDict()\n>>> d['n'] = 'chang'\n>>> d.n\n'chang'\n>>> d.n = 'liu'\n>>> d['n']\n'liu'\n>>> d = DefaultDict()\n>>> d.add('a')\nTraceback (most recent call last):\nValueError: Cannot add to a DefaultDict with no default_factory\n
Source code in chanfig/default_dict.py
Pythondef add(self, name: Any):\nr\"\"\"\n Add a new default factory to the dictionary.\n Args:\n name:\n Raises:\n ValueError: If `default_factory` is None.\n Examples:\n >>> d = DefaultDict(default_factory=DefaultDict)\n >>> d.add('d')\n DefaultDict()\n >>> d.get('d')\n DefaultDict()\n >>> d['n'] = 'chang'\n >>> d.n\n 'chang'\n >>> d.n = 'liu'\n >>> d['n']\n 'liu'\n >>> d = DefaultDict()\n >>> d.add('a')\n Traceback (most recent call last):\n ValueError: Cannot add to a DefaultDict with no default_factory\n \"\"\"\nif self.default_factory is None:\nraise ValueError(\"Cannot add to a DefaultDict with no default_factory\")\nself.set(name, self.default_factory()) # pylint: disable=E1102\nreturn self.get(name)\n
"},{"location":"flat_dict/","title":"FlatDict","text":" Bases: dict
FlatDict
with attribute-style access.
FlatDict
inherits from built-in dict
.
It comes with many easy to use helper methods, such as merge
, sort
, difference
, intersect
.
It also has full support for IO operations, such as json
and yaml
.
Even better, FlatDict
has pytorch support built-in. You can directly call FlatDict.cpu()
or FlatDict.to(\"cpu\")
to move all torch.Tensor
objects across devices.
FlatDict
works best with Variable
objects. Simply call flat_dict.a = Variable(1); flat_dict.b = flat_dict.a
, and their values will be synced.
Even better, FlatDict
support variable interpolation. Just set the value of one key to another key (surrounded by braces with $ at the begin, like ${xxx}), and calls flat_dict.interpolate()
, FlatDict
will interpolate their values and create Variable
automatically.
FlatDict
has many other easy to use helper methods, such as difference
, intersect
. And has full support for IO operations, such as json
and yaml
.
FlatDict
also has pytorch support built-in. You can directly call flat_dict.cpu()
or flat_dict.to(\"cpu\")
to move all torch.Tensor
objects across devices.
Attributes:
Name Type Descriptionindent
int
Indentation level in printing and dumping to json or yaml.
NotesFlatDict
rewrite __getattribute__
and __getattr__
to supports attribute-style access to its members. Therefore, all internal attributes should be set and get through flat_dict.setattr
and flat_dict.getattr
.
Although it is possible to override other internal methods, it is not recommended to do so.
__class__
, __dict__
, and getattr
are reserved and cannot be overrode in any manner.
Examples:
Python Console Session>>> d = FlatDict()\n>>> d.d = 1013\n>>> d['d']\n1013\n>>> d['i'] = 1013\n>>> d.i\n1013\n>>> d.a = Variable(1)\n>>> d.b = d.a\n>>> d.a, d.b\n(1, 1)\n>>> d.a += 1\n>>> d.a, d.b\n(2, 2)\n>>> d.a = 3\n>>> d.a, d.b\n(3, 3)\n>>> d.a = Variable('hello')\n>>> f\"{d.a}, world!\"\n'hello, world!'\n>>> d.a = d.a + ', world!'\n>>> d.b\n'hello, world!'\n
Source code in chanfig/flat_dict.py
Pythonclass FlatDict(dict, metaclass=Dict): # type: ignore\nr\"\"\"\n `FlatDict` with attribute-style access.\n `FlatDict` inherits from built-in `dict`.\n It comes with many easy to use helper methods, such as `merge`, `sort`, `difference`, `intersect`.\n It also has full support for IO operations, such as `json` and `yaml`.\n Even better, `FlatDict` has pytorch support built-in.\n You can directly call `FlatDict.cpu()` or `FlatDict.to(\"cpu\")` to move all `torch.Tensor` objects across devices.\n `FlatDict` works best with `Variable` objects.\n Simply call `flat_dict.a = Variable(1); flat_dict.b = flat_dict.a`, and their values will be synced.\n Even better, `FlatDict` support variable interpolation.\n Just set the value of one key to another key (surrounded by braces with $ at the begin, like ${xxx}),\n and calls `flat_dict.interpolate()`, `FlatDict` will interpolate their values and create `Variable` automatically.\n `FlatDict` has many other easy to use helper methods, such as `difference`, `intersect`.\n And has full support for IO operations, such as `json` and `yaml`.\n `FlatDict` also has pytorch support built-in.\n You can directly call `flat_dict.cpu()` or `flat_dict.to(\"cpu\")` to move all `torch.Tensor` objects across devices.\n Attributes:\n indent: Indentation level in printing and dumping to json or yaml.\n Notes:\n `FlatDict` rewrite `__getattribute__` and `__getattr__` to supports attribute-style access to its members.\n Therefore, all internal attributes should be set and get through `flat_dict.setattr` and `flat_dict.getattr`.\n Although it is possible to override other internal methods, it is not recommended to do so.\n `__class__`, `__dict__`, and `getattr` are reserved and cannot be overrode in any manner.\n Examples:\n >>> d = FlatDict()\n >>> d.d = 1013\n >>> d['d']\n 1013\n >>> d['i'] = 1013\n >>> d.i\n 1013\n >>> d.a = Variable(1)\n >>> d.b = d.a\n >>> d.a, d.b\n (1, 1)\n >>> d.a += 1\n >>> d.a, d.b\n (2, 2)\n >>> d.a = 3\n >>> d.a, d.b\n (3, 3)\n >>> d.a = Variable('hello')\n >>> f\"{d.a}, world!\"\n 'hello, world!'\n >>> d.a = d.a + ', world!'\n >>> d.b\n 'hello, world!'\n \"\"\"\n# pylint: disable=R0904\nindent: int = 2\ndef __post_init__(self, *args, **kwargs) -> None:\npass\ndef __getattribute__(self, name: Any) -> Any:\nif (name not in (\"getattr\",) and not (name.startswith(\"__\") and name.endswith(\"__\"))) and name in self:\nreturn self.get(name)\nreturn super().__getattribute__(name)\ndef get(self, name: Any, default: Any = None) -> Any:\nr\"\"\"\n Get value from `FlatDict`.\n Args:\n name:\n default:\n Returns:\n value:\n If `FlatDict` does not contain `name`, return `default`.\n Raises:\n KeyError: If `FlatDict` does not contain `name` and `default` is not specified.\n TypeError: If `name` is not hashable.\n Examples:\n >>> d = FlatDict(d=1013)\n >>> d.get('d')\n 1013\n >>> d['d']\n 1013\n >>> d.d\n 1013\n >>> d.get('d', None)\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.get('f')\n >>> d.get('f', Null)\n Traceback (most recent call last):\n KeyError: 'f'\n \"\"\"\nif name in self:\nreturn dict.__getitem__(self, name)\nif default is not Null:\nreturn default\nreturn self.__missing__(name)\ndef __getitem__(self, name: Any) -> Any:\nreturn self.get(name, default=Null)\ndef __getattr__(self, name: Any) -> Any:\ntry:\nreturn self.get(name, default=Null)\nexcept KeyError:\nraise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\ndef set(self, name: Any, value: Any) -> None:\nr\"\"\"\n Set value of `FlatDict`.\n Args:\n name:\n value:\n Examples:\n >>> d = FlatDict()\n >>> d.set('d', 1013)\n >>> d.get('d')\n 1013\n >>> d['n'] = 'chang'\n >>> d.n\n 'chang'\n >>> d.n = 'liu'\n >>> d['n']\n 'liu'\n \"\"\"\nif name in self and isinstance(self.get(name), Variable):\nself.get(name).set(value)\nelse:\ndict.__setitem__(self, name, value)\ndef __setitem__(self, name: Any, value: Any) -> None:\nself.set(name, value)\ndef __setattr__(self, name: Any, value: Any) -> None:\nself.set(name, value)\ndef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `FlatDict`.\n Args:\n name:\n Examples:\n >>> d = FlatDict(d=1016, n='chang')\n >>> d.d\n 1016\n >>> d.n\n 'chang'\n >>> d.delete('d')\n >>> d.d\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'd'\n >>> del d.n\n >>> d.n\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'n'\n >>> del d.f\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'f'\n \"\"\"\ndict.__delitem__(self, name)\ndef __delitem__(self, name: Any) -> None:\nreturn self.delete(name)\ndef __delattr__(self, name: Any) -> None:\ntry:\nself.delete(name)\nexcept KeyError:\nraise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\ndef __missing__(self, name: Any) -> Any: # pylint: disable=R1710\nraise KeyError(name)\ndef validate(self) -> None:\nr\"\"\"\n Validate `FlatDict`.\n Raises:\n TypeError: If value is not of the type declared in class annotations.\n TypeError: If `Variable` has invalid type.\n ValueError: If `Variable` has invalid value.\n Examples:\n >>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.islower()))\n >>> d = FlatDict(d=Variable(1016, type=str), n=Variable('chang', validator=lambda x: x.islower()))\n Traceback (most recent call last):\n TypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n >>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.isupper()))\n Traceback (most recent call last):\n ValueError: 'n' has invalid value. Value chang is not valid.\n \"\"\"\nself._validate(self)\n@staticmethod\ndef _validate(obj) -> None:\nif isinstance(obj, FlatDict):\nannotations = get_annotations(obj)\nfor name, value in obj.items():\nif annotations and name in annotations and not isvalid(value, annotations[name]):\nraise TypeError(f\"'{name}' has invalid type. Value {value} is not of type {annotations[name]}.\")\nif isinstance(value, Variable):\ntry:\nvalue.validate()\nexcept TypeError as exc:\nraise TypeError(f\"'{name}' has invalid type. {exc}\") from None\nexcept ValueError as exc:\nraise ValueError(f\"'{name}' has invalid value. {exc}\") from None\ndef getattr(self, name: str, default: Any = Null) -> Any:\nr\"\"\"\n Get attribute of `FlatDict`.\n Note that it won't retrieve value in `FlatDict`,\n Args:\n name:\n default:\n Returns:\n value: If `FlatDict` does not contain `name`, return `default`.\n Raises:\n AttributeError: If `FlatDict` does not contain `name` and `default` is not specified.\n Examples:\n >>> d = FlatDict(a=1)\n >>> d.get('a')\n 1\n >>> d.getattr('a')\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'a'\n >>> d.getattr('b', 2)\n 2\n >>> d.setattr('b', 3)\n >>> d.getattr('b')\n 3\n \"\"\"\ntry:\nif name in self.__dict__:\nreturn self.__dict__[name]\nif name in self.__class__.__dict__:\nreturn self.__class__.__dict__[name]\nreturn super().getattr(name, default) # type: ignore\nexcept AttributeError:\nif default is not Null:\nreturn default\nraise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\ndef setattr(self, name: str, value: Any) -> None:\nr\"\"\"\n Set attribute of `FlatDict`.\n Note that it won't alter values in `FlatDict`.\n Args:\n name:\n value:\n Warns:\n RuntimeWarning: If name already exists in `FlatDict`.\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('attr', 'value')\n >>> d.getattr('attr')\n 'value'\n >>> d.set('d', 1013)\n >>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict.\n >>> d.get('d')\n 1013\n >>> d.d\n 1013\n >>> d.getattr('d')\n 1031\n \"\"\"\nif name in self:\nwarn(\nf\"{name} already exists in {self.__class__.__name__}.\\n\"\nf\"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.\",\nRuntimeWarning,\n)\nself.__dict__[name] = value\ndef delattr(self, name: str) -> None:\nr\"\"\"\n Delete attribute of `FlatDict`.\n Note that it won't delete values in `FlatDict`.\n Args:\n name:\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('name', 'chang')\n >>> d.getattr('name')\n 'chang'\n >>> d.delattr('name')\n >>> d.getattr('name')\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'name'\n \"\"\"\ndel self.__dict__[name]\ndef hasattr(self, name: str) -> bool:\nr\"\"\"\n Determine if an attribute exists in `FlatDict`.\n Args:\n name:\n Returns:\n (bool):\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('name', 'chang')\n >>> d.hasattr('name')\n True\n >>> d.delattr('name')\n >>> d.hasattr('name')\n False\n \"\"\"\ntry:\nif name in self.__dict__ or name in self.__class__.__dict__:\nreturn True\nreturn super().hasattr(name) # type: ignore\nexcept AttributeError:\nreturn False\ndef dict(self, cls: Callable = dict) -> Mapping:\nr\"\"\"\n Convert `FlatDict` to other `Mapping`.\n Args:\n cls: Target class to be converted to.\n Returns:\n (Mapping):\n See Also:\n [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nreturn cls(to_dict(self))\n@classmethod\ndef from_dict(cls, obj: Mapping | Sequence) -> Any: # pylint: disable=R0911\nr\"\"\"\n Convert `Mapping` or `Sequence` to `FlatDict`.\n Examples:\n >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})\n FlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])\n FlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n >>> FlatDict.from_dict({1, 2, 3})\n Traceback (most recent call last):\n TypeError: Expected Mapping or Sequence, but got <class 'set'>.\n \"\"\"\nif obj is None:\nreturn cls()\nif issubclass(cls, FlatDict):\ncls = cls.empty # type: ignore # pylint: disable=W0642\nif isinstance(obj, Mapping):\nreturn cls(obj)\nif isinstance(obj, Sequence):\ntry:\nreturn cls(obj)\nexcept ValueError:\nreturn [cls(json) for json in obj]\nraise TypeError(f\"Expected Mapping or Sequence, but got {type(obj)}.\")\ndef sort(self, key: Callable | None = None, reverse: bool = False) -> FlatDict:\nr\"\"\"\n Sort `FlatDict`.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.sort().dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d = FlatDict(b=2, c=3, a=1)\n >>> d.sort().dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> a = [1]\n >>> d = FlatDict(z=0, a=a)\n >>> a.append(2)\n >>> d.sort().dict()\n {'a': [1, 2], 'z': 0}\n \"\"\"\nitems = sorted(self.items(), key=key, reverse=reverse)\nself.clear()\nfor k, v in items:\nself[k] = v\nreturn self\ndef interpolate(self, use_variable: bool = True, interpolators: MutableMapping | None = None) -> FlatDict:\nr\"\"\"\n Perform Variable interpolation.\n Variable interpolation allows you to set the value of one key to be the value of another key easily.\n Args:\n use_variable: Whether to convert values to `Variable` objects.\n interpolators: Mapping contains values for interpolation. Defaults to `self`.\n Raises:\n ValueError: If value is not interpolatable.\n ValueError: If reference to itself.\n ValueError: If has circular reference.\n See Also:\n [Variable][`chanfig.Variable`]: Mutable wrapper of immutable objects.\n Examples:\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${a}.${b}\")\n >>> d.dict()\n {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}\n >>> d.interpolate().dict()\n {'a': 1, 'b': 1, 'c': '1.1'}\n >>> isinstance(d.a, Variable)\n True\n >>> d.a += 1\n >>> d.dict()\n {'a': 2, 'b': 2, 'c': '1.1'}\n >>> d.a is d.b\n True\n >>> d.b is d.c\n False\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${b}\")\n >>> d.dict()\n {'a': 1, 'b': '${a}', 'c': '${b}'}\n >>> d.interpolate(False).dict()\n {'a': 1, 'b': 1, 'c': 1}\n >>> isinstance(d.a, Variable)\n False\n >>> d.a += 1\n >>> d.dict()\n {'a': 2, 'b': 1, 'c': 1}\n >>> d = FlatDict(a=1, b=\"${b}\", c=\"${b}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: Cannot interpolate b to itself.\n >>> d = FlatDict(a=\"${b}\", b=\"${c}\", c=\"${d}\", d=\"${a}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: Circular reference found: a->b->c->d->a.\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${d}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: d is not found in FlatDict(\n ('a'): '1'\n ('b'): '${a}'\n ('c'): '${d}'\n ).\n \"\"\"\ninterpolators = interpolators or self\nplaceholders: dict[str, list[str]] = {}\nfor key, value in self.all_items():\nif isinstance(value, list):\nfor v in value:\nself.find_placeholders(key, v, placeholders)\nelif isinstance(value, Mapping):\nfor v in value.values():\nself.find_placeholders(key, v, placeholders)\nelse:\nself.find_placeholders(key, value, placeholders)\ncircular_references = find_circular_reference(placeholders)\nif circular_references:\nraise ValueError(f\"Circular reference found: {'->'.join(circular_references)}.\")\nif use_variable:\nplaceholder_names = {i for j in placeholders.values() for i in j}\nfor name in list(placeholder_names.difference(placeholders.keys())):\nif name not in interpolators:\nraise ValueError(f\"{name} is not found in {interpolators}.\")\nif not isinstance(interpolators[name], Variable):\ninterpolators[name] = Variable(interpolators[name])\nfor key, value in placeholders.items():\nif isinstance(self[key], list):\nfor index, v in enumerate(self[key]):\nself[key][index] = self.substitute(v, interpolators, value)\nelif isinstance(self[key], Mapping):\nfor k, v in self[key].items():\nself[key][k] = self.substitute(v, interpolators, value)\nelse:\nself[key] = self.substitute(self[key], interpolators, value)\nreturn self\n@staticmethod\ndef find_placeholders(key, value, placeholders):\nplaceholder = find_placeholders(value)\nif placeholder:\nfor index, name in enumerate(placeholder):\nif name.startswith(\".\"):\nplaceholder[index] = key.rsplit(\".\", 1)[0] + name\nif key == name:\nraise ValueError(f\"Cannot interpolate {key} to itself.\")\nplaceholders[key] = placeholder\n@staticmethod\ndef substitute(placeholder, interpolators, value):\ntry:\nif len(value) == 1 and placeholder.startswith(\"${\") and placeholder.endswith(\"}\"):\nreturn interpolators[value[0]]\nreturn placeholder.replace(\"$\", \"\").format(**interpolators)\nexcept KeyError as exc:\nraise ValueError(f\"{exc} is not found in {interpolators}.\") from None\ndef merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Merge `other` into `FlatDict`.\n Args:\n *args: `Mapping` or `Sequence` to be merged.\n overwrite: Whether to overwrite existing values.\n **kwargs: `Mapping` to be merged.\n Returns:\n self:\n **Alias**:\n + `union`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.merge(n).dict()\n {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.merge(l).dict()\n {'a': 1, 'b': 'b', 'c': 3, 'd': 4}\n >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias\n {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d = FlatDict()\n >>> d.merge({1: 1, 2: 2, 3:3}).dict()\n {1: 1, 2: 2, 3: 3}\n >>> d.merge(d.clone()).dict()\n {1: 1, 2: 2, 3: 3}\n >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()\n {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}\n \"\"\"\nif len(args) == 1:\nargs = args[0]\nif isinstance(args, (PathLike, str, bytes)):\nargs = self.load(args) # type: ignore\nwarn(\n\"merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.\",\nPendingDeprecationWarning,\n)\nself._merge(self, args, overwrite=overwrite)\nelif len(args) > 1:\nself._merge(self, args, overwrite=overwrite)\nif kwargs:\nself._merge(self, kwargs, overwrite=overwrite)\nreturn self\n@staticmethod\ndef _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:\nif not that:\nreturn this\nif isinstance(that, Mapping):\nthat = that.items()\nfor key, value in that:\nif key in this and isinstance(this[key], Mapping):\nif isinstance(value, Mapping):\nFlatDict._merge(this[key], value)\nelif overwrite:\nif isinstance(value, FlatDict):\nthis.set(key, value)\nelse:\nthis[key] = value\nelif overwrite or key not in this:\nthis.set(key, value)\nreturn this\ndef union(self, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`merge`][chanfig.FlatDict.merge].\n \"\"\"\nreturn self.merge(*args, **kwargs)\ndef merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Merge content of `file` into `FlatDict`.\n Args:\n file (File):\n *args: Passed to [`load`][chanfig.FlatDict.load].\n **kwargs: Passed to [`load`][chanfig.FlatDict.load].\n Returns:\n self:\n Examples:\n >>> d = FlatDict(a=1, b=1)\n >>> d.merge_from_file(\"tests/test.yaml\").dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nreturn self.merge(self.load(file, *args, **kwargs))\ndef intersect(self, other: Mapping | Iterable | PathStr) -> FlatDict:\nr\"\"\"\n Intersection of `FlatDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n Returns:\n (FlatDict):\n **Alias**:\n + `inter`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.intersect(n).dict()\n {}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.intersect(l).dict()\n {'c': 3}\n >>> d.merge(l).intersect(\"tests/test.yaml\").dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d.intersect(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n >>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias\n {}\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(**{key: value for key, value in other if key in self and self[key] == value}) # type: ignore\ndef inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`intersect`][chanfig.FlatDict.intersect].\n \"\"\"\nreturn self.intersect(other, *args, **kwargs)\ndef difference(self, other: Mapping | Iterable | PathStr) -> FlatDict:\nr\"\"\"\n Difference between `FlatDict` and `other`.\n Args:\n other:\n Returns:\n (FlatDict):\n **Alias**:\n + `diff`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.difference(n).dict()\n {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.difference(l).dict()\n {'d': 4}\n >>> d.merge(l).difference(\"tests/test.yaml\").dict()\n {}\n >>> d.difference(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias\n {'b': 'b', 'c': 'c', 'd': 'd'}\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(\n**{key: value for key, value in other if key not in self or self[key] != value} # type: ignore\n)\ndef diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`difference`][chanfig.FlatDict.difference].\n \"\"\"\nreturn self.difference(other, *args, **kwargs)\ndef to(self, cls: str | TorchDevice | TorchDType) -> FlatDict: # pragma: no cover\nr\"\"\"\n Convert values of `FlatDict` to target `cls`.\n Args:\n cls (str | torch.device | torch.dtype):\n Returns:\n self:\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.to(int)\n Traceback (most recent call last):\n TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.\n \"\"\"\n# pylint: disable=C0103\nif isinstance(cls, (str, TorchDevice, TorchDType)):\nfor k, v in self.all_items():\nif hasattr(v, \"to\"):\nself[k] = v.to(cls)\nreturn self\nraise TypeError(f\"to() only support torch.dtype and torch.device, but got {cls}.\")\ndef cpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to cpu.\n Returns:\n self:\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.cpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='cpu')}\n \"\"\"\nreturn self.to(TorchDevice(\"cpu\"))\ndef gpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to gpu.\n Returns:\n self:\n **Alias**:\n + `cuda`\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.gpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='cuda:0')}\n >>> d.cuda().dict() # alias # doctest: +SKIP\n {'a': tensor(1, device='cuda:0')}\n \"\"\"\nreturn self.to(TorchDevice(\"cuda\"))\ndef cuda(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Alias of [`gpu`][chanfig.FlatDict.gpu].\n \"\"\"\nreturn self.gpu()\ndef tpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to tpu.\n Returns:\n self:\n **Alias**:\n + `xla`\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.tpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='xla:0')}\n >>> d.xla().dict() # alias # doctest: +SKIP\n {'a': tensor(1, device='xla:0')}\n \"\"\"\nreturn self.to(TorchDevice(\"xla\"))\ndef xla(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Alias of [`tpu`][chanfig.FlatDict.tpu].\n \"\"\"\nreturn self.tpu()\ndef copy(self) -> FlatDict:\nr\"\"\"\n Create a shallow copy of `FlatDict`.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.copy()\n >>> c.dict()\n {'a': []}\n >>> d.a.append(1)\n >>> c.dict()\n {'a': [1]}\n >>> c.getattr(\"name\")\n 'Chang'\n \"\"\"\nreturn copy(self)\ndef __deepcopy__(self, memo: Mapping | None = None) -> FlatDict:\n# pylint: disable=C0103\nif memo is not None and id(self) in memo:\nreturn memo[id(self)]\nret = self.empty()\nret.__dict__.update(deepcopy(self.__dict__))\nfor k, v in self.items():\nif isinstance(v, FlatDict):\nret[k] = v.deepcopy(memo=memo)\nelse:\nret[k] = deepcopy(v)\nreturn ret\ndef deepcopy(self, memo: Mapping | None = None) -> FlatDict: # pylint: disable=W0613\nr\"\"\"\n Create a deep copy of `FlatDict`.\n Returns:\n (FlatDict):\n **Alias**:\n + `clone`\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.deepcopy()\n >>> c.dict()\n {'a': []}\n >>> d.a.append(1)\n >>> c.dict()\n {'a': []}\n >>> c.getattr(\"name\")\n 'Chang'\n >>> d == d.clone() # alias\n True\n \"\"\"\nreturn deepcopy(self)\ndef clone(self, memo: Mapping | None = None) -> FlatDict:\nr\"\"\"\n Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].\n \"\"\"\nreturn self.deepcopy(memo=memo)\ndef save(self, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Save `FlatDict` to file.\n Raises:\n ValueError: If save to `IO` and `method` is not specified.\n TypeError: If save to unsupported extension.\n **Alias**:\n + `save`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.save(\"tests/test.yaml\")\n >>> d.save(\"test.conf\")\n Traceback (most recent call last):\n TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"test.yaml\", \"w\") as f:\n ... d.save(f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when saving to IO.\n \"\"\"\nif method is None:\nif isinstance(file, (IOBase, IO)):\nraise ValueError(\"`method` must be specified when saving to IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in YAML:\nreturn self.yaml(file=file, *args, **kwargs) # type: ignore\nif extension in JSON:\nreturn self.json(file=file, *args, **kwargs) # type: ignore\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\ndef dump(self, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Alias of [`save`][chanfig.FlatDict.save].\n \"\"\"\nreturn self.save(file, method, *args, **kwargs)\n@classmethod\ndef load( # pylint: disable=W1113\ncls, file: File, method: str | None = None, *args: Any, **kwargs: Any\n) -> FlatDict:\n\"\"\"\n Load `FlatDict` from file.\n Args:\n file: File to load from.\n method: File type, should be in `JSON` or `YAML`.\n Returns:\n (FlatDict):\n Raises:\n ValueError: If load from `IO` and `method` is not specified.\n TypeError: If dump to unsupported extension.\n Examples:\n >>> d = FlatDict.load(\"tests/test.yaml\")\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d.load(\"tests/test.conf\")\n Traceback (most recent call last):\n TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"tests/test.yaml\") as f:\n ... d.load(f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when loading from IO.\n \"\"\"\nif method is None:\nif isinstance(file, (IOBase, IO)):\nraise ValueError(\"`method` must be specified when loading from IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in JSON:\nreturn cls.from_json(file, *args, **kwargs)\nif extension in YAML:\nreturn cls.from_yaml(file, *args, **kwargs)\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\ndef json(self, file: File, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Dump `FlatDict` to json file.\n This method internally calls `self.jsons()` to generate json string.\n You may overwrite `jsons` in case something is not json serializable.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.json(\"tests/test.json\")\n \"\"\"\nwith self.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nfp.write(self.jsons(*args, **kwargs))\n@classmethod\ndef from_json(cls, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from json file.\n This method internally calls `self.from_jsons()` to construct object from json string.\n You may overwrite `from_jsons` in case something is not json serializable.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict.from_json('tests/test.json')\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nwith cls.open(file) as fp: # pylint: disable=C0103\nif isinstance(file, (IOBase, IO)):\nreturn cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore\nreturn cls.from_jsons(fp.read(), *args, **kwargs)\ndef jsons(self, *args: Any, **kwargs: Any) -> str:\nr\"\"\"\n Dump `FlatDict` to json string.\n Returns:\n (str):\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.jsons()\n '{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}'\n \"\"\"\nkwargs.setdefault(\"cls\", JsonEncoder)\nkwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\nreturn json_dumps(self.dict(), *args, **kwargs)\n@classmethod\ndef from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from json string.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_jsons('{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_jsons('[[\"a\", 1], [\"b\", 2], [\"c\", 3]]').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_jsons('[{\"a\": 1}, {\"b\": 2}, {\"c\": 3}]')\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n \"\"\"\nreturn cls.from_dict(json_loads(string, *args, **kwargs))\ndef yaml(self, file: File, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Dump `FlatDict` to yaml file.\n This method internally calls `self.yamls()` to generate yaml string.\n You may overwrite `yamls` in case something is not yaml serializable.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.yaml(\"tests/test.yaml\")\n \"\"\"\nwith self.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nself.yamls(fp, *args, **kwargs)\n@classmethod\ndef from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from yaml file.\n This method internally calls `self.from_yamls()` to construct object from yaml string.\n You may overwrite `from_yamls` in case something is not yaml serializable.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_yaml('tests/test.yaml').dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nkwargs.setdefault(\"Loader\", YamlLoader)\nwith cls.open(file) as fp: # pylint: disable=C0103\nif isinstance(file, (IOBase, IO)):\nreturn cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore\nreturn cls.from_dict(yaml_load(fp, *args, **kwargs))\ndef yamls(self, *args: Any, **kwargs: Any) -> str:\nr\"\"\"\n Dump `FlatDict` to yaml string.\n Returns:\n (str):\n Examples:\n >>> FlatDict(a=1, b=2, c=3).yamls()\n 'a: 1\\nb: 2\\nc: 3\\n'\n \"\"\"\nkwargs.setdefault(\"Dumper\", YamlDumper)\nkwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\nreturn yaml_dump(self.dict(), *args, **kwargs) # type: ignore\n@classmethod\ndef from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from yaml string.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_yamls('a: 1\\nb: 2\\nc: 3\\n').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_yamls('- - a\\n - 1\\n- - b\\n - 2\\n- - c\\n - 3\\n').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_yamls('- a: 1\\n- b: 2\\n- c: 3\\n')\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n \"\"\"\nkwargs.setdefault(\"Loader\", SafeLoader)\nreturn cls.from_dict(yaml_load(string, *args, **kwargs))\n@staticmethod\n@contextmanager\ndef open(file: File, *args: Any, encoding: str = \"utf-8\", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:\nr\"\"\"\n Open file IO from file path or IO.\n This methods extends the ability of built-in `open` by allowing it to accept an `IOBase` object.\n Args:\n file: File path or IO.\n *args: Additional arguments passed to `open`.\n Defaults to ().\n **kwargs: Any\n Additional keyword arguments passed to `open`.\n Defaults to {}.\n Yields:\n (Generator[IOBase | IO, Any, Any]):\n Examples:\n >>> with FlatDict.open(\"tests/test.yaml\") as fp:\n ... print(fp.read())\n a: 1\n b: 2\n c: 3\n <BLANKLINE>\n >>> io = open(\"tests/test.yaml\")\n >>> with FlatDict.open(io) as fp:\n ... print(fp.read())\n a: 1\n b: 2\n c: 3\n <BLANKLINE>\n >>> with FlatDict.open(123, mode=\"w\") as fp:\n ... print(fp.read())\n Traceback (most recent call last):\n TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int\n \"\"\"\nif isinstance(file, (IOBase, IO)):\nyield file\nelif isinstance(file, (PathLike, str, bytes)):\ntry:\nfile = open(file, *args, encoding=encoding, **kwargs) # type: ignore # noqa: SIM115\nyield file # type: ignore\nfinally:\nwith suppress(Exception):\nfile.close() # type: ignore\nelse:\nraise TypeError(f\"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}\")\n@classmethod\ndef empty(cls, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Initialise an empty `FlatDict`.\n This method is helpful when you inheriting `FlatDict` with default values defined in `__init__()`.\n As use `type(self)()` in this case would copy all the default values, which might not be desired.\n This method will preserve everything in `FlatDict.__class__.__dict__`.\n Returns:\n (FlatDict):\n See Also:\n [`empty_like`][chanfig.FlatDict.empty_like]\n Examples:\n >>> d = FlatDict(a=[])\n >>> c = d.empty()\n >>> c.dict()\n {}\n \"\"\"\nempty = cls.__new__(cls)\nempty.merge(*args, **kwargs) # pylint: disable=W0212\nreturn empty\ndef empty_like(self, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Initialise an empty copy of `FlatDict`.\n This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.\n For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this\n method.\n Returns:\n (FlatDict):\n See Also:\n [`empty`][chanfig.FlatDict.empty]\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.empty_like()\n >>> c.dict()\n {}\n >>> c.getattr(\"name\")\n 'Chang'\n \"\"\"\nempty = self.empty(*args, **kwargs)\nempty.__dict__.update(self.__dict__)\nreturn empty\ndef all_keys(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_keys`][chanfig.NestedDict.all_keys]\n \"\"\"\nyield from self.keys()\ndef all_values(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_values`][chanfig.NestedDict.all_values]\n \"\"\"\nyield from self.values()\ndef all_items(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_items`][chanfig.NestedDict.all_items]\n \"\"\"\nyield from self.items()\ndef dropnull(self) -> FlatDict:\nr\"\"\"\n Drop key-value pairs with `Null` value.\n Returns:\n (FlatDict):\n **Alias**:\n + `dropna`\n Examples:\n >>> d = FlatDict(a=Null, b=Null, c=3)\n >>> d.dict()\n {'a': Null, 'b': Null, 'c': 3}\n >>> d.dropnull().dict()\n {'c': 3}\n >>> d.dropna().dict() # alias\n {'c': 3}\n \"\"\"\nreturn self.empty({k: v for k, v in self.all_items() if v is not Null})\ndef dropna(self) -> FlatDict:\nr\"\"\"\n Alias of [`dropnull`][chanfig.FlatDict.dropnull].\n \"\"\"\nreturn self.dropnull()\n@staticmethod\ndef extra_repr() -> str: # pylint: disable=C0116\nreturn \"\"\ndef __repr__(self) -> str:\nextra_lines = []\nextra_repr = self.extra_repr()\n# empty string will be split into list ['']\nif extra_repr:\nextra_lines = extra_repr.split(\"\\n\")\nchild_lines = []\nfor key, value in self.items():\nkey_repr = repr(key)\nvalue_repr = repr(value)\nvalue_repr = self._add_indent(value_repr)\nchild_lines.append(f\"({key_repr}): {value_repr}\")\n# child_lines.append(f\"{key_repr}: {value_repr}\")\nlines = extra_lines + child_lines\nmain_repr = self.__class__.__name__ + \"(\"\nif lines:\n# simple one-liner info, which most builtin Modules will use\nif len(extra_lines) == 1 and not child_lines:\nmain_repr += extra_lines[0]\nelif len(child_lines) == 1 and not extra_lines and len(child_lines[0]) < 10:\nmain_repr += child_lines[0]\nelse:\nmain_repr += \"\\n \" + \"\\n \".join(lines) + \"\\n\"\nmain_repr += \")\"\nreturn main_repr\ndef _add_indent(self, text: str) -> str:\nlines = text.split(\"\\n\")\n# don't do anything for single-line stuff\nif len(lines) == 1:\nreturn text\nfirst = lines.pop(0)\nlines = [(self.getattr(\"indent\", 2) * \" \") + line for line in lines]\ntext = \"\\n\".join(lines)\ntext = first + \"\\n\" + text\nreturn text\ndef __format__(self, format_spec: str) -> str:\nreturn repr(self.empty({k: v.__format__(format_spec) for k, v in self.all_items()}))\ndef __hash__(self):\nreturn hash(frozenset(self.items()))\ndef _ipython_display_(self): # pragma: no cover\nreturn repr(self)\ndef _ipython_canary_method_should_not_exist_(self): # pragma: no cover\nreturn None\ndef aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf(self): # pragma: no cover\nreturn None\ndef __rich__(self): # pragma: no cover\nreturn self.__repr__()\n
"},{"location":"flat_dict/#chanfig.FlatDict.all_items","title":"all_items()
","text":"Equivalent to keys
.
This method is provided solely to make methods work on both FlatDict
and NestedDict
.
all_items
chanfig/flat_dict.py
Pythondef all_items(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_items`][chanfig.NestedDict.all_items]\n \"\"\"\nyield from self.items()\n
"},{"location":"flat_dict/#chanfig.FlatDict.all_keys","title":"all_keys()
","text":"Equivalent to keys
.
This method is provided solely to make methods work on both FlatDict
and NestedDict
.
all_keys
chanfig/flat_dict.py
Pythondef all_keys(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_keys`][chanfig.NestedDict.all_keys]\n \"\"\"\nyield from self.keys()\n
"},{"location":"flat_dict/#chanfig.FlatDict.all_values","title":"all_values()
","text":"Equivalent to keys
.
This method is provided solely to make methods work on both FlatDict
and NestedDict
.
all_values
chanfig/flat_dict.py
Pythondef all_values(self) -> Generator:\nr\"\"\"\n Equivalent to `keys`.\n This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n See Also:\n [`all_values`][chanfig.NestedDict.all_values]\n \"\"\"\nyield from self.values()\n
"},{"location":"flat_dict/#chanfig.FlatDict.clone","title":"clone(memo=None)
","text":"Alias of deepcopy
.
chanfig/flat_dict.py
Pythondef clone(self, memo: Mapping | None = None) -> FlatDict:\nr\"\"\"\n Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].\n \"\"\"\nreturn self.deepcopy(memo=memo)\n
"},{"location":"flat_dict/#chanfig.FlatDict.copy","title":"copy()
","text":"Create a shallow copy of FlatDict
.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> d = FlatDict(a=[])\n>>> d.setattr(\"name\", \"Chang\")\n>>> c = d.copy()\n>>> c.dict()\n{'a': []}\n>>> d.a.append(1)\n>>> c.dict()\n{'a': [1]}\n>>> c.getattr(\"name\")\n'Chang'\n
Source code in chanfig/flat_dict.py
Pythondef copy(self) -> FlatDict:\nr\"\"\"\n Create a shallow copy of `FlatDict`.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.copy()\n >>> c.dict()\n {'a': []}\n >>> d.a.append(1)\n >>> c.dict()\n {'a': [1]}\n >>> c.getattr(\"name\")\n 'Chang'\n \"\"\"\nreturn copy(self)\n
"},{"location":"flat_dict/#chanfig.FlatDict.cpu","title":"cpu()
","text":"Move all tensors to cpu.
Returns:
Name Type Descriptionself
FlatDict
Examples:
Python Console Session>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.cpu().dict()\n{'a': tensor(1, device='cpu')}\n
Source code in chanfig/flat_dict.py
Pythondef cpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to cpu.\n Returns:\n self:\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.cpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='cpu')}\n \"\"\"\nreturn self.to(TorchDevice(\"cpu\"))\n
"},{"location":"flat_dict/#chanfig.FlatDict.cuda","title":"cuda()
","text":"Alias of gpu
.
chanfig/flat_dict.py
Pythondef cuda(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Alias of [`gpu`][chanfig.FlatDict.gpu].\n \"\"\"\nreturn self.gpu()\n
"},{"location":"flat_dict/#chanfig.FlatDict.deepcopy","title":"deepcopy(memo=None)
","text":"Create a deep copy of FlatDict
.
Returns:
Type DescriptionFlatDict
Alias:
clone
Examples:
Python Console Session>>> d = FlatDict(a=[])\n>>> d.setattr(\"name\", \"Chang\")\n>>> c = d.deepcopy()\n>>> c.dict()\n{'a': []}\n>>> d.a.append(1)\n>>> c.dict()\n{'a': []}\n>>> c.getattr(\"name\")\n'Chang'\n>>> d == d.clone() # alias\nTrue\n
Source code in chanfig/flat_dict.py
Pythondef deepcopy(self, memo: Mapping | None = None) -> FlatDict: # pylint: disable=W0613\nr\"\"\"\n Create a deep copy of `FlatDict`.\n Returns:\n (FlatDict):\n **Alias**:\n + `clone`\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.deepcopy()\n >>> c.dict()\n {'a': []}\n >>> d.a.append(1)\n >>> c.dict()\n {'a': []}\n >>> c.getattr(\"name\")\n 'Chang'\n >>> d == d.clone() # alias\n True\n \"\"\"\nreturn deepcopy(self)\n
"},{"location":"flat_dict/#chanfig.FlatDict.delattr","title":"delattr(name)
","text":"Delete attribute of FlatDict
.
Note that it won\u2019t delete values in FlatDict
.
Parameters:
Name Type Description Defaultname
str
required Examples:
Python Console Session>>> d = FlatDict()\n>>> d.setattr('name', 'chang')\n>>> d.getattr('name')\n'chang'\n>>> d.delattr('name')\n>>> d.getattr('name')\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'name'\n
Source code in chanfig/flat_dict.py
Pythondef delattr(self, name: str) -> None:\nr\"\"\"\n Delete attribute of `FlatDict`.\n Note that it won't delete values in `FlatDict`.\n Args:\n name:\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('name', 'chang')\n >>> d.getattr('name')\n 'chang'\n >>> d.delattr('name')\n >>> d.getattr('name')\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'name'\n \"\"\"\ndel self.__dict__[name]\n
"},{"location":"flat_dict/#chanfig.FlatDict.delete","title":"delete(name)
","text":"Delete value from FlatDict
.
Parameters:
Name Type Description Defaultname
Any
required Examples:
Python Console Session>>> d = FlatDict(d=1016, n='chang')\n>>> d.d\n1016\n>>> d.n\n'chang'\n>>> d.delete('d')\n>>> d.d\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'd'\n>>> del d.n\n>>> d.n\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'n'\n>>> del d.f\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'f'\n
Source code in chanfig/flat_dict.py
Pythondef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `FlatDict`.\n Args:\n name:\n Examples:\n >>> d = FlatDict(d=1016, n='chang')\n >>> d.d\n 1016\n >>> d.n\n 'chang'\n >>> d.delete('d')\n >>> d.d\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'd'\n >>> del d.n\n >>> d.n\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'n'\n >>> del d.f\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'f'\n \"\"\"\ndict.__delitem__(self, name)\n
"},{"location":"flat_dict/#chanfig.FlatDict.dict","title":"dict(cls=dict)
","text":"Convert FlatDict
to other Mapping
.
Parameters:
Name Type Description Defaultcls
Callable
Target class to be converted to.
dict
Returns:
Type DescriptionMapping
See Also to_dict
: Implementation of dict
.
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/flat_dict.py
Pythondef dict(self, cls: Callable = dict) -> Mapping:\nr\"\"\"\n Convert `FlatDict` to other `Mapping`.\n Args:\n cls: Target class to be converted to.\n Returns:\n (Mapping):\n See Also:\n [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nreturn cls(to_dict(self))\n
"},{"location":"flat_dict/#chanfig.FlatDict.diff","title":"diff(other, *args, **kwargs)
","text":"Alias of difference
.
chanfig/flat_dict.py
Pythondef diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`difference`][chanfig.FlatDict.difference].\n \"\"\"\nreturn self.difference(other, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.difference","title":"difference(other)
","text":"Difference between FlatDict
and other
.
Parameters:
Name Type Description Defaultother
Mapping | Iterable | PathStr
required Returns:
Type DescriptionFlatDict
Alias:
diff
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.difference(n).dict()\n{'b': 'b', 'c': 'c', 'd': 'd'}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.difference(l).dict()\n{'d': 4}\n>>> d.merge(l).difference(\"tests/test.yaml\").dict()\n{}\n>>> d.difference(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n>>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias\n{'b': 'b', 'c': 'c', 'd': 'd'}\n
Source code in chanfig/flat_dict.py
Pythondef difference(self, other: Mapping | Iterable | PathStr) -> FlatDict:\nr\"\"\"\n Difference between `FlatDict` and `other`.\n Args:\n other:\n Returns:\n (FlatDict):\n **Alias**:\n + `diff`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.difference(n).dict()\n {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.difference(l).dict()\n {'d': 4}\n >>> d.merge(l).difference(\"tests/test.yaml\").dict()\n {}\n >>> d.difference(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias\n {'b': 'b', 'c': 'c', 'd': 'd'}\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(\n**{key: value for key, value in other if key not in self or self[key] != value} # type: ignore\n)\n
"},{"location":"flat_dict/#chanfig.FlatDict.dropna","title":"dropna()
","text":"Alias of dropnull
.
chanfig/flat_dict.py
Pythondef dropna(self) -> FlatDict:\nr\"\"\"\n Alias of [`dropnull`][chanfig.FlatDict.dropnull].\n \"\"\"\nreturn self.dropnull()\n
"},{"location":"flat_dict/#chanfig.FlatDict.dropnull","title":"dropnull()
","text":"Drop key-value pairs with Null
value.
Returns:
Type DescriptionFlatDict
Alias:
dropna
Examples:
Python Console Session>>> d = FlatDict(a=Null, b=Null, c=3)\n>>> d.dict()\n{'a': Null, 'b': Null, 'c': 3}\n>>> d.dropnull().dict()\n{'c': 3}\n>>> d.dropna().dict() # alias\n{'c': 3}\n
Source code in chanfig/flat_dict.py
Pythondef dropnull(self) -> FlatDict:\nr\"\"\"\n Drop key-value pairs with `Null` value.\n Returns:\n (FlatDict):\n **Alias**:\n + `dropna`\n Examples:\n >>> d = FlatDict(a=Null, b=Null, c=3)\n >>> d.dict()\n {'a': Null, 'b': Null, 'c': 3}\n >>> d.dropnull().dict()\n {'c': 3}\n >>> d.dropna().dict() # alias\n {'c': 3}\n \"\"\"\nreturn self.empty({k: v for k, v in self.all_items() if v is not Null})\n
"},{"location":"flat_dict/#chanfig.FlatDict.dump","title":"dump(file, method=None, *args, **kwargs)
","text":"Alias of save
.
chanfig/flat_dict.py
Pythondef dump(self, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Alias of [`save`][chanfig.FlatDict.save].\n \"\"\"\nreturn self.save(file, method, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.empty","title":"empty(*args, **kwargs)
classmethod
","text":"Initialise an empty FlatDict
.
This method is helpful when you inheriting FlatDict
with default values defined in __init__()
. As use type(self)()
in this case would copy all the default values, which might not be desired.
This method will preserve everything in FlatDict.__class__.__dict__
.
Returns:
Type DescriptionFlatDict
See Also empty_like
Examples:
Python Console Session>>> d = FlatDict(a=[])\n>>> c = d.empty()\n>>> c.dict()\n{}\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef empty(cls, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Initialise an empty `FlatDict`.\n This method is helpful when you inheriting `FlatDict` with default values defined in `__init__()`.\n As use `type(self)()` in this case would copy all the default values, which might not be desired.\n This method will preserve everything in `FlatDict.__class__.__dict__`.\n Returns:\n (FlatDict):\n See Also:\n [`empty_like`][chanfig.FlatDict.empty_like]\n Examples:\n >>> d = FlatDict(a=[])\n >>> c = d.empty()\n >>> c.dict()\n {}\n \"\"\"\nempty = cls.__new__(cls)\nempty.merge(*args, **kwargs) # pylint: disable=W0212\nreturn empty\n
"},{"location":"flat_dict/#chanfig.FlatDict.empty_like","title":"empty_like(*args, **kwargs)
","text":"Initialise an empty copy of FlatDict
.
This method will preserve everything in FlatDict.__class__.__dict__
and FlatDict.__dict__
.
For example, property
s are saved in __dict__
, they will keep their original reference after calling this method.
Returns:
Type DescriptionFlatDict
See Also empty
Examples:
Python Console Session>>> d = FlatDict(a=[])\n>>> d.setattr(\"name\", \"Chang\")\n>>> c = d.empty_like()\n>>> c.dict()\n{}\n>>> c.getattr(\"name\")\n'Chang'\n
Source code in chanfig/flat_dict.py
Pythondef empty_like(self, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Initialise an empty copy of `FlatDict`.\n This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.\n For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this\n method.\n Returns:\n (FlatDict):\n See Also:\n [`empty`][chanfig.FlatDict.empty]\n Examples:\n >>> d = FlatDict(a=[])\n >>> d.setattr(\"name\", \"Chang\")\n >>> c = d.empty_like()\n >>> c.dict()\n {}\n >>> c.getattr(\"name\")\n 'Chang'\n \"\"\"\nempty = self.empty(*args, **kwargs)\nempty.__dict__.update(self.__dict__)\nreturn empty\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_dict","title":"from_dict(obj)
classmethod
","text":"Convert Mapping
or Sequence
to FlatDict
.
Examples:
Python Console Session>>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})\nFlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n)\n>>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])\nFlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n)\n>>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])\n[FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n>>> FlatDict.from_dict({1, 2, 3})\nTraceback (most recent call last):\nTypeError: Expected Mapping or Sequence, but got <class 'set'>.\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_dict(cls, obj: Mapping | Sequence) -> Any: # pylint: disable=R0911\nr\"\"\"\n Convert `Mapping` or `Sequence` to `FlatDict`.\n Examples:\n >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})\n FlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])\n FlatDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n >>> FlatDict.from_dict({1, 2, 3})\n Traceback (most recent call last):\n TypeError: Expected Mapping or Sequence, but got <class 'set'>.\n \"\"\"\nif obj is None:\nreturn cls()\nif issubclass(cls, FlatDict):\ncls = cls.empty # type: ignore # pylint: disable=W0642\nif isinstance(obj, Mapping):\nreturn cls(obj)\nif isinstance(obj, Sequence):\ntry:\nreturn cls(obj)\nexcept ValueError:\nreturn [cls(json) for json in obj]\nraise TypeError(f\"Expected Mapping or Sequence, but got {type(obj)}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_json","title":"from_json(file, *args, **kwargs)
classmethod
","text":"Construct FlatDict
from json file.
This method internally calls self.from_jsons()
to construct object from json string. You may overwrite from_jsons
in case something is not json serializable.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> d = FlatDict.from_json('tests/test.json')\n>>> d.dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_json(cls, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from json file.\n This method internally calls `self.from_jsons()` to construct object from json string.\n You may overwrite `from_jsons` in case something is not json serializable.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict.from_json('tests/test.json')\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nwith cls.open(file) as fp: # pylint: disable=C0103\nif isinstance(file, (IOBase, IO)):\nreturn cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore\nreturn cls.from_jsons(fp.read(), *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_jsons","title":"from_jsons(string, *args, **kwargs)
classmethod
","text":"Construct FlatDict
from json string.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> FlatDict.from_jsons('{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}').dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> FlatDict.from_jsons('[[\"a\", 1], [\"b\", 2], [\"c\", 3]]').dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> FlatDict.from_jsons('[{\"a\": 1}, {\"b\": 2}, {\"c\": 3}]')\n[FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from json string.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_jsons('{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_jsons('[[\"a\", 1], [\"b\", 2], [\"c\", 3]]').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_jsons('[{\"a\": 1}, {\"b\": 2}, {\"c\": 3}]')\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n \"\"\"\nreturn cls.from_dict(json_loads(string, *args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_yaml","title":"from_yaml(file, *args, **kwargs)
classmethod
","text":"Construct FlatDict
from yaml file.
This method internally calls self.from_yamls()
to construct object from yaml string. You may overwrite from_yamls
in case something is not yaml serializable.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> FlatDict.from_yaml('tests/test.yaml').dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from yaml file.\n This method internally calls `self.from_yamls()` to construct object from yaml string.\n You may overwrite `from_yamls` in case something is not yaml serializable.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_yaml('tests/test.yaml').dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nkwargs.setdefault(\"Loader\", YamlLoader)\nwith cls.open(file) as fp: # pylint: disable=C0103\nif isinstance(file, (IOBase, IO)):\nreturn cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore\nreturn cls.from_dict(yaml_load(fp, *args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.from_yamls","title":"from_yamls(string, *args, **kwargs)
classmethod
","text":"Construct FlatDict
from yaml string.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> FlatDict.from_yamls('a: 1\\nb: 2\\nc: 3\\n').dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> FlatDict.from_yamls('- - a\\n - 1\\n- - b\\n - 2\\n- - c\\n - 3\\n').dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> FlatDict.from_yamls('- a: 1\\n- b: 2\\n- c: 3\\n')\n[FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Construct `FlatDict` from yaml string.\n Returns:\n (FlatDict):\n Examples:\n >>> FlatDict.from_yamls('a: 1\\nb: 2\\nc: 3\\n').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_yamls('- - a\\n - 1\\n- - b\\n - 2\\n- - c\\n - 3\\n').dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> FlatDict.from_yamls('- a: 1\\n- b: 2\\n- c: 3\\n')\n [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]\n \"\"\"\nkwargs.setdefault(\"Loader\", SafeLoader)\nreturn cls.from_dict(yaml_load(string, *args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.get","title":"get(name, default=None)
","text":"Get value from FlatDict
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
None
Returns:
Name Type Descriptionvalue
Any
If FlatDict
does not contain name
, return default
.
Raises:
Type DescriptionKeyError
If FlatDict
does not contain name
and default
is not specified.
TypeError
If name
is not hashable.
Examples:
Python Console Session>>> d = FlatDict(d=1013)\n>>> d.get('d')\n1013\n>>> d['d']\n1013\n>>> d.d\n1013\n>>> d.get('d', None)\n1013\n>>> d.get('f', 2)\n2\n>>> d.get('f')\n>>> d.get('f', Null)\nTraceback (most recent call last):\nKeyError: 'f'\n
Source code in chanfig/flat_dict.py
Pythondef get(self, name: Any, default: Any = None) -> Any:\nr\"\"\"\n Get value from `FlatDict`.\n Args:\n name:\n default:\n Returns:\n value:\n If `FlatDict` does not contain `name`, return `default`.\n Raises:\n KeyError: If `FlatDict` does not contain `name` and `default` is not specified.\n TypeError: If `name` is not hashable.\n Examples:\n >>> d = FlatDict(d=1013)\n >>> d.get('d')\n 1013\n >>> d['d']\n 1013\n >>> d.d\n 1013\n >>> d.get('d', None)\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.get('f')\n >>> d.get('f', Null)\n Traceback (most recent call last):\n KeyError: 'f'\n \"\"\"\nif name in self:\nreturn dict.__getitem__(self, name)\nif default is not Null:\nreturn default\nreturn self.__missing__(name)\n
"},{"location":"flat_dict/#chanfig.FlatDict.getattr","title":"getattr(name, default=Null)
","text":"Get attribute of FlatDict
.
Note that it won\u2019t retrieve value in FlatDict
,
Parameters:
Name Type Description Defaultname
str
required default
Any
Null
Returns:
Name Type Descriptionvalue
Any
If FlatDict
does not contain name
, return default
.
Raises:
Type DescriptionAttributeError
If FlatDict
does not contain name
and default
is not specified.
Examples:
Python Console Session>>> d = FlatDict(a=1)\n>>> d.get('a')\n1\n>>> d.getattr('a')\nTraceback (most recent call last):\nAttributeError: 'FlatDict' object has no attribute 'a'\n>>> d.getattr('b', 2)\n2\n>>> d.setattr('b', 3)\n>>> d.getattr('b')\n3\n
Source code in chanfig/flat_dict.py
Pythondef getattr(self, name: str, default: Any = Null) -> Any:\nr\"\"\"\n Get attribute of `FlatDict`.\n Note that it won't retrieve value in `FlatDict`,\n Args:\n name:\n default:\n Returns:\n value: If `FlatDict` does not contain `name`, return `default`.\n Raises:\n AttributeError: If `FlatDict` does not contain `name` and `default` is not specified.\n Examples:\n >>> d = FlatDict(a=1)\n >>> d.get('a')\n 1\n >>> d.getattr('a')\n Traceback (most recent call last):\n AttributeError: 'FlatDict' object has no attribute 'a'\n >>> d.getattr('b', 2)\n 2\n >>> d.setattr('b', 3)\n >>> d.getattr('b')\n 3\n \"\"\"\ntry:\nif name in self.__dict__:\nreturn self.__dict__[name]\nif name in self.__class__.__dict__:\nreturn self.__class__.__dict__[name]\nreturn super().getattr(name, default) # type: ignore\nexcept AttributeError:\nif default is not Null:\nreturn default\nraise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\n
"},{"location":"flat_dict/#chanfig.FlatDict.gpu","title":"gpu()
","text":"Move all tensors to gpu.
Returns:
Name Type Descriptionself
FlatDict
Alias:
cuda
Examples:
Python Console Session>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.gpu().dict()\n{'a': tensor(1, device='cuda:0')}\n>>> d.cuda().dict() # alias\n{'a': tensor(1, device='cuda:0')}\n
Source code in chanfig/flat_dict.py
Pythondef gpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to gpu.\n Returns:\n self:\n **Alias**:\n + `cuda`\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.gpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='cuda:0')}\n >>> d.cuda().dict() # alias # doctest: +SKIP\n {'a': tensor(1, device='cuda:0')}\n \"\"\"\nreturn self.to(TorchDevice(\"cuda\"))\n
"},{"location":"flat_dict/#chanfig.FlatDict.hasattr","title":"hasattr(name)
","text":"Determine if an attribute exists in FlatDict
.
Parameters:
Name Type Description Defaultname
str
required Returns:
Type Descriptionbool
Examples:
Python Console Session>>> d = FlatDict()\n>>> d.setattr('name', 'chang')\n>>> d.hasattr('name')\nTrue\n>>> d.delattr('name')\n>>> d.hasattr('name')\nFalse\n
Source code in chanfig/flat_dict.py
Pythondef hasattr(self, name: str) -> bool:\nr\"\"\"\n Determine if an attribute exists in `FlatDict`.\n Args:\n name:\n Returns:\n (bool):\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('name', 'chang')\n >>> d.hasattr('name')\n True\n >>> d.delattr('name')\n >>> d.hasattr('name')\n False\n \"\"\"\ntry:\nif name in self.__dict__ or name in self.__class__.__dict__:\nreturn True\nreturn super().hasattr(name) # type: ignore\nexcept AttributeError:\nreturn False\n
"},{"location":"flat_dict/#chanfig.FlatDict.inter","title":"inter(other, *args, **kwargs)
","text":"Alias of intersect
.
chanfig/flat_dict.py
Pythondef inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`intersect`][chanfig.FlatDict.intersect].\n \"\"\"\nreturn self.intersect(other, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.interpolate","title":"interpolate(use_variable=True, interpolators=None)
","text":"Perform Variable interpolation.
Variable interpolation allows you to set the value of one key to be the value of another key easily.
Parameters:
Name Type Description Defaultuse_variable
bool
Whether to convert values to Variable
objects.
True
interpolators
MutableMapping | None
Mapping contains values for interpolation. Defaults to self
.
None
Raises:
Type DescriptionValueError
If value is not interpolatable.
ValueError
If reference to itself.
ValueError
If has circular reference.
See Also[Variable][chanfig.Variable
]: Mutable wrapper of immutable objects.
Examples:
Python Console Session>>> d = FlatDict(a=1, b=\"${a}\", c=\"${a}.${b}\")\n>>> d.dict()\n{'a': 1, 'b': '${a}', 'c': '${a}.${b}'}\n>>> d.interpolate().dict()\n{'a': 1, 'b': 1, 'c': '1.1'}\n>>> isinstance(d.a, Variable)\nTrue\n>>> d.a += 1\n>>> d.dict()\n{'a': 2, 'b': 2, 'c': '1.1'}\n>>> d.a is d.b\nTrue\n>>> d.b is d.c\nFalse\n>>> d = FlatDict(a=1, b=\"${a}\", c=\"${b}\")\n>>> d.dict()\n{'a': 1, 'b': '${a}', 'c': '${b}'}\n>>> d.interpolate(False).dict()\n{'a': 1, 'b': 1, 'c': 1}\n>>> isinstance(d.a, Variable)\nFalse\n>>> d.a += 1\n>>> d.dict()\n{'a': 2, 'b': 1, 'c': 1}\n>>> d = FlatDict(a=1, b=\"${b}\", c=\"${b}\")\n>>> d.interpolate().dict()\nTraceback (most recent call last):\nValueError: Cannot interpolate b to itself.\n>>> d = FlatDict(a=\"${b}\", b=\"${c}\", c=\"${d}\", d=\"${a}\")\n>>> d.interpolate().dict()\nTraceback (most recent call last):\nValueError: Circular reference found: a->b->c->d->a.\n>>> d = FlatDict(a=1, b=\"${a}\", c=\"${d}\")\n>>> d.interpolate().dict()\nTraceback (most recent call last):\nValueError: d is not found in FlatDict(\n ('a'): '1'\n ('b'): '${a}'\n ('c'): '${d}'\n).\n
Source code in chanfig/flat_dict.py
Pythondef interpolate(self, use_variable: bool = True, interpolators: MutableMapping | None = None) -> FlatDict:\nr\"\"\"\n Perform Variable interpolation.\n Variable interpolation allows you to set the value of one key to be the value of another key easily.\n Args:\n use_variable: Whether to convert values to `Variable` objects.\n interpolators: Mapping contains values for interpolation. Defaults to `self`.\n Raises:\n ValueError: If value is not interpolatable.\n ValueError: If reference to itself.\n ValueError: If has circular reference.\n See Also:\n [Variable][`chanfig.Variable`]: Mutable wrapper of immutable objects.\n Examples:\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${a}.${b}\")\n >>> d.dict()\n {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}\n >>> d.interpolate().dict()\n {'a': 1, 'b': 1, 'c': '1.1'}\n >>> isinstance(d.a, Variable)\n True\n >>> d.a += 1\n >>> d.dict()\n {'a': 2, 'b': 2, 'c': '1.1'}\n >>> d.a is d.b\n True\n >>> d.b is d.c\n False\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${b}\")\n >>> d.dict()\n {'a': 1, 'b': '${a}', 'c': '${b}'}\n >>> d.interpolate(False).dict()\n {'a': 1, 'b': 1, 'c': 1}\n >>> isinstance(d.a, Variable)\n False\n >>> d.a += 1\n >>> d.dict()\n {'a': 2, 'b': 1, 'c': 1}\n >>> d = FlatDict(a=1, b=\"${b}\", c=\"${b}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: Cannot interpolate b to itself.\n >>> d = FlatDict(a=\"${b}\", b=\"${c}\", c=\"${d}\", d=\"${a}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: Circular reference found: a->b->c->d->a.\n >>> d = FlatDict(a=1, b=\"${a}\", c=\"${d}\")\n >>> d.interpolate().dict()\n Traceback (most recent call last):\n ValueError: d is not found in FlatDict(\n ('a'): '1'\n ('b'): '${a}'\n ('c'): '${d}'\n ).\n \"\"\"\ninterpolators = interpolators or self\nplaceholders: dict[str, list[str]] = {}\nfor key, value in self.all_items():\nif isinstance(value, list):\nfor v in value:\nself.find_placeholders(key, v, placeholders)\nelif isinstance(value, Mapping):\nfor v in value.values():\nself.find_placeholders(key, v, placeholders)\nelse:\nself.find_placeholders(key, value, placeholders)\ncircular_references = find_circular_reference(placeholders)\nif circular_references:\nraise ValueError(f\"Circular reference found: {'->'.join(circular_references)}.\")\nif use_variable:\nplaceholder_names = {i for j in placeholders.values() for i in j}\nfor name in list(placeholder_names.difference(placeholders.keys())):\nif name not in interpolators:\nraise ValueError(f\"{name} is not found in {interpolators}.\")\nif not isinstance(interpolators[name], Variable):\ninterpolators[name] = Variable(interpolators[name])\nfor key, value in placeholders.items():\nif isinstance(self[key], list):\nfor index, v in enumerate(self[key]):\nself[key][index] = self.substitute(v, interpolators, value)\nelif isinstance(self[key], Mapping):\nfor k, v in self[key].items():\nself[key][k] = self.substitute(v, interpolators, value)\nelse:\nself[key] = self.substitute(self[key], interpolators, value)\nreturn self\n
"},{"location":"flat_dict/#chanfig.FlatDict.intersect","title":"intersect(other)
","text":"Intersection of FlatDict
and other
.
Parameters:
Name Type Description Defaultother
Mapping | Iterable | PathStr
required Returns:
Type DescriptionFlatDict
Alias:
inter
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.intersect(n).dict()\n{}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.intersect(l).dict()\n{'c': 3}\n>>> d.merge(l).intersect(\"tests/test.yaml\").dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> d.intersect(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n>>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias\n{}\n
Source code in chanfig/flat_dict.py
Pythondef intersect(self, other: Mapping | Iterable | PathStr) -> FlatDict:\nr\"\"\"\n Intersection of `FlatDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n Returns:\n (FlatDict):\n **Alias**:\n + `inter`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.intersect(n).dict()\n {}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.intersect(l).dict()\n {'c': 3}\n >>> d.merge(l).intersect(\"tests/test.yaml\").dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d.intersect(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n >>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias\n {}\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(**{key: value for key, value in other if key in self and self[key] == value}) # type: ignore\n
"},{"location":"flat_dict/#chanfig.FlatDict.json","title":"json(file, *args, **kwargs)
","text":"Dump FlatDict
to json file.
This method internally calls self.jsons()
to generate json string. You may overwrite jsons
in case something is not json serializable.
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.json(\"tests/test.json\")\n
Source code in chanfig/flat_dict.py
Pythondef json(self, file: File, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Dump `FlatDict` to json file.\n This method internally calls `self.jsons()` to generate json string.\n You may overwrite `jsons` in case something is not json serializable.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.json(\"tests/test.json\")\n \"\"\"\nwith self.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nfp.write(self.jsons(*args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.jsons","title":"jsons(*args, **kwargs)
","text":"Dump FlatDict
to json string.
Returns:
Type Descriptionstr
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.jsons()\n'{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}'\n
Source code in chanfig/flat_dict.py
Pythondef jsons(self, *args: Any, **kwargs: Any) -> str:\nr\"\"\"\n Dump `FlatDict` to json string.\n Returns:\n (str):\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.jsons()\n '{\\n \"a\": 1,\\n \"b\": 2,\\n \"c\": 3\\n}'\n \"\"\"\nkwargs.setdefault(\"cls\", JsonEncoder)\nkwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\nreturn json_dumps(self.dict(), *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.load","title":"load(file, method=None, *args, **kwargs)
classmethod
","text":"Load FlatDict
from file.
Parameters:
Name Type Description Defaultfile
File
File to load from.
requiredmethod
str | None
File type, should be in JSON
or YAML
.
None
Returns:
Type DescriptionFlatDict
Raises:
Type DescriptionValueError
If load from IO
and method
is not specified.
TypeError
If dump to unsupported extension.
Examples:
Python Console Session>>> d = FlatDict.load(\"tests/test.yaml\")\n>>> d.dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> d.load(\"tests/test.conf\")\nTraceback (most recent call last):\nTypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"tests/test.yaml\") as f:\n... d.load(f)\nTraceback (most recent call last):\nValueError: `method` must be specified when loading from IO.\n
Source code in chanfig/flat_dict.py
Python@classmethod\ndef load( # pylint: disable=W1113\ncls, file: File, method: str | None = None, *args: Any, **kwargs: Any\n) -> FlatDict:\n\"\"\"\n Load `FlatDict` from file.\n Args:\n file: File to load from.\n method: File type, should be in `JSON` or `YAML`.\n Returns:\n (FlatDict):\n Raises:\n ValueError: If load from `IO` and `method` is not specified.\n TypeError: If dump to unsupported extension.\n Examples:\n >>> d = FlatDict.load(\"tests/test.yaml\")\n >>> d.dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d.load(\"tests/test.conf\")\n Traceback (most recent call last):\n TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"tests/test.yaml\") as f:\n ... d.load(f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when loading from IO.\n \"\"\"\nif method is None:\nif isinstance(file, (IOBase, IO)):\nraise ValueError(\"`method` must be specified when loading from IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in JSON:\nreturn cls.from_json(file, *args, **kwargs)\nif extension in YAML:\nreturn cls.from_yaml(file, *args, **kwargs)\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.merge","title":"merge(*args, overwrite=True, **kwargs)
","text":"Merge other
into FlatDict
.
Parameters:
Name Type Description Default*args
Any
Mapping
or Sequence
to be merged.
()
overwrite
bool
Whether to overwrite existing values.
True
**kwargs
Any
Mapping
to be merged.
{}
Returns:
Name Type Descriptionself
FlatDict
Alias:
union
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.merge(n).dict()\n{'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.merge(l).dict()\n{'a': 1, 'b': 'b', 'c': 3, 'd': 4}\n>>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias\n{'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d = FlatDict()\n>>> d.merge({1: 1, 2: 2, 3:3}).dict()\n{1: 1, 2: 2, 3: 3}\n>>> d.merge(d.clone()).dict()\n{1: 1, 2: 2, 3: 3}\n>>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()\n{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}\n
Source code in chanfig/flat_dict.py
Pythondef merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Merge `other` into `FlatDict`.\n Args:\n *args: `Mapping` or `Sequence` to be merged.\n overwrite: Whether to overwrite existing values.\n **kwargs: `Mapping` to be merged.\n Returns:\n self:\n **Alias**:\n + `union`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d.merge(n).dict()\n {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n >>> l = [('c', 3), ('d', 4)]\n >>> d.merge(l).dict()\n {'a': 1, 'b': 'b', 'c': 3, 'd': 4}\n >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias\n {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n >>> d = FlatDict()\n >>> d.merge({1: 1, 2: 2, 3:3}).dict()\n {1: 1, 2: 2, 3: 3}\n >>> d.merge(d.clone()).dict()\n {1: 1, 2: 2, 3: 3}\n >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()\n {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}\n \"\"\"\nif len(args) == 1:\nargs = args[0]\nif isinstance(args, (PathLike, str, bytes)):\nargs = self.load(args) # type: ignore\nwarn(\n\"merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.\",\nPendingDeprecationWarning,\n)\nself._merge(self, args, overwrite=overwrite)\nelif len(args) > 1:\nself._merge(self, args, overwrite=overwrite)\nif kwargs:\nself._merge(self, kwargs, overwrite=overwrite)\nreturn self\n
"},{"location":"flat_dict/#chanfig.FlatDict.merge_from_file","title":"merge_from_file(file, *args, **kwargs)
","text":"Merge content of file
into FlatDict
.
Parameters:
Name Type Description Defaultfile
File
required *args
Any
Passed to load
.
()
**kwargs
Any
Passed to load
.
{}
Returns:
Name Type Descriptionself
FlatDict
Examples:
Python Console Session>>> d = FlatDict(a=1, b=1)\n>>> d.merge_from_file(\"tests/test.yaml\").dict()\n{'a': 1, 'b': 2, 'c': 3}\n
Source code in chanfig/flat_dict.py
Pythondef merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Merge content of `file` into `FlatDict`.\n Args:\n file (File):\n *args: Passed to [`load`][chanfig.FlatDict.load].\n **kwargs: Passed to [`load`][chanfig.FlatDict.load].\n Returns:\n self:\n Examples:\n >>> d = FlatDict(a=1, b=1)\n >>> d.merge_from_file(\"tests/test.yaml\").dict()\n {'a': 1, 'b': 2, 'c': 3}\n \"\"\"\nreturn self.merge(self.load(file, *args, **kwargs))\n
"},{"location":"flat_dict/#chanfig.FlatDict.open","title":"open(file, *args, encoding='utf-8', **kwargs)
staticmethod
","text":"Open file IO from file path or IO.
This methods extends the ability of built-in open
by allowing it to accept an IOBase
object.
Parameters:
Name Type Description Defaultfile
File
File path or IO.
required*args
Any
Additional arguments passed to open
. Defaults to ().
()
**kwargs
Any
Any Additional keyword arguments passed to open
. Defaults to {}.
{}
Yields:
Type DescriptionGenerator[IOBase | IO, Any, Any]
Examples:
Python Console Session>>> with FlatDict.open(\"tests/test.yaml\") as fp:\n... print(fp.read())\na: 1\nb: 2\nc: 3\n>>> io = open(\"tests/test.yaml\")\n>>> with FlatDict.open(io) as fp:\n... print(fp.read())\na: 1\nb: 2\nc: 3\n>>> with FlatDict.open(123, mode=\"w\") as fp:\n... print(fp.read())\nTraceback (most recent call last):\nTypeError: expected str, bytes, os.PathLike, IO or IOBase, not int\n
Source code in chanfig/flat_dict.py
Python@staticmethod\n@contextmanager\ndef open(file: File, *args: Any, encoding: str = \"utf-8\", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:\nr\"\"\"\n Open file IO from file path or IO.\n This methods extends the ability of built-in `open` by allowing it to accept an `IOBase` object.\n Args:\n file: File path or IO.\n *args: Additional arguments passed to `open`.\n Defaults to ().\n **kwargs: Any\n Additional keyword arguments passed to `open`.\n Defaults to {}.\n Yields:\n (Generator[IOBase | IO, Any, Any]):\n Examples:\n >>> with FlatDict.open(\"tests/test.yaml\") as fp:\n ... print(fp.read())\n a: 1\n b: 2\n c: 3\n <BLANKLINE>\n >>> io = open(\"tests/test.yaml\")\n >>> with FlatDict.open(io) as fp:\n ... print(fp.read())\n a: 1\n b: 2\n c: 3\n <BLANKLINE>\n >>> with FlatDict.open(123, mode=\"w\") as fp:\n ... print(fp.read())\n Traceback (most recent call last):\n TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int\n \"\"\"\nif isinstance(file, (IOBase, IO)):\nyield file\nelif isinstance(file, (PathLike, str, bytes)):\ntry:\nfile = open(file, *args, encoding=encoding, **kwargs) # type: ignore # noqa: SIM115\nyield file # type: ignore\nfinally:\nwith suppress(Exception):\nfile.close() # type: ignore\nelse:\nraise TypeError(f\"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.save","title":"save(file, method=None, *args, **kwargs)
","text":"Save FlatDict
to file.
Raises:
Type DescriptionValueError
If save to IO
and method
is not specified.
TypeError
If save to unsupported extension.
Alias:
save
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.save(\"tests/test.yaml\")\n>>> d.save(\"test.conf\")\nTraceback (most recent call last):\nTypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"test.yaml\", \"w\") as f:\n... d.save(f)\nTraceback (most recent call last):\nValueError: `method` must be specified when saving to IO.\n
Source code in chanfig/flat_dict.py
Pythondef save(self, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Save `FlatDict` to file.\n Raises:\n ValueError: If save to `IO` and `method` is not specified.\n TypeError: If save to unsupported extension.\n **Alias**:\n + `save`\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.save(\"tests/test.yaml\")\n >>> d.save(\"test.conf\")\n Traceback (most recent call last):\n TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"test.yaml\", \"w\") as f:\n ... d.save(f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when saving to IO.\n \"\"\"\nif method is None:\nif isinstance(file, (IOBase, IO)):\nraise ValueError(\"`method` must be specified when saving to IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in YAML:\nreturn self.yaml(file=file, *args, **kwargs) # type: ignore\nif extension in JSON:\nreturn self.json(file=file, *args, **kwargs) # type: ignore\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.set","title":"set(name, value)
","text":"Set value of FlatDict
.
Parameters:
Name Type Description Defaultname
Any
required value
Any
required Examples:
Python Console Session>>> d = FlatDict()\n>>> d.set('d', 1013)\n>>> d.get('d')\n1013\n>>> d['n'] = 'chang'\n>>> d.n\n'chang'\n>>> d.n = 'liu'\n>>> d['n']\n'liu'\n
Source code in chanfig/flat_dict.py
Pythondef set(self, name: Any, value: Any) -> None:\nr\"\"\"\n Set value of `FlatDict`.\n Args:\n name:\n value:\n Examples:\n >>> d = FlatDict()\n >>> d.set('d', 1013)\n >>> d.get('d')\n 1013\n >>> d['n'] = 'chang'\n >>> d.n\n 'chang'\n >>> d.n = 'liu'\n >>> d['n']\n 'liu'\n \"\"\"\nif name in self and isinstance(self.get(name), Variable):\nself.get(name).set(value)\nelse:\ndict.__setitem__(self, name, value)\n
"},{"location":"flat_dict/#chanfig.FlatDict.setattr","title":"setattr(name, value)
","text":"Set attribute of FlatDict
.
Note that it won\u2019t alter values in FlatDict
.
Parameters:
Name Type Description Defaultname
str
required value
Any
required Warns:
Type DescriptionRuntimeWarning
If name already exists in FlatDict
.
Examples:
Python Console Session>>> d = FlatDict()\n>>> d.setattr('attr', 'value')\n>>> d.getattr('attr')\n'value'\n>>> d.set('d', 1013)\n>>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict.\n>>> d.get('d')\n1013\n>>> d.d\n1013\n>>> d.getattr('d')\n1031\n
Source code in chanfig/flat_dict.py
Pythondef setattr(self, name: str, value: Any) -> None:\nr\"\"\"\n Set attribute of `FlatDict`.\n Note that it won't alter values in `FlatDict`.\n Args:\n name:\n value:\n Warns:\n RuntimeWarning: If name already exists in `FlatDict`.\n Examples:\n >>> d = FlatDict()\n >>> d.setattr('attr', 'value')\n >>> d.getattr('attr')\n 'value'\n >>> d.set('d', 1013)\n >>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict.\n >>> d.get('d')\n 1013\n >>> d.d\n 1013\n >>> d.getattr('d')\n 1031\n \"\"\"\nif name in self:\nwarn(\nf\"{name} already exists in {self.__class__.__name__}.\\n\"\nf\"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.\",\nRuntimeWarning,\n)\nself.__dict__[name] = value\n
"},{"location":"flat_dict/#chanfig.FlatDict.sort","title":"sort(key=None, reverse=False)
","text":"Sort FlatDict
.
Returns:
Type DescriptionFlatDict
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.sort().dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> d = FlatDict(b=2, c=3, a=1)\n>>> d.sort().dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> a = [1]\n>>> d = FlatDict(z=0, a=a)\n>>> a.append(2)\n>>> d.sort().dict()\n{'a': [1, 2], 'z': 0}\n
Source code in chanfig/flat_dict.py
Pythondef sort(self, key: Callable | None = None, reverse: bool = False) -> FlatDict:\nr\"\"\"\n Sort `FlatDict`.\n Returns:\n (FlatDict):\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.sort().dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> d = FlatDict(b=2, c=3, a=1)\n >>> d.sort().dict()\n {'a': 1, 'b': 2, 'c': 3}\n >>> a = [1]\n >>> d = FlatDict(z=0, a=a)\n >>> a.append(2)\n >>> d.sort().dict()\n {'a': [1, 2], 'z': 0}\n \"\"\"\nitems = sorted(self.items(), key=key, reverse=reverse)\nself.clear()\nfor k, v in items:\nself[k] = v\nreturn self\n
"},{"location":"flat_dict/#chanfig.FlatDict.to","title":"to(cls)
","text":"Convert values of FlatDict
to target cls
.
Parameters:
Name Type Description Defaultcls
str | device | dtype
required Returns:
Name Type Descriptionself
FlatDict
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.to(int)\nTraceback (most recent call last):\nTypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.\n
Source code in chanfig/flat_dict.py
Pythondef to(self, cls: str | TorchDevice | TorchDType) -> FlatDict: # pragma: no cover\nr\"\"\"\n Convert values of `FlatDict` to target `cls`.\n Args:\n cls (str | torch.device | torch.dtype):\n Returns:\n self:\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.to(int)\n Traceback (most recent call last):\n TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.\n \"\"\"\n# pylint: disable=C0103\nif isinstance(cls, (str, TorchDevice, TorchDType)):\nfor k, v in self.all_items():\nif hasattr(v, \"to\"):\nself[k] = v.to(cls)\nreturn self\nraise TypeError(f\"to() only support torch.dtype and torch.device, but got {cls}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.tpu","title":"tpu()
","text":"Move all tensors to tpu.
Returns:
Name Type Descriptionself
FlatDict
Alias:
xla
Examples:
Python Console Session>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.tpu().dict()\n{'a': tensor(1, device='xla:0')}\n>>> d.xla().dict() # alias\n{'a': tensor(1, device='xla:0')}\n
Source code in chanfig/flat_dict.py
Pythondef tpu(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Move all tensors to tpu.\n Returns:\n self:\n **Alias**:\n + `xla`\n Examples:\n >>> import torch\n >>> d = FlatDict(a=torch.tensor(1))\n >>> d.tpu().dict() # doctest: +SKIP\n {'a': tensor(1, device='xla:0')}\n >>> d.xla().dict() # alias # doctest: +SKIP\n {'a': tensor(1, device='xla:0')}\n \"\"\"\nreturn self.to(TorchDevice(\"xla\"))\n
"},{"location":"flat_dict/#chanfig.FlatDict.union","title":"union(*args, **kwargs)
","text":"Alias of merge
.
chanfig/flat_dict.py
Pythondef union(self, *args: Any, **kwargs: Any) -> FlatDict:\nr\"\"\"\n Alias of [`merge`][chanfig.FlatDict.merge].\n \"\"\"\nreturn self.merge(*args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.validate","title":"validate()
","text":"Validate FlatDict
.
Raises:
Type DescriptionTypeError
If value is not of the type declared in class annotations.
TypeError
If Variable
has invalid type.
ValueError
If Variable
has invalid value.
Examples:
Python Console Session>>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.islower()))\n>>> d = FlatDict(d=Variable(1016, type=str), n=Variable('chang', validator=lambda x: x.islower()))\nTraceback (most recent call last):\nTypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n>>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.isupper()))\nTraceback (most recent call last):\nValueError: 'n' has invalid value. Value chang is not valid.\n
Source code in chanfig/flat_dict.py
Pythondef validate(self) -> None:\nr\"\"\"\n Validate `FlatDict`.\n Raises:\n TypeError: If value is not of the type declared in class annotations.\n TypeError: If `Variable` has invalid type.\n ValueError: If `Variable` has invalid value.\n Examples:\n >>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.islower()))\n >>> d = FlatDict(d=Variable(1016, type=str), n=Variable('chang', validator=lambda x: x.islower()))\n Traceback (most recent call last):\n TypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n >>> d = FlatDict(d=Variable(1016, type=int), n=Variable('chang', validator=lambda x: x.isupper()))\n Traceback (most recent call last):\n ValueError: 'n' has invalid value. Value chang is not valid.\n \"\"\"\nself._validate(self)\n
"},{"location":"flat_dict/#chanfig.FlatDict.xla","title":"xla()
","text":"Alias of tpu
.
chanfig/flat_dict.py
Pythondef xla(self) -> FlatDict: # pragma: no cover\nr\"\"\"\n Alias of [`tpu`][chanfig.FlatDict.tpu].\n \"\"\"\nreturn self.tpu()\n
"},{"location":"flat_dict/#chanfig.FlatDict.yaml","title":"yaml(file, *args, **kwargs)
","text":"Dump FlatDict
to yaml file.
This method internally calls self.yamls()
to generate yaml string. You may overwrite yamls
in case something is not yaml serializable.
Examples:
Python Console Session>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.yaml(\"tests/test.yaml\")\n
Source code in chanfig/flat_dict.py
Pythondef yaml(self, file: File, *args: Any, **kwargs: Any) -> None:\nr\"\"\"\n Dump `FlatDict` to yaml file.\n This method internally calls `self.yamls()` to generate yaml string.\n You may overwrite `yamls` in case something is not yaml serializable.\n Examples:\n >>> d = FlatDict(a=1, b=2, c=3)\n >>> d.yaml(\"tests/test.yaml\")\n \"\"\"\nwith self.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nself.yamls(fp, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.yamls","title":"yamls(*args, **kwargs)
","text":"Dump FlatDict
to yaml string.
Returns:
Type Descriptionstr
Examples:
Python Console Session>>> FlatDict(a=1, b=2, c=3).yamls()\n'a: 1\\nb: 2\\nc: 3\\n'\n
Source code in chanfig/flat_dict.py
Pythondef yamls(self, *args: Any, **kwargs: Any) -> str:\nr\"\"\"\n Dump `FlatDict` to yaml string.\n Returns:\n (str):\n Examples:\n >>> FlatDict(a=1, b=2, c=3).yamls()\n 'a: 1\\nb: 2\\nc: 3\\n'\n \"\"\"\nkwargs.setdefault(\"Dumper\", YamlDumper)\nkwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\nreturn yaml_dump(self.dict(), *args, **kwargs) # type: ignore\n
"},{"location":"functional/","title":"Functional","text":"Convert an object to a dict.
Note that when converting a set
object, it may be converted to a tuple
object if its values is not hashable.
Parameters:
Name Type Description Defaultobj
Any
Object to be converted.
requiredReturns:
Type DescriptionMapping[str, Any]
A dict.
Examples:
Python Console Session>>> to_dict(1)\n1\n>>> to_dict([1, 2, 3])\n[1, 2, 3]\n>>> to_dict((1, 2, 3))\n(1, 2, 3)\n>>> to_dict({1, 2, 3})\n{1, 2, 3}\n>>> to_dict({'a': 1, 'b': 2})\n{'a': 1, 'b': 2}\n>>> to_dict(Variable(1))\n1\n>>> to_dict(FlatDict(a=[[[[[FlatDict(b=1)]]]]]))\n{'a': [[[[[{'b': 1}]]]]]}\n>>> to_dict(FlatDict(a={FlatDict(b=1)}))\n{'a': ({'b': 1},)}\n
Source code in chanfig/flat_dict.py
Pythondef to_dict(obj: Any) -> Mapping[str, Any]: # pylint: disable=R0911\nr\"\"\"\n Convert an object to a dict.\n Note that when converting a `set` object, it may be converted to a `tuple` object if its values is not hashable.\n Args:\n obj: Object to be converted.\n Returns:\n A dict.\n Examples:\n >>> to_dict(1)\n 1\n >>> to_dict([1, 2, 3])\n [1, 2, 3]\n >>> to_dict((1, 2, 3))\n (1, 2, 3)\n >>> to_dict({1, 2, 3})\n {1, 2, 3}\n >>> to_dict({'a': 1, 'b': 2})\n {'a': 1, 'b': 2}\n >>> to_dict(Variable(1))\n 1\n >>> to_dict(FlatDict(a=[[[[[FlatDict(b=1)]]]]]))\n {'a': [[[[[{'b': 1}]]]]]}\n >>> to_dict(FlatDict(a={FlatDict(b=1)}))\n {'a': ({'b': 1},)}\n \"\"\"\nif isinstance(obj, Mapping):\nreturn {k: to_dict(v) for k, v in obj.items()}\nif isinstance(obj, list):\nreturn [to_dict(v) for v in obj] # type: ignore\nif isinstance(obj, tuple):\nreturn tuple(to_dict(v) for v in obj) # type: ignore\nif isinstance(obj, set):\ntry:\nreturn {to_dict(v) for v in obj} # type: ignore\nexcept TypeError:\nreturn tuple(to_dict(v) for v in obj) # type: ignore\nif isinstance(obj, Variable):\nreturn obj.value\nreturn obj\n
options: heading_level: 0
Save FlatDict
to file.
Raises:
Type DescriptionValueError
If save to IO
and method
is not specified.
TypeError
If save to unsupported extension.
Alias:
save
Examples:
Python Console Session>>> obj = {\"a\": 1, \"b\": 2, \"c\": 3}\n>>> save(obj, \"test.yaml\")\n>>> save(obj, \"test.json\")\n>>> save(obj, \"test.conf\")\nTraceback (most recent call last):\nTypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"test.yaml\", \"w\") as f:\n... save(obj, f)\nTraceback (most recent call last):\nValueError: `method` must be specified when saving to IO.\n
Source code in chanfig/functional.py
Pythondef save(obj, file: File, method: str | None = None, *args: Any, **kwargs: Any) -> None: # pylint: disable=W1113\nr\"\"\"\n Save `FlatDict` to file.\n Raises:\n ValueError: If save to `IO` and `method` is not specified.\n TypeError: If save to unsupported extension.\n **Alias**:\n + `save`\n Examples:\n >>> obj = {\"a\": 1, \"b\": 2, \"c\": 3}\n >>> save(obj, \"test.yaml\")\n >>> save(obj, \"test.json\")\n >>> save(obj, \"test.conf\")\n Traceback (most recent call last):\n TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n >>> with open(\"test.yaml\", \"w\") as f:\n ... save(obj, f)\n Traceback (most recent call last):\n ValueError: `method` must be specified when saving to IO.\n \"\"\"\nif isinstance(obj, FlatDict):\nreturn obj.save(file, method, *args, **kwargs)\ndata = to_dict(obj)\nif method is None:\nif isinstance(file, IOBase):\nraise ValueError(\"`method` must be specified when saving to IO.\")\nmethod = splitext(file)[-1][1:] # type: ignore\nextension = method.lower() # type: ignore\nif extension in YAML:\nwith FlatDict.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nyaml_dump(data, fp, *args, **kwargs)\nreturn\nif extension in JSON:\nwith FlatDict.open(file, mode=\"w\") as fp: # pylint: disable=C0103\nfp.write(json_dumps(data, *args, **kwargs))\nreturn\nraise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\") # type: ignore\n
options: heading_level: 0
Load a file into a NestedDict
.
This function simply calls NestedDict.load
.
Parameters:
Name Type Description Defaultfile
PathStr
The file to load.
required*args
Any
The arguments to pass to NestedDict.load
.
()
**kwargs
Any
The keyword arguments to pass to NestedDict.load
.
{}
See Also load
Examples:
Python Console Session>>> from chanfig import load\n>>> config = load(\"tests/test.yaml\")\n>>> config\nNestedDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n)\n
Source code in chanfig/functional.py
Pythondef load(file: PathStr, cls: type = NestedDict, *args: Any, **kwargs: Any) -> NestedDict: # pylint: disable=W1113\nr\"\"\"\n Load a file into a `NestedDict`.\n This function simply calls `NestedDict.load`.\n Args:\n file: The file to load.\n *args: The arguments to pass to `NestedDict.load`.\n **kwargs: The keyword arguments to pass to `NestedDict.load`.\n See Also:\n [`load`][chanfig.FlatDict.load]\n Examples:\n >>> from chanfig import load\n >>> config = load(\"tests/test.yaml\")\n >>> config\n NestedDict(\n ('a'): 1\n ('b'): 2\n ('c'): 3\n )\n \"\"\"\nreturn cls.load(file, *args, **kwargs) # type: ignore\n
options: heading_level: 0
Apply func
to all children of obj
.
Note that this method is meant for non-in-place modification of obj
and should return the original object.
Parameters:
Name Type Description Defaultobj
Any
Object to apply function.
requiredfunc
Callable
Function to be applied.
required*args
Any
Positional arguments to be passed to func
.
()
**kwargs
Any
Keyword arguments to be passed to func
.
{}
Returns:
Type DescriptionAny
Return value of func
.
apply_
: Apply an in-place operation.
chanfig/nested_dict.py
Pythondef apply(obj: Any, func: Callable, *args: Any, **kwargs: Any) -> Any:\nr\"\"\"\n Apply `func` to all children of `obj`.\n Note that this method is meant for non-in-place modification of `obj` and should return the original object.\n Args:\n obj: Object to apply function.\n func: Function to be applied.\n *args: Positional arguments to be passed to `func`.\n **kwargs: Keyword arguments to be passed to `func`.\n Returns:\n (Any): Return value of `func`.\n See Also:\n [`apply_`][chanfig.nested_dict.apply_]: Apply an in-place operation.\n \"\"\"\nif isinstance(obj, NestedDict):\nreturn obj.empty_like(**{k: apply(v, func, *args, **kwargs) for k, v in obj.items()})\nif isinstance(obj, Mapping):\nreturn {k: apply(v, func, *args, **kwargs) for k, v in obj.items()}\nif isinstance(obj, list):\nreturn [apply(v, func, *args, **kwargs) for v in obj]\nif isinstance(obj, tuple):\nreturn tuple(apply(v, func, *args, **kwargs) for v in obj)\nif isinstance(obj, set):\ntry:\nreturn {apply(v, func, *args, **kwargs) for v in obj}\nexcept TypeError:\ntuple(apply(v, func, *args, **kwargs) for v in obj)\nreturn func(*args, **kwargs) if ismethod(func) else func(obj, *args, **kwargs)\n
options: heading_level: 0
Apply func
to all children of obj
.
Note that this method is meant for non-in-place modification of obj
and should return a new object.
Parameters:
Name Type Description Defaultobj
Any
Object to apply function.
requiredfunc
Callable
Function to be applied.
required*args
Any
Positional arguments to be passed to func
.
()
**kwargs
Any
Keyword arguments to be passed to func
.
{}
Returns:
Type DescriptionAny
Return value of func
.
apply_
: Apply a non-in-place operation.
chanfig/nested_dict.py
Pythondef apply_(obj: Any, func: Callable, *args: Any, **kwargs: Any) -> Any:\nr\"\"\"\n Apply `func` to all children of `obj`.\n Note that this method is meant for non-in-place modification of `obj` and should return a new object.\n Args:\n obj: Object to apply function.\n func: Function to be applied.\n *args: Positional arguments to be passed to `func`.\n **kwargs: Keyword arguments to be passed to `func`.\n Returns:\n (Any): Return value of `func`.\n See Also:\n [`apply_`][chanfig.nested_dict.apply]: Apply a non-in-place operation.\n \"\"\"\nif isinstance(obj, Mapping):\nfor v in obj.values():\napply_(v, func, *args, **kwargs)\nif isinstance(obj, (list, tuple, set)):\nfor v in obj:\napply_(v, func, *args, **kwargs)\nreturn func(*args, **kwargs) if ismethod(func) else func(obj, *args, **kwargs)\n
options: heading_level: 0
"},{"location":"nested_dict/","title":"NestedDict","text":" Bases: DefaultDict
NestedDict
further extends DefaultDict
object by introducing a nested structure with delimiter
. By default, delimiter
is .
, but it could be modified in subclass or by calling dict.setattr('delimiter', D)
.
d = NestedDict({\"a.b.c\": 1})
is equivalent to d = NestedDict({\"a\": {\"b\": {\"c\": 1}}})
, and you can access members either by d[\"a.b.c\"]
or more simply by d.a.b.c
.
This behaviour allows you to pass keyword arguments to other functions as easy as func1(**d.func1)
.
Since NestedDict
inherits from DefaultDict
, it also supports default_factory
. With default_factory
, you can assign d.a.b.c = 1
without assign d.a = NestedDict()
in the first place. Note that the constructor of NestedDict
is different from DefaultDict
, default_factory
is not a positional argument, and must be set in a keyword argument.
NestedDict
also introduce all_keys
, all_values
, all_items
methods to get all keys, values, items respectively in the nested structure.
Attributes:
Name Type Descriptionconvert_mapping
bool
bool = False If True
, all new values with type of Mapping
will be converted to default_factory
. If default_factory
is Null
, will create an empty instance via self.empty
as default_factory
.
delimiter
str
str = \u201c.\u201d Delimiter for nested structure.
NotesWhen convert_mapping
specified, all new values with type of Mapping
will be converted to default_factory
. If default_factory
is Null
, will create an empty instance via self.empty
as default_factory
.
convert_mapping
is automatically applied to arguments during initialisation.
Examples:
Python Console Session>>> NestedDict({\"f.n\": \"chang\"})\nNestedDict(\n ('f'): NestedDict(\n ('n'): 'chang'\n )\n)\n>>> d = NestedDict({\"f.n\": \"chang\"}, default_factory=NestedDict)\n>>> d.i.d = 1013\n>>> d['i.d']\n1013\n>>> d.i.d\n1013\n>>> d.dict()\n{'f': {'n': 'chang'}, 'i': {'d': 1013}}\n
Source code in chanfig/nested_dict.py
Pythonclass NestedDict(DefaultDict): # type: ignore # pylint: disable=E1136\nr\"\"\"\n `NestedDict` further extends `DefaultDict` object by introducing a nested structure with `delimiter`.\n By default, `delimiter` is `.`, but it could be modified in subclass or by calling `dict.setattr('delimiter', D)`.\n `d = NestedDict({\"a.b.c\": 1})` is equivalent to `d = NestedDict({\"a\": {\"b\": {\"c\": 1}}})`,\n and you can access members either by `d[\"a.b.c\"]` or more simply by `d.a.b.c`.\n This behaviour allows you to pass keyword arguments to other functions as easy as `func1(**d.func1)`.\n Since `NestedDict` inherits from `DefaultDict`, it also supports `default_factory`.\n With `default_factory`, you can assign `d.a.b.c = 1` without assign `d.a = NestedDict()` in the first place.\n Note that the constructor of `NestedDict` is different from `DefaultDict`, `default_factory` is not a positional\n argument, and must be set in a keyword argument.\n `NestedDict` also introduce `all_keys`, `all_values`, `all_items` methods to get all keys, values, items\n respectively in the nested structure.\n Attributes:\n convert_mapping: bool = False\n If `True`, all new values with type of `Mapping` will be converted to `default_factory`.\n If `default_factory` is `Null`, will create an empty instance via `self.empty` as `default_factory`.\n delimiter: str = \".\"\n Delimiter for nested structure.\n Notes:\n When `convert_mapping` specified, all new values with type of `Mapping` will be converted to `default_factory`.\n If `default_factory` is `Null`, will create an empty instance via `self.empty` as `default_factory`.\n `convert_mapping` is automatically applied to arguments during initialisation.\n Examples:\n >>> NestedDict({\"f.n\": \"chang\"})\n NestedDict(\n ('f'): NestedDict(\n ('n'): 'chang'\n )\n )\n >>> d = NestedDict({\"f.n\": \"chang\"}, default_factory=NestedDict)\n >>> d.i.d = 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.dict()\n {'f': {'n': 'chang'}, 'i': {'d': 1013}}\n \"\"\"\nconvert_mapping: bool = False\ndelimiter: str = \".\"\nfallback: bool = False\ndef __init__(\nself,\n*args: Any,\ndefault_factory: Callable | None = None,\nconvert_mapping: bool | None = None,\nfallback: bool | None = None,\n**kwargs: Any,\n) -> None:\nsuper().__init__(default_factory)\nself.merge(*args, **kwargs)\nif convert_mapping is not None:\nself.setattr(\"convert_mapping\", convert_mapping)\nif fallback is not None:\nself.setattr(\"fallback\", fallback)\ndef all_keys(self) -> Generator:\nr\"\"\"\n Get all keys of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_keys())\n ['a', 'b.c', 'b.d']\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\n@wraps(self.all_keys)\ndef all_keys(self, prefix=\"\"):\nfor key, value in self.items():\nif prefix:\nkey = str(prefix) + str(delimiter) + str(key)\nif isinstance(value, NestedDict):\nyield from all_keys(value, key)\nelse:\nyield key\nreturn all_keys(self)\ndef all_values(self) -> Generator:\nr\"\"\"\n Get all values of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_values())\n [1, 2, 3]\n \"\"\"\nfor value in self.values():\nif isinstance(value, NestedDict):\nyield from value.all_values()\nelse:\nyield value\ndef all_items(self) -> Generator:\nr\"\"\"\n Get all items of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_items())\n [('a', 1), ('b.c', 2), ('b.d', 3)]\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\n@wraps(self.all_items)\ndef all_items(self, prefix=\"\"):\nfor key, value in self.items():\nif prefix:\nkey = str(prefix) + str(delimiter) + str(key)\nif isinstance(value, NestedDict):\nyield from all_items(value, key)\nelse:\nyield key, value\nreturn all_items(self)\ndef apply(self, func: Callable, *args: Any, **kwargs: Any) -> NestedDict:\nr\"\"\"\n Recursively apply a function to `NestedDict` and its children.\n Note:\n This method is meant for non-in-place modification of `obj`, for example, [`to`][chanfig.NestedDict.to].\n Args:\n func (Callable):\n See Also:\n [`apply_`][chanfig.NestedDict.apply_]: Apply an in-place operation.\n [`apply`][chanfig.nested_dict.apply]: Implementation of `apply`.\n tionples:\n >>> def func(d):\n ... if isinstance(d, NestedDict):\n ... d.t = 1\n >>> d = NestedDict()\n >>> d.a = NestedDict()\n >>> d.b = [NestedDict(),]\n >>> d.c = (NestedDict(),)\n >>> d.d = {NestedDict(),}\n >>> d.apply(func).dict()\n {'a': {}, 'b': [{}], 'c': ({},), 'd': ({},)}\n \"\"\"\nreturn apply(self, func, *args, **kwargs)\ndef apply_(self, func: Callable, *args: Any, **kwargs: Any) -> NestedDict:\nr\"\"\"\n Recursively apply a function to `NestedDict` and its children.\n Note:\n This method is meant for in-place modification of `obj`, for example, [`freeze`][chanfig.Config.freeze].\n Args:\n func (Callable):\n See Also:\n [`apply`][chanfig.NestedDict.apply]: Apply a non-in-place operation.\n [`apply_`][chanfig.nested_dict.apply_]: Implementation of `apply_` method.\n Examples:\n >>> def func(d):\n ... if isinstance(d, NestedDict):\n ... d.t = 1\n >>> d = NestedDict()\n >>> d.a = NestedDict()\n >>> d.b = [NestedDict(),]\n >>> d.c = (NestedDict(),)\n >>> d.d = {NestedDict(),}\n >>> d.apply_(func).dict()\n {'a': {'t': 1}, 'b': [{'t': 1}], 'c': ({'t': 1},), 'd': ({'t': 1},), 't': 1}\n \"\"\"\napply_(self, func, *args, **kwargs)\nreturn self\ndef get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\nr\"\"\"\n Get value from `NestedDict`.\n Note that `default` has higher priority than `default_factory`.\n Args:\n name:\n default:\n Returns:\n value:\n If `NestedDict` does not contain `name`, return `default`.\n If `default` is not specified, return `default_factory()`.\n Raises:\n KeyError: If `NestedDict` does not contain `name` and `default`/`default_factory` is not specified.\n TypeError: If `name` is not hashable.\n Examples:\n >>> d = NestedDict({\"i.d\": 1013}, default_factory=NestedDict)\n >>> d.get('i.d')\n 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.get('i.d', None)\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.get('a.b', None)\n >>> d.f\n NestedDict(<class 'chanfig.nested_dict.NestedDict'>, )\n >>> del d.f\n >>> d = NestedDict({\"i.d\": 1013})\n >>> d.e\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'e'\n >>> d.e = {}\n >>> d.get('e.f', Null)\n Traceback (most recent call last):\n KeyError: 'f'\n >>> d.get('e.f')\n >>> d.get('e.f', 1)\n 1\n >>> d.e.f\n Traceback (most recent call last):\n AttributeError: 'dict' object has no attribute 'f'\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\nif fallback is None:\nfallback = self.getattr(\"fallback\", False)\nfallback_name = name.split(delimiter)[-1] if isinstance(name, str) else name\nfallback_values = []\ntry:\nwhile isinstance(name, str) and delimiter in name:\nif fallback and fallback_name in self:\nfallback_values.append(self.get(fallback_name))\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (KeyError, AttributeError, TypeError):\nif fallback and fallback_values:\nreturn fallback_values[-1]\nif default is not Null:\nreturn default\nraise KeyError(name) from None\nif (fallback and fallback_values) and (not isinstance(self, Iterable) or name not in self):\nreturn fallback_values[-1]\n# if value is a python dict\nif not isinstance(self, NestedDict):\nif name not in self and default is not Null:\nreturn default\nreturn self[name]\nreturn super().get(name, default)\ndef set( # pylint: disable=W0221\nself,\nname: Any,\nvalue: Any,\nconvert_mapping: bool | None = None,\n) -> None:\nr\"\"\"\n Set value of `NestedDict`.\n Args:\n name:\n value:\n convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n Defaults to self.convert_mapping.\n Examples:\n >>> d = NestedDict(default_factory=NestedDict)\n >>> d.set('i.d', 1013)\n >>> d.get('i.d')\n 1013\n >>> d.dict()\n {'i': {'d': 1013}}\n >>> d['f.n'] = 'chang'\n >>> d.f.n\n 'chang'\n >>> d.n.l = 'liu'\n >>> d['n.l']\n 'liu'\n >>> d['f.n.e'] = \"error\"\n Traceback (most recent call last):\n ValueError: Cannot set `f.n.e` to `error`, as `f.n=chang`.\n >>> d['f.n.e.a'] = \"error\"\n Traceback (most recent call last):\n KeyError: 'e'\n >>> d.f.n.e.a = \"error\"\n Traceback (most recent call last):\n AttributeError: 'str' object has no attribute 'e'\n >>> d.setattr('convert_mapping', True)\n >>> d.a.b = {'c': {'d': 1}, 'e.f' : 2}\n >>> d.a.b.c.d\n 1\n >>> d['c.d'] = {'c': {'d': 1}, 'e.f' : 2}\n >>> d.c.d['e.f']\n 2\n >>> d.setattr('convert_mapping', False)\n >>> d.set('e.f', {'c': {'d': 1}, 'e.f' : 2}, convert_mapping=True)\n >>> d['e.f']['c.d']\n 1\n \"\"\"\n# pylint: disable=W0642\nfull_name = name\ndelimiter = self.getattr(\"delimiter\", \".\")\nif convert_mapping is None:\nconvert_mapping = self.getattr(\"convert_mapping\", False)\ndefault_factory = self.getattr(\"default_factory\", self.empty)\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nif name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\nself, name = getattr(self, name), rest\nelif name not in self and isinstance(self, Mapping):\ndefault = (\nself.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n)\nself, name = default, rest\nelse:\nself, name = self[name], rest\nif isinstance(self, NestedDict):\ndefault_factory = self.getattr(\"default_factory\", self.empty)\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\nif (\nconvert_mapping\nand isinstance(value, Mapping)\nand not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\nand not isinstance(value, Variable)\n):\ntry:\nvalue = default_factory(**value)\nexcept TypeError:\nvalue = default_factory(value)\nif isinstance(self, NestedDict):\nsuper().set(name, value)\nelif isinstance(self, Mapping):\ndict.__setitem__(self, name, value)\nelse:\nraise ValueError(\nf\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n)\ndef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `NestedDict`.\n Args:\n name:\n Examples:\n >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\"})\n >>> d.i.d\n 1013\n >>> d.f.n\n 'chang'\n >>> d.delete('i.d')\n >>> d.dict()\n {'i': {}, 'f': {'n': 'chang'}}\n >>> d.i.d\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'd'\n >>> del d.f.n\n >>> d.dict()\n {'i': {}, 'f': {}}\n >>> d.f.n\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'n'\n >>> del d.e\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'e'\n >>> del d['f.n']\n Traceback (most recent call last):\n KeyError: 'n'\n >>> d.e = {'a': {'b': 1}}\n >>> del d['e.a.b']\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\n# if value is a python dict\nif not isinstance(self, NestedDict):\ndel self[name]\nreturn\nsuper().delete(name)\ndef pop(self, name: Any, default: Any = Null) -> Any:\nr\"\"\"\n Pop value from `NestedDict`.\n Args:\n name:\n default:\n Returns:\n value: If `NestedDict` does not contain `name`, return `default`.\n Examples:\n >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1}, default_factory=NestedDict)\n >>> d.pop('i.d')\n 1013\n >>> d.pop('i.d', True)\n True\n >>> d.pop('i.d')\n Traceback (most recent call last):\n KeyError: 'd'\n >>> d.pop('e')\n Traceback (most recent call last):\n KeyError: 'e'\n >>> d.pop('e.f')\n Traceback (most recent call last):\n KeyError: 'f'\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\nif not isinstance(self, dict) or name not in self:\nif default is not Null:\nreturn default\nraise KeyError(name)\nreturn super().pop(name)\ndef validate(self) -> None:\nr\"\"\"\n Validate `NestedDict`.\n Raises:\n TypeError: If `Variable` has invalid type.\n ValueError: If `Variable` has invalid value.\n Examples:\n >>> d = NestedDict({\"i.d\": Variable(1016, type=int, validator=lambda x: x > 0)})\n >>> d = NestedDict({\"i.d\": Variable(1016, type=str, validator=lambda x: x > 0)})\n Traceback (most recent call last):\n TypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n >>> d = NestedDict({\"i.d\": Variable(-1, type=int, validator=lambda x: x > 0)})\n Traceback (most recent call last):\n ValueError: 'd' has invalid value. Value -1 is not valid.\n \"\"\"\nself.apply_(self._validate)\ndef sort(self, key: Callable | None = None, reverse: bool = False, recursive: bool = True) -> NestedDict:\nr\"\"\"\n Sort `NestedDict`.\n Args:\n recursive (bool): Whether to apply `sort` recursively.\n Returns:\n (NestedDict):\n Examples:\n >>> l = [1]\n >>> d = NestedDict({\"a\": 1, \"b\": {\"c\": 2, \"d\": 3}, \"b.e.f\": l})\n >>> d.sort().dict()\n {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n >>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n >>> d.sort().dict()\n {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n >>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n >>> d.sort(recursive=False).dict()\n {'a': 1, 'b': {'e': {'f': [1]}, 'd': 3, 'c': 2}}\n >>> l.append(2)\n >>> d.b.e.f\n [1, 2]\n \"\"\"\nif recursive:\nfor value in self.values():\nif isinstance(value, FlatDict):\nvalue.sort(key=key, reverse=reverse)\nreturn super().sort(key=key, reverse=reverse) # type: ignore\n@staticmethod\ndef _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:\nif not that:\nreturn this\nelif isinstance(that, Mapping):\nthat = that.items()\ncontext = this.converting() if isinstance(this, NestedDict) else nullcontext()\nwith context:\nfor key, value in that:\nif key in this and isinstance(this[key], Mapping):\nif isinstance(value, Mapping):\nNestedDict._merge(this[key], value, overwrite)\nelif overwrite:\nif isinstance(this, NestedDict):\nthis.set(key, value)\nelse:\nthis[key] = value\nelif key in dir(this) and isinstance(getattr(this.__class__, key), (property, cached_property)):\ngetattr(this, key).merge(value, overwrite=overwrite)\nelif overwrite or key not in this:\nif isinstance(this, NestedDict):\nthis.set(key, value)\nelse:\nthis[key] = value\nreturn this\ndef intersect( # pylint: disable=W0221\nself, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> NestedDict:\nr\"\"\"\n Intersection of `NestedDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n recursive (bool):\n Examples:\n >>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n >>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n >>> d.intersect(n).dict()\n {'c': {'d': {'e': 4, 'f': 5}}}\n >>> d.intersect(\"tests/test.yaml\").dict()\n {'a': 1}\n >>> d.intersect(n, recursive=False).dict()\n {}\n >>> l = [('a', 1), ('d', 4)]\n >>> d.intersect(l).dict()\n {'a': 1}\n >>> d.intersect(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(self._intersect(self, other, recursive)) # type: ignore\n@staticmethod\ndef _intersect(this: NestedDict, that: Iterable, recursive: bool = True) -> Mapping:\nret: NestedDict = NestedDict()\nfor key, value in that:\nif key in this:\nif isinstance(this[key], NestedDict) and isinstance(value, Mapping) and recursive:\nintersects = this[key].intersect(value)\nif intersects:\nret[key] = intersects\nelif this[key] == value:\nret[key] = value\nreturn ret\ndef difference( # pylint: disable=W0221, C0103\nself, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> NestedDict:\nr\"\"\"\n Difference between `NestedDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n recursive (bool):\n Examples:\n >>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n >>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n >>> d.difference(n).dict()\n {'b': {'c': 3, 'd': 5}, 'd': 0}\n >>> d.difference(\"tests/test.yaml\").dict()\n {'b': 2, 'c': 3}\n >>> d.difference(n, recursive=False).dict()\n {'b': {'c': 3, 'd': 5}, 'c': {'d': {'e': 4, 'f': 5}}, 'd': 0}\n >>> l = [('a', 1), ('d', 4)]\n >>> d.difference(l).dict()\n {'d': 4}\n >>> d.difference(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(self._difference(self, other, recursive)) # type: ignore\n@staticmethod\ndef _difference(this: NestedDict, that: Iterable, recursive: bool = True) -> Mapping:\nret: NestedDict = NestedDict()\nfor key, value in that:\nif key not in this:\nret[key] = value\nelif isinstance(this[key], NestedDict) and isinstance(value, Mapping) and recursive:\ndifferences = this[key].difference(value)\nif differences:\nret[key] = differences\nelif this[key] != value:\nret[key] = value\nreturn ret\n@contextmanager\ndef converting(self):\nconvert_mapping = self.getattr(\"convert_mapping\", False)\ntry:\nself.setattr(\"convert_mapping\", True)\nyield\nfinally:\nself.setattr(\"convert_mapping\", convert_mapping)\ndef __contains__(self, name: Any) -> bool: # type: ignore\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nif super().__contains__(name):\nself, name = self[name], rest # pylint: disable=W0642\nelse:\nreturn False\nreturn super().__contains__(name)\nexcept (TypeError, KeyError): # TypeError when name is not in self\nreturn False\n
"},{"location":"nested_dict/#chanfig.NestedDict.all_items","title":"all_items()
","text":"Get all items of NestedDict
.
Returns:
Type DescriptionGenerator
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n>>> list(d.all_items())\n[('a', 1), ('b.c', 2), ('b.d', 3)]\n
Source code in chanfig/nested_dict.py
Pythondef all_items(self) -> Generator:\nr\"\"\"\n Get all items of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_items())\n [('a', 1), ('b.c', 2), ('b.d', 3)]\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\n@wraps(self.all_items)\ndef all_items(self, prefix=\"\"):\nfor key, value in self.items():\nif prefix:\nkey = str(prefix) + str(delimiter) + str(key)\nif isinstance(value, NestedDict):\nyield from all_items(value, key)\nelse:\nyield key, value\nreturn all_items(self)\n
"},{"location":"nested_dict/#chanfig.NestedDict.all_keys","title":"all_keys()
","text":"Get all keys of NestedDict
.
Returns:
Type DescriptionGenerator
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n>>> list(d.all_keys())\n['a', 'b.c', 'b.d']\n
Source code in chanfig/nested_dict.py
Pythondef all_keys(self) -> Generator:\nr\"\"\"\n Get all keys of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_keys())\n ['a', 'b.c', 'b.d']\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\n@wraps(self.all_keys)\ndef all_keys(self, prefix=\"\"):\nfor key, value in self.items():\nif prefix:\nkey = str(prefix) + str(delimiter) + str(key)\nif isinstance(value, NestedDict):\nyield from all_keys(value, key)\nelse:\nyield key\nreturn all_keys(self)\n
"},{"location":"nested_dict/#chanfig.NestedDict.all_values","title":"all_values()
","text":"Get all values of NestedDict
.
Returns:
Type DescriptionGenerator
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n>>> list(d.all_values())\n[1, 2, 3]\n
Source code in chanfig/nested_dict.py
Pythondef all_values(self) -> Generator:\nr\"\"\"\n Get all values of `NestedDict`.\n Returns:\n (Generator):\n Examples:\n >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n >>> list(d.all_values())\n [1, 2, 3]\n \"\"\"\nfor value in self.values():\nif isinstance(value, NestedDict):\nyield from value.all_values()\nelse:\nyield value\n
"},{"location":"nested_dict/#chanfig.NestedDict.apply","title":"apply(func, *args, **kwargs)
","text":"Recursively apply a function to NestedDict
and its children.
This method is meant for non-in-place modification of obj
, for example, to
.
Parameters:
Name Type Description Defaultfunc
Callable
required See Also apply_
: Apply an in-place operation. apply
: Implementation of apply
.
def func(d): \u2026 if isinstance(d, NestedDict): \u2026 d.t = 1 d = NestedDict() d.a = NestedDict() d.b = [NestedDict(),] d.c = (NestedDict(),) d.d = {NestedDict(),} d.apply(func).dict() {\u2018a\u2019: {}, \u2018b\u2019: [{}], \u2018c\u2019: ({},), \u2018d\u2019: ({},)}
Source code inchanfig/nested_dict.py
Pythondef apply(self, func: Callable, *args: Any, **kwargs: Any) -> NestedDict:\nr\"\"\"\n Recursively apply a function to `NestedDict` and its children.\n Note:\n This method is meant for non-in-place modification of `obj`, for example, [`to`][chanfig.NestedDict.to].\n Args:\n func (Callable):\n See Also:\n [`apply_`][chanfig.NestedDict.apply_]: Apply an in-place operation.\n [`apply`][chanfig.nested_dict.apply]: Implementation of `apply`.\n tionples:\n >>> def func(d):\n ... if isinstance(d, NestedDict):\n ... d.t = 1\n >>> d = NestedDict()\n >>> d.a = NestedDict()\n >>> d.b = [NestedDict(),]\n >>> d.c = (NestedDict(),)\n >>> d.d = {NestedDict(),}\n >>> d.apply(func).dict()\n {'a': {}, 'b': [{}], 'c': ({},), 'd': ({},)}\n \"\"\"\nreturn apply(self, func, *args, **kwargs)\n
"},{"location":"nested_dict/#chanfig.NestedDict.apply_","title":"apply_(func, *args, **kwargs)
","text":"Recursively apply a function to NestedDict
and its children.
This method is meant for in-place modification of obj
, for example, freeze
.
Parameters:
Name Type Description Defaultfunc
Callable
required See Also apply
: Apply a non-in-place operation. apply_
: Implementation of apply_
method.
Examples:
Python Console Session>>> def func(d):\n... if isinstance(d, NestedDict):\n... d.t = 1\n>>> d = NestedDict()\n>>> d.a = NestedDict()\n>>> d.b = [NestedDict(),]\n>>> d.c = (NestedDict(),)\n>>> d.d = {NestedDict(),}\n>>> d.apply_(func).dict()\n{'a': {'t': 1}, 'b': [{'t': 1}], 'c': ({'t': 1},), 'd': ({'t': 1},), 't': 1}\n
Source code in chanfig/nested_dict.py
Pythondef apply_(self, func: Callable, *args: Any, **kwargs: Any) -> NestedDict:\nr\"\"\"\n Recursively apply a function to `NestedDict` and its children.\n Note:\n This method is meant for in-place modification of `obj`, for example, [`freeze`][chanfig.Config.freeze].\n Args:\n func (Callable):\n See Also:\n [`apply`][chanfig.NestedDict.apply]: Apply a non-in-place operation.\n [`apply_`][chanfig.nested_dict.apply_]: Implementation of `apply_` method.\n Examples:\n >>> def func(d):\n ... if isinstance(d, NestedDict):\n ... d.t = 1\n >>> d = NestedDict()\n >>> d.a = NestedDict()\n >>> d.b = [NestedDict(),]\n >>> d.c = (NestedDict(),)\n >>> d.d = {NestedDict(),}\n >>> d.apply_(func).dict()\n {'a': {'t': 1}, 'b': [{'t': 1}], 'c': ({'t': 1},), 'd': ({'t': 1},), 't': 1}\n \"\"\"\napply_(self, func, *args, **kwargs)\nreturn self\n
"},{"location":"nested_dict/#chanfig.NestedDict.delete","title":"delete(name)
","text":"Delete value from NestedDict
.
Parameters:
Name Type Description Defaultname
Any
required Examples:
Python Console Session>>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\"})\n>>> d.i.d\n1013\n>>> d.f.n\n'chang'\n>>> d.delete('i.d')\n>>> d.dict()\n{'i': {}, 'f': {'n': 'chang'}}\n>>> d.i.d\nTraceback (most recent call last):\nAttributeError: 'NestedDict' object has no attribute 'd'\n>>> del d.f.n\n>>> d.dict()\n{'i': {}, 'f': {}}\n>>> d.f.n\nTraceback (most recent call last):\nAttributeError: 'NestedDict' object has no attribute 'n'\n>>> del d.e\nTraceback (most recent call last):\nAttributeError: 'NestedDict' object has no attribute 'e'\n>>> del d['f.n']\nTraceback (most recent call last):\nKeyError: 'n'\n>>> d.e = {'a': {'b': 1}}\n>>> del d['e.a.b']\n
Source code in chanfig/nested_dict.py
Pythondef delete(self, name: Any) -> None:\nr\"\"\"\n Delete value from `NestedDict`.\n Args:\n name:\n Examples:\n >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\"})\n >>> d.i.d\n 1013\n >>> d.f.n\n 'chang'\n >>> d.delete('i.d')\n >>> d.dict()\n {'i': {}, 'f': {'n': 'chang'}}\n >>> d.i.d\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'd'\n >>> del d.f.n\n >>> d.dict()\n {'i': {}, 'f': {}}\n >>> d.f.n\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'n'\n >>> del d.e\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'e'\n >>> del d['f.n']\n Traceback (most recent call last):\n KeyError: 'n'\n >>> d.e = {'a': {'b': 1}}\n >>> del d['e.a.b']\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\n# if value is a python dict\nif not isinstance(self, NestedDict):\ndel self[name]\nreturn\nsuper().delete(name)\n
"},{"location":"nested_dict/#chanfig.NestedDict.difference","title":"difference(other, recursive=True)
","text":"Difference between NestedDict
and other
.
Parameters:
Name Type Description Defaultother
Mapping | Iterable | PathStr
required recursive
bool
True
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n>>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n>>> d.difference(n).dict()\n{'b': {'c': 3, 'd': 5}, 'd': 0}\n>>> d.difference(\"tests/test.yaml\").dict()\n{'b': 2, 'c': 3}\n>>> d.difference(n, recursive=False).dict()\n{'b': {'c': 3, 'd': 5}, 'c': {'d': {'e': 4, 'f': 5}}, 'd': 0}\n>>> l = [('a', 1), ('d', 4)]\n>>> d.difference(l).dict()\n{'d': 4}\n>>> d.difference(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n
Source code in chanfig/nested_dict.py
Pythondef difference( # pylint: disable=W0221, C0103\nself, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> NestedDict:\nr\"\"\"\n Difference between `NestedDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n recursive (bool):\n Examples:\n >>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n >>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n >>> d.difference(n).dict()\n {'b': {'c': 3, 'd': 5}, 'd': 0}\n >>> d.difference(\"tests/test.yaml\").dict()\n {'b': 2, 'c': 3}\n >>> d.difference(n, recursive=False).dict()\n {'b': {'c': 3, 'd': 5}, 'c': {'d': {'e': 4, 'f': 5}}, 'd': 0}\n >>> l = [('a', 1), ('d', 4)]\n >>> d.difference(l).dict()\n {'d': 4}\n >>> d.difference(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(self._difference(self, other, recursive)) # type: ignore\n
"},{"location":"nested_dict/#chanfig.NestedDict.get","title":"get(name, default=None, fallback=None)
","text":"Get value from NestedDict
.
Note that default
has higher priority than default_factory
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
None
Returns:
Name Type Descriptionvalue
Any
If NestedDict
does not contain name
, return default
. If default
is not specified, return default_factory()
.
Raises:
Type DescriptionKeyError
If NestedDict
does not contain name
and default
/default_factory
is not specified.
TypeError
If name
is not hashable.
Examples:
Python Console Session>>> d = NestedDict({\"i.d\": 1013}, default_factory=NestedDict)\n>>> d.get('i.d')\n1013\n>>> d['i.d']\n1013\n>>> d.i.d\n1013\n>>> d.get('i.d', None)\n1013\n>>> d.get('f', 2)\n2\n>>> d.get('a.b', None)\n>>> d.f\nNestedDict(<class 'chanfig.nested_dict.NestedDict'>, )\n>>> del d.f\n>>> d = NestedDict({\"i.d\": 1013})\n>>> d.e\nTraceback (most recent call last):\nAttributeError: 'NestedDict' object has no attribute 'e'\n>>> d.e = {}\n>>> d.get('e.f', Null)\nTraceback (most recent call last):\nKeyError: 'f'\n>>> d.get('e.f')\n>>> d.get('e.f', 1)\n1\n>>> d.e.f\nTraceback (most recent call last):\nAttributeError: 'dict' object has no attribute 'f'\n
Source code in chanfig/nested_dict.py
Pythondef get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\nr\"\"\"\n Get value from `NestedDict`.\n Note that `default` has higher priority than `default_factory`.\n Args:\n name:\n default:\n Returns:\n value:\n If `NestedDict` does not contain `name`, return `default`.\n If `default` is not specified, return `default_factory()`.\n Raises:\n KeyError: If `NestedDict` does not contain `name` and `default`/`default_factory` is not specified.\n TypeError: If `name` is not hashable.\n Examples:\n >>> d = NestedDict({\"i.d\": 1013}, default_factory=NestedDict)\n >>> d.get('i.d')\n 1013\n >>> d['i.d']\n 1013\n >>> d.i.d\n 1013\n >>> d.get('i.d', None)\n 1013\n >>> d.get('f', 2)\n 2\n >>> d.get('a.b', None)\n >>> d.f\n NestedDict(<class 'chanfig.nested_dict.NestedDict'>, )\n >>> del d.f\n >>> d = NestedDict({\"i.d\": 1013})\n >>> d.e\n Traceback (most recent call last):\n AttributeError: 'NestedDict' object has no attribute 'e'\n >>> d.e = {}\n >>> d.get('e.f', Null)\n Traceback (most recent call last):\n KeyError: 'f'\n >>> d.get('e.f')\n >>> d.get('e.f', 1)\n 1\n >>> d.e.f\n Traceback (most recent call last):\n AttributeError: 'dict' object has no attribute 'f'\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\nif fallback is None:\nfallback = self.getattr(\"fallback\", False)\nfallback_name = name.split(delimiter)[-1] if isinstance(name, str) else name\nfallback_values = []\ntry:\nwhile isinstance(name, str) and delimiter in name:\nif fallback and fallback_name in self:\nfallback_values.append(self.get(fallback_name))\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (KeyError, AttributeError, TypeError):\nif fallback and fallback_values:\nreturn fallback_values[-1]\nif default is not Null:\nreturn default\nraise KeyError(name) from None\nif (fallback and fallback_values) and (not isinstance(self, Iterable) or name not in self):\nreturn fallback_values[-1]\n# if value is a python dict\nif not isinstance(self, NestedDict):\nif name not in self and default is not Null:\nreturn default\nreturn self[name]\nreturn super().get(name, default)\n
"},{"location":"nested_dict/#chanfig.NestedDict.intersect","title":"intersect(other, recursive=True)
","text":"Intersection of NestedDict
and other
.
Parameters:
Name Type Description Defaultother
Mapping | Iterable | PathStr
required recursive
bool
True
Examples:
Python Console Session>>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n>>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n>>> d.intersect(n).dict()\n{'c': {'d': {'e': 4, 'f': 5}}}\n>>> d.intersect(\"tests/test.yaml\").dict()\n{'a': 1}\n>>> d.intersect(n, recursive=False).dict()\n{}\n>>> l = [('a', 1), ('d', 4)]\n>>> d.intersect(l).dict()\n{'a': 1}\n>>> d.intersect(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n
Source code in chanfig/nested_dict.py
Pythondef intersect( # pylint: disable=W0221\nself, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> NestedDict:\nr\"\"\"\n Intersection of `NestedDict` and `other`.\n Args:\n other (Mapping | Iterable | PathStr):\n recursive (bool):\n Examples:\n >>> d = NestedDict({'a': 1, 'b.c': 2, 'b.d': 3, 'c.d.e': 4, 'c.d.f': 5, 'c.e': 6})\n >>> n = {'b': {'c': 3, 'd': 5}, 'c.d.e': 4, 'c.d': {'f': 5}, 'd': 0}\n >>> d.intersect(n).dict()\n {'c': {'d': {'e': 4, 'f': 5}}}\n >>> d.intersect(\"tests/test.yaml\").dict()\n {'a': 1}\n >>> d.intersect(n, recursive=False).dict()\n {}\n >>> l = [('a', 1), ('d', 4)]\n >>> d.intersect(l).dict()\n {'a': 1}\n >>> d.intersect(1)\n Traceback (most recent call last):\n TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n \"\"\"\nif isinstance(other, (PathLike, str, bytes)):\nother = self.load(other)\nif isinstance(other, (Mapping,)):\nother = self.empty(other).items()\nif not isinstance(other, Iterable):\nraise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\nreturn self.empty(self._intersect(self, other, recursive)) # type: ignore\n
"},{"location":"nested_dict/#chanfig.NestedDict.pop","title":"pop(name, default=Null)
","text":"Pop value from NestedDict
.
Parameters:
Name Type Description Defaultname
Any
required default
Any
Null
Returns:
Name Type Descriptionvalue
Any
If NestedDict
does not contain name
, return default
.
Examples:
Python Console Session>>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1}, default_factory=NestedDict)\n>>> d.pop('i.d')\n1013\n>>> d.pop('i.d', True)\nTrue\n>>> d.pop('i.d')\nTraceback (most recent call last):\nKeyError: 'd'\n>>> d.pop('e')\nTraceback (most recent call last):\nKeyError: 'e'\n>>> d.pop('e.f')\nTraceback (most recent call last):\nKeyError: 'f'\n
Source code in chanfig/nested_dict.py
Pythondef pop(self, name: Any, default: Any = Null) -> Any:\nr\"\"\"\n Pop value from `NestedDict`.\n Args:\n name:\n default:\n Returns:\n value: If `NestedDict` does not contain `name`, return `default`.\n Examples:\n >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1}, default_factory=NestedDict)\n >>> d.pop('i.d')\n 1013\n >>> d.pop('i.d', True)\n True\n >>> d.pop('i.d')\n Traceback (most recent call last):\n KeyError: 'd'\n >>> d.pop('e')\n Traceback (most recent call last):\n KeyError: 'e'\n >>> d.pop('e.f')\n Traceback (most recent call last):\n KeyError: 'f'\n \"\"\"\ndelimiter = self.getattr(\"delimiter\", \".\")\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nself, name = self[name], rest # pylint: disable=W0642\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\nif not isinstance(self, dict) or name not in self:\nif default is not Null:\nreturn default\nraise KeyError(name)\nreturn super().pop(name)\n
"},{"location":"nested_dict/#chanfig.NestedDict.set","title":"set(name, value, convert_mapping=None)
","text":"Set value of NestedDict
.
Parameters:
Name Type Description Defaultname
Any
required value
Any
required convert_mapping
bool | None
Whether to convert Mapping
to NestedDict
. Defaults to self.convert_mapping.
None
Examples:
Python Console Session>>> d = NestedDict(default_factory=NestedDict)\n>>> d.set('i.d', 1013)\n>>> d.get('i.d')\n1013\n>>> d.dict()\n{'i': {'d': 1013}}\n>>> d['f.n'] = 'chang'\n>>> d.f.n\n'chang'\n>>> d.n.l = 'liu'\n>>> d['n.l']\n'liu'\n>>> d['f.n.e'] = \"error\"\nTraceback (most recent call last):\nValueError: Cannot set `f.n.e` to `error`, as `f.n=chang`.\n>>> d['f.n.e.a'] = \"error\"\nTraceback (most recent call last):\nKeyError: 'e'\n>>> d.f.n.e.a = \"error\"\nTraceback (most recent call last):\nAttributeError: 'str' object has no attribute 'e'\n>>> d.setattr('convert_mapping', True)\n>>> d.a.b = {'c': {'d': 1}, 'e.f' : 2}\n>>> d.a.b.c.d\n1\n>>> d['c.d'] = {'c': {'d': 1}, 'e.f' : 2}\n>>> d.c.d['e.f']\n2\n>>> d.setattr('convert_mapping', False)\n>>> d.set('e.f', {'c': {'d': 1}, 'e.f' : 2}, convert_mapping=True)\n>>> d['e.f']['c.d']\n1\n
Source code in chanfig/nested_dict.py
Pythondef set( # pylint: disable=W0221\nself,\nname: Any,\nvalue: Any,\nconvert_mapping: bool | None = None,\n) -> None:\nr\"\"\"\n Set value of `NestedDict`.\n Args:\n name:\n value:\n convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n Defaults to self.convert_mapping.\n Examples:\n >>> d = NestedDict(default_factory=NestedDict)\n >>> d.set('i.d', 1013)\n >>> d.get('i.d')\n 1013\n >>> d.dict()\n {'i': {'d': 1013}}\n >>> d['f.n'] = 'chang'\n >>> d.f.n\n 'chang'\n >>> d.n.l = 'liu'\n >>> d['n.l']\n 'liu'\n >>> d['f.n.e'] = \"error\"\n Traceback (most recent call last):\n ValueError: Cannot set `f.n.e` to `error`, as `f.n=chang`.\n >>> d['f.n.e.a'] = \"error\"\n Traceback (most recent call last):\n KeyError: 'e'\n >>> d.f.n.e.a = \"error\"\n Traceback (most recent call last):\n AttributeError: 'str' object has no attribute 'e'\n >>> d.setattr('convert_mapping', True)\n >>> d.a.b = {'c': {'d': 1}, 'e.f' : 2}\n >>> d.a.b.c.d\n 1\n >>> d['c.d'] = {'c': {'d': 1}, 'e.f' : 2}\n >>> d.c.d['e.f']\n 2\n >>> d.setattr('convert_mapping', False)\n >>> d.set('e.f', {'c': {'d': 1}, 'e.f' : 2}, convert_mapping=True)\n >>> d['e.f']['c.d']\n 1\n \"\"\"\n# pylint: disable=W0642\nfull_name = name\ndelimiter = self.getattr(\"delimiter\", \".\")\nif convert_mapping is None:\nconvert_mapping = self.getattr(\"convert_mapping\", False)\ndefault_factory = self.getattr(\"default_factory\", self.empty)\ntry:\nwhile isinstance(name, str) and delimiter in name:\nname, rest = name.split(delimiter, 1)\nif name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\nself, name = getattr(self, name), rest\nelif name not in self and isinstance(self, Mapping):\ndefault = (\nself.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n)\nself, name = default, rest\nelse:\nself, name = self[name], rest\nif isinstance(self, NestedDict):\ndefault_factory = self.getattr(\"default_factory\", self.empty)\nexcept (AttributeError, TypeError):\nraise KeyError(name) from None\nif (\nconvert_mapping\nand isinstance(value, Mapping)\nand not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\nand not isinstance(value, Variable)\n):\ntry:\nvalue = default_factory(**value)\nexcept TypeError:\nvalue = default_factory(value)\nif isinstance(self, NestedDict):\nsuper().set(name, value)\nelif isinstance(self, Mapping):\ndict.__setitem__(self, name, value)\nelse:\nraise ValueError(\nf\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n)\n
"},{"location":"nested_dict/#chanfig.NestedDict.sort","title":"sort(key=None, reverse=False, recursive=True)
","text":"Sort NestedDict
.
Parameters:
Name Type Description Defaultrecursive
bool
Whether to apply sort
recursively.
True
Returns:
Type DescriptionNestedDict
Examples:
Python Console Session>>> l = [1]\n>>> d = NestedDict({\"a\": 1, \"b\": {\"c\": 2, \"d\": 3}, \"b.e.f\": l})\n>>> d.sort().dict()\n{'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n>>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n>>> d.sort().dict()\n{'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n>>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n>>> d.sort(recursive=False).dict()\n{'a': 1, 'b': {'e': {'f': [1]}, 'd': 3, 'c': 2}}\n>>> l.append(2)\n>>> d.b.e.f\n[1, 2]\n
Source code in chanfig/nested_dict.py
Pythondef sort(self, key: Callable | None = None, reverse: bool = False, recursive: bool = True) -> NestedDict:\nr\"\"\"\n Sort `NestedDict`.\n Args:\n recursive (bool): Whether to apply `sort` recursively.\n Returns:\n (NestedDict):\n Examples:\n >>> l = [1]\n >>> d = NestedDict({\"a\": 1, \"b\": {\"c\": 2, \"d\": 3}, \"b.e.f\": l})\n >>> d.sort().dict()\n {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n >>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n >>> d.sort().dict()\n {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': [1]}}}\n >>> d = NestedDict({\"b.e.f\": l, \"b.d\": 3, \"a\": 1, \"b.c\": 2})\n >>> d.sort(recursive=False).dict()\n {'a': 1, 'b': {'e': {'f': [1]}, 'd': 3, 'c': 2}}\n >>> l.append(2)\n >>> d.b.e.f\n [1, 2]\n \"\"\"\nif recursive:\nfor value in self.values():\nif isinstance(value, FlatDict):\nvalue.sort(key=key, reverse=reverse)\nreturn super().sort(key=key, reverse=reverse) # type: ignore\n
"},{"location":"nested_dict/#chanfig.NestedDict.validate","title":"validate()
","text":"Validate NestedDict
.
Raises:
Type DescriptionTypeError
If Variable
has invalid type.
ValueError
If Variable
has invalid value.
Examples:
Python Console Session>>> d = NestedDict({\"i.d\": Variable(1016, type=int, validator=lambda x: x > 0)})\n>>> d = NestedDict({\"i.d\": Variable(1016, type=str, validator=lambda x: x > 0)})\nTraceback (most recent call last):\nTypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n>>> d = NestedDict({\"i.d\": Variable(-1, type=int, validator=lambda x: x > 0)})\nTraceback (most recent call last):\nValueError: 'd' has invalid value. Value -1 is not valid.\n
Source code in chanfig/nested_dict.py
Pythondef validate(self) -> None:\nr\"\"\"\n Validate `NestedDict`.\n Raises:\n TypeError: If `Variable` has invalid type.\n ValueError: If `Variable` has invalid value.\n Examples:\n >>> d = NestedDict({\"i.d\": Variable(1016, type=int, validator=lambda x: x > 0)})\n >>> d = NestedDict({\"i.d\": Variable(1016, type=str, validator=lambda x: x > 0)})\n Traceback (most recent call last):\n TypeError: 'd' has invalid type. Value 1016 is not of type <class 'str'>.\n >>> d = NestedDict({\"i.d\": Variable(-1, type=int, validator=lambda x: x > 0)})\n Traceback (most recent call last):\n ValueError: 'd' has invalid value. Value -1 is not valid.\n \"\"\"\nself.apply_(self._validate)\n
"},{"location":"parser/","title":"ConfigParser","text":" Bases: ArgumentParser
Parser to parse command-line arguments for CHANfiG.
ConfigParser
is a subclass of argparse.ArgumentParser
. It provides a new parse
method to parse command-line arguments to CHANfiG.Config
object.
Different to ArgumentParser.parse_args
, ConfigParser.parse
will try to parse any command-line arguments, even if they are not pre-defined by ArgumentParser.add_argument
. This allows to relief the burden of adding tons of arguments for each tuneable parameter. In the meantime, there is no mechanism to notify you if you made a typo in command-line arguments.
Note that ArgumentParser.parse_args
method is not overridden in ConfigParser
. This is because it is still possible to construct CHANfiG.Config
with ArgumentParser.parse_args
, which has strict checking on command-line arguments.
chanfig/parser.py
Pythonclass ConfigParser(ArgumentParser): # pylint: disable=C0115\nr\"\"\"\n Parser to parse command-line arguments for CHANfiG.\n `ConfigParser` is a subclass of `argparse.ArgumentParser`.\n It provides a new `parse` method to parse command-line arguments to `CHANfiG.Config` object.\n Different to `ArgumentParser.parse_args`, `ConfigParser.parse` will try to parse any command-line arguments,\n even if they are not pre-defined by `ArgumentParser.add_argument`.\n This allows to relief the burden of adding tons of arguments for each tuneable parameter.\n In the meantime, there is no mechanism to notify you if you made a typo in command-line arguments.\n Note that `ArgumentParser.parse_args` method is not overridden in `ConfigParser`.\n This is because it is still possible to construct `CHANfiG.Config` with `ArgumentParser.parse_args`,\n which has strict checking on command-line arguments.\n \"\"\"\ndef __init__(self, *args: Any, **kwargs: Any):\nsuper().__init__(*args, **kwargs)\nself._registries[\"action\"][None] = StoreAction\nself._registries[\"action\"][\"store\"] = StoreAction\ndef parse_args( # type: ignore\nself, args: Sequence[str] | None = None, namespace: Namespace | None = None\n) -> NestedDict:\nparsed = super().parse_args(args, namespace)\nif isinstance(parsed, Namespace):\nparsed = vars(parsed) # type: ignore\nif not isinstance(parsed, NestedDict):\nparsed = NestedDict({key: value for key, value in parsed.items() if value is not Null}) # type: ignore\nfor key, value in parsed.all_items():\nwith suppress(TypeError, ValueError, SyntaxError):\nvalue = literal_eval(value)\nparsed[key] = value\nreturn parsed # type: ignore\ndef parse( # pylint: disable=R0912\nself,\nargs: Sequence[str] | None = None,\nconfig: Config | NestedDict | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\n) -> Config:\nr\"\"\"\n Parse the arguments for `Config`.\n You may optionally specify a name for `default_config`,\n and CHANfiG will read the file under this name.\n There are three levels of config:\n 1. The base `Config` parsed into this method,\n 2. The base config file located at the path of `default_config` (if specified),\n 3. The config specified in arguments.\n Higher levels override lower levels (i.e. 3 > 2 > 1).\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `Config`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n Returns:\n config: The parsed `Config`.\n Raises:\n ValueError: If `default_config` is specified but not found in args,\n and `no_default_config_action` is neither `warn` nor `ignore`.\n ValueError: If `no_default_config_action` is not in `raise`, `warn` and `ignore`.\n See Also:\n [`parse_config`][chanfig.ConfigParser.parse_config]: Only parse valid config arguments.\n Examples:\n Note that all examples uses NestedDict instead of Config for avoiding circular import.\n >>> p = ConfigParser()\n >>> p.parse(['--i.d', '1013', '--f.n', 'chang']).dict()\n {'i': {'d': 1013}, 'f': {'n': 'chang'}}\n Values in command line overrides values in `default_config` file.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2', '--config', 'tests/test.yaml'], default_config='config').dict()\n {'a': 2, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n Values in `default_config` file overrides values in `Config` object.\n >>> p = ConfigParser()\n >>> p.parse(['--config', 'tests/test.yaml'], config=NestedDict(a=2), default_config='config').dict()\n {'a': 1, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n ValueError will be raised when `default_config` is specified but not presented in command line.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config').dict()\n Traceback (most recent call last):\n RuntimeError: default_config is set to config, but not found in args.\n ValueError will be suppressed when `default_config` is specified bug not presented in command line,\n and `no_default_config_action` is set to `ignore` or `warn`.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config', no_default_config_action='ignore').dict()\n {'a': 2}\n ValueError will be raised when `no_default_config_action` is not in `raise`, `ignore`, and `warn`.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config', no_default_config_action='suppress').dict()\n Traceback (most recent call last):\n ValueError: no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got suppress\n \"\"\"\nif args is None:\nargs = sys.argv[1:]\nif config is None:\nfrom .config import Config # pylint: disable=C0415\nconfig = Config()\nelse:\nself.add_config_arguments(config)\nif no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\nraise ValueError(\nf\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n)\n# add the command-line arguments\nkey_value_args = []\nfor arg in args:\nif args == \"--\":\nbreak\nif arg.startswith(\"--\"):\nkey_value_args.append(arg.split(\"=\", maxsplit=1))\nelse:\nif not key_value_args:\ncontinue\nkey_value_args[-1].append(arg)\nfor key_value in key_value_args:\nif key_value[0] not in self:\nif len(key_value) > 2:\nself.add_argument(key_value[0], nargs=\"+\")\nelse:\nself.add_argument(key_value[0])\nparsed = self.parse_args(args)\n# parse the default config file\nif default_config is not None:\nparsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n# parse the command-line arguments\nreturn config.merge(parsed) # type: ignore\ndef parse_config( # pylint: disable=R0912\nself,\nargs: Sequence[str] | None = None,\nconfig: Config | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\n) -> Config:\nr\"\"\"\n Parse the arguments for `Config`.\n You may optionally specify a name for `default_config`,\n and CHANfiG will read the file under this name.\n There are three levels of config:\n 1. The base `Config` parsed into this method,\n 2. The base config file located at the path of `default_config` (if specified),\n 3. The config specified in arguments.\n Higher levels override lower levels (i.e. 3 > 2 > 1).\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `Config`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n Returns:\n config: The parsed `Config`.\n Raises:\n ValueError: If `default_config` is specified but not found in args,\n and `no_default_config_action` is neither `warn` nor `ignore`.\n ValueError: If `no_default_config_action` is not in `raise`, `warn` and `ignore`.\n See Also:\n [`parse`][chanfig.ConfigParser.parse]: Parse all command-line arguments.\n Examples:\n Note that all examples uses NestedDict instead of Config for avoiding circular import.\n >>> p = ConfigParser()\n >>> p.parse_config(['--a', '1'], config=NestedDict(a=2)).dict()\n {'a': 1}\n You can only parse argument that is defined in `Config`.\n error: unrecognized arguments: --b 1\n >>> p = ConfigParser()\n >>> p.parse_config(['--b', '1'], config=NestedDict(a=2)).dict() # doctest: +SKIP\n Traceback (most recent call last):\n SystemExit: 2\n \"\"\"\nif args is None:\nargs = sys.argv[1:]\nif config is None:\nraise ValueError(\"config must be specified\")\nself.add_config_arguments(config)\nif no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\nraise ValueError(\nf\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n)\nparsed = self.parse_args(args)\n# parse the default config file\nif default_config is not None:\nparsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n# parse the command-line arguments\nreturn config.merge(parsed) # type: ignore\ndef add_config_arguments(self, config):\nfor key, value in config.all_items():\nif isinstance(value, Variable):\ndtype = value._type or value.dtype # pylint: disable=W0212\nelif value is not None:\ndtype = type(value)\nelse:\ndtype = None\nname = \"--\" + key\nif name not in self:\nhelp = value._help if isinstance(value, Variable) else None # pylint: disable=W0212,W0622\nif isinstance(value, (list, tuple, dict, set)):\nself.add_argument(name, type=dtype, nargs=\"+\", help=help, dest=key)\nelse:\nself.add_argument(name, type=dtype, help=help, dest=key)\ndef merge_default_config(self, parsed, default_config: str, no_default_config_action: str = \"raise\") -> NestedDict:\nmessage = f\"default_config is set to {default_config}, but not found in args.\"\nif default_config in parsed:\npath = parsed[default_config]\nwarn(f\"Config has 'default_config={path}' specified, its values will override values in Config\")\nreturn NestedDict.load(path).merge(parsed) # type: ignore\nif no_default_config_action == \"ignore\":\npass\nelif no_default_config_action == \"warn\":\nwarn(message, category=RuntimeWarning, stacklevel=2)\nelse:\nraise RuntimeError(message)\nreturn parsed\n@staticmethod\ndef identity(string):\nr\"\"\"\n https://stackoverflow.com/questions/69896931/cant-pickle-local-object-argumentparser-init-locals-identity\n \"\"\"\nreturn string\ndef __contains__(self, name: str):\nif name in self._option_string_actions:\nreturn True\nreturn False\n
"},{"location":"parser/#chanfig.ConfigParser.identity","title":"identity(string)
staticmethod
","text":"Source code in chanfig/parser.py
Python@staticmethod\ndef identity(string):\nr\"\"\"\n https://stackoverflow.com/questions/69896931/cant-pickle-local-object-argumentparser-init-locals-identity\n \"\"\"\nreturn string\n
"},{"location":"parser/#chanfig.ConfigParser.parse","title":"parse(args=None, config=None, default_config=None, no_default_config_action='raise')
","text":"Parse the arguments for Config
.
You may optionally specify a name for default_config
, and CHANfiG will read the file under this name.
There are three levels of config:
Config
parsed into this method,default_config
(if specified),Higher levels override lower levels (i.e. 3 > 2 > 1).
Parameters:
Name Type Description Defaultargs
Iterable[str] | None
Command-line arguments. Defaults to None
.
None
default_config
str | None
Path to default config file. Defaults to Config
.
None
no_default_config_action
str
Action when default_config
is not found. Can be one of [\"raise\", \"warn\", \"ignore\"]
. Defaults to \"raise\"
.
'raise'
Returns:
Name Type Descriptionconfig
Config
The parsed Config
.
Raises:
Type DescriptionValueError
If default_config
is specified but not found in args, and no_default_config_action
is neither warn
nor ignore
.
ValueError
If no_default_config_action
is not in raise
, warn
and ignore
.
parse_config
: Only parse valid config arguments.
Examples:
Note that all examples uses NestedDict instead of Config for avoiding circular import.
Python Console Session>>> p = ConfigParser()\n>>> p.parse(['--i.d', '1013', '--f.n', 'chang']).dict()\n{'i': {'d': 1013}, 'f': {'n': 'chang'}}\n
Values in command line overrides values in default_config
file.
>>> p = ConfigParser()\n>>> p.parse(['--a', '2', '--config', 'tests/test.yaml'], default_config='config').dict()\n{'a': 2, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n
Values in default_config
file overrides values in Config
object.
>>> p = ConfigParser()\n>>> p.parse(['--config', 'tests/test.yaml'], config=NestedDict(a=2), default_config='config').dict()\n{'a': 1, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n
ValueError will be raised when default_config
is specified but not presented in command line.
>>> p = ConfigParser()\n>>> p.parse(['--a', '2'], default_config='config').dict()\nTraceback (most recent call last):\nRuntimeError: default_config is set to config, but not found in args.\n
ValueError will be suppressed when default_config
is specified bug not presented in command line, and no_default_config_action
is set to ignore
or warn
.
>>> p = ConfigParser()\n>>> p.parse(['--a', '2'], default_config='config', no_default_config_action='ignore').dict()\n{'a': 2}\n
ValueError will be raised when no_default_config_action
is not in raise
, ignore
, and warn
.
>>> p = ConfigParser()\n>>> p.parse(['--a', '2'], default_config='config', no_default_config_action='suppress').dict()\nTraceback (most recent call last):\nValueError: no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got suppress\n
Source code in chanfig/parser.py
Pythondef parse( # pylint: disable=R0912\nself,\nargs: Sequence[str] | None = None,\nconfig: Config | NestedDict | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\n) -> Config:\nr\"\"\"\n Parse the arguments for `Config`.\n You may optionally specify a name for `default_config`,\n and CHANfiG will read the file under this name.\n There are three levels of config:\n 1. The base `Config` parsed into this method,\n 2. The base config file located at the path of `default_config` (if specified),\n 3. The config specified in arguments.\n Higher levels override lower levels (i.e. 3 > 2 > 1).\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `Config`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n Returns:\n config: The parsed `Config`.\n Raises:\n ValueError: If `default_config` is specified but not found in args,\n and `no_default_config_action` is neither `warn` nor `ignore`.\n ValueError: If `no_default_config_action` is not in `raise`, `warn` and `ignore`.\n See Also:\n [`parse_config`][chanfig.ConfigParser.parse_config]: Only parse valid config arguments.\n Examples:\n Note that all examples uses NestedDict instead of Config for avoiding circular import.\n >>> p = ConfigParser()\n >>> p.parse(['--i.d', '1013', '--f.n', 'chang']).dict()\n {'i': {'d': 1013}, 'f': {'n': 'chang'}}\n Values in command line overrides values in `default_config` file.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2', '--config', 'tests/test.yaml'], default_config='config').dict()\n {'a': 2, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n Values in `default_config` file overrides values in `Config` object.\n >>> p = ConfigParser()\n >>> p.parse(['--config', 'tests/test.yaml'], config=NestedDict(a=2), default_config='config').dict()\n {'a': 1, 'b': 2, 'c': 3, 'config': 'tests/test.yaml'}\n ValueError will be raised when `default_config` is specified but not presented in command line.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config').dict()\n Traceback (most recent call last):\n RuntimeError: default_config is set to config, but not found in args.\n ValueError will be suppressed when `default_config` is specified bug not presented in command line,\n and `no_default_config_action` is set to `ignore` or `warn`.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config', no_default_config_action='ignore').dict()\n {'a': 2}\n ValueError will be raised when `no_default_config_action` is not in `raise`, `ignore`, and `warn`.\n >>> p = ConfigParser()\n >>> p.parse(['--a', '2'], default_config='config', no_default_config_action='suppress').dict()\n Traceback (most recent call last):\n ValueError: no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got suppress\n \"\"\"\nif args is None:\nargs = sys.argv[1:]\nif config is None:\nfrom .config import Config # pylint: disable=C0415\nconfig = Config()\nelse:\nself.add_config_arguments(config)\nif no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\nraise ValueError(\nf\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n)\n# add the command-line arguments\nkey_value_args = []\nfor arg in args:\nif args == \"--\":\nbreak\nif arg.startswith(\"--\"):\nkey_value_args.append(arg.split(\"=\", maxsplit=1))\nelse:\nif not key_value_args:\ncontinue\nkey_value_args[-1].append(arg)\nfor key_value in key_value_args:\nif key_value[0] not in self:\nif len(key_value) > 2:\nself.add_argument(key_value[0], nargs=\"+\")\nelse:\nself.add_argument(key_value[0])\nparsed = self.parse_args(args)\n# parse the default config file\nif default_config is not None:\nparsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n# parse the command-line arguments\nreturn config.merge(parsed) # type: ignore\n
"},{"location":"parser/#chanfig.ConfigParser.parse_config","title":"parse_config(args=None, config=None, default_config=None, no_default_config_action='raise')
","text":"Parse the arguments for Config
.
You may optionally specify a name for default_config
, and CHANfiG will read the file under this name.
There are three levels of config:
Config
parsed into this method,default_config
(if specified),Higher levels override lower levels (i.e. 3 > 2 > 1).
Parameters:
Name Type Description Defaultargs
Iterable[str] | None
Command-line arguments. Defaults to None
.
None
default_config
str | None
Path to default config file. Defaults to Config
.
None
no_default_config_action
str
Action when default_config
is not found. Can be one of [\"raise\", \"warn\", \"ignore\"]
. Defaults to \"raise\"
.
'raise'
Returns:
Name Type Descriptionconfig
Config
The parsed Config
.
Raises:
Type DescriptionValueError
If default_config
is specified but not found in args, and no_default_config_action
is neither warn
nor ignore
.
ValueError
If no_default_config_action
is not in raise
, warn
and ignore
.
parse
: Parse all command-line arguments.
Examples:
Note that all examples uses NestedDict instead of Config for avoiding circular import.
Python Console Session>>> p = ConfigParser()\n>>> p.parse_config(['--a', '1'], config=NestedDict(a=2)).dict()\n{'a': 1}\n
You can only parse argument that is defined in Config
. error: unrecognized arguments: \u2013b 1
>>> p = ConfigParser()\n>>> p.parse_config(['--b', '1'], config=NestedDict(a=2)).dict()\nTraceback (most recent call last):\nSystemExit: 2\n
Source code in chanfig/parser.py
Pythondef parse_config( # pylint: disable=R0912\nself,\nargs: Sequence[str] | None = None,\nconfig: Config | None = None,\ndefault_config: str | None = None,\nno_default_config_action: str = \"raise\",\n) -> Config:\nr\"\"\"\n Parse the arguments for `Config`.\n You may optionally specify a name for `default_config`,\n and CHANfiG will read the file under this name.\n There are three levels of config:\n 1. The base `Config` parsed into this method,\n 2. The base config file located at the path of `default_config` (if specified),\n 3. The config specified in arguments.\n Higher levels override lower levels (i.e. 3 > 2 > 1).\n Args:\n args (Iterable[str] | None, optional): Command-line arguments. Defaults to `None`.\n default_config (str | None, optional): Path to default config file. Defaults to `Config`.\n no_default_config_action (str, optional): Action when `default_config` is not found.\n Can be one of `[\"raise\", \"warn\", \"ignore\"]`. Defaults to `\"raise\"`.\n Returns:\n config: The parsed `Config`.\n Raises:\n ValueError: If `default_config` is specified but not found in args,\n and `no_default_config_action` is neither `warn` nor `ignore`.\n ValueError: If `no_default_config_action` is not in `raise`, `warn` and `ignore`.\n See Also:\n [`parse`][chanfig.ConfigParser.parse]: Parse all command-line arguments.\n Examples:\n Note that all examples uses NestedDict instead of Config for avoiding circular import.\n >>> p = ConfigParser()\n >>> p.parse_config(['--a', '1'], config=NestedDict(a=2)).dict()\n {'a': 1}\n You can only parse argument that is defined in `Config`.\n error: unrecognized arguments: --b 1\n >>> p = ConfigParser()\n >>> p.parse_config(['--b', '1'], config=NestedDict(a=2)).dict() # doctest: +SKIP\n Traceback (most recent call last):\n SystemExit: 2\n \"\"\"\nif args is None:\nargs = sys.argv[1:]\nif config is None:\nraise ValueError(\"config must be specified\")\nself.add_config_arguments(config)\nif no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\nraise ValueError(\nf\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n)\nparsed = self.parse_args(args)\n# parse the default config file\nif default_config is not None:\nparsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n# parse the command-line arguments\nreturn config.merge(parsed) # type: ignore\n
"},{"location":"registry/","title":"Registry","text":" Bases: NestedDict
Registry
for components.
Registry provides 3 core functionalities:
To facilitate the usage scenario, registry
is designed to be a decorator. You could register a component by simply calling registry.register
, and it will be registered with its name. You may also specify the name of the component by calling registry.register(name=\"ComponentName\")
.
build
makes it easy to construct a component from a configuration. build
automatically determines the component to construct by the name
field in the configuration. So you could either call registry.build(config)
or registry.build(**config)
. Beyond this, build
is just a syntax sugar for registry.init(registry.lookup(name), *args, **kwargs)
.
lookup
is used to lookup for a component by its name. By default, lookup
internally calls NestedDict.get
, but you may override it to provide more functionalities.
init
is used to construct a component. By default, init
internally calls cls(*args, **kwargs)
, but you may override it to provide more functionalities.
Registry
inherits from NestedDict
.
Therefore, Registry
comes in a nested structure by nature. You could create a sub-registry by simply calling registry.sub_registry = Registry
, and access through registry.sub_registry.register()
.
Examples:
Python Console Session>>> registry = Registry()\n>>> @registry.register\n... @registry.register(\"Module1\")\n... class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> module = registry.register(Module, \"Module2\")\n>>> registry\nRegistry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n)\n>>> module = registry.register(Module, \"Module\")\nTraceback (most recent call last):\nValueError: Component with name Module already registered.\n>>> registry.lookup(\"Module\")\n<class 'chanfig.registry.Module'>\n>>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n>>> # registry.register(Module)\n>>> module = registry.build(config[\"module\"])\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a\n1\n>>> module.b\n2\n
Source code in chanfig/registry.py
Pythonclass Registry(NestedDict): # type: ignore\n\"\"\"\n `Registry` for components.\n Registry provides 3 core functionalities:\n - Register a new component.\n - Lookup for a component.\n - Build a component.\n To facilitate the usage scenario, `registry` is designed to be a decorator.\n You could register a component by simply calling `registry.register`, and it will be registered with its name.\n You may also specify the name of the component by calling `registry.register(name=\"ComponentName\")`.\n `build` makes it easy to construct a component from a configuration.\n `build` automatically determines the component to construct by the `name` field in the configuration.\n So you could either call `registry.build(config)` or `registry.build(**config)`.\n Beyond this, `build` is just a syntax sugar for `registry.init(registry.lookup(name), *args, **kwargs)`.\n `lookup` is used to lookup for a component by its name.\n By default, `lookup` internally calls `NestedDict.get`, but you may override it to provide more functionalities.\n `init` is used to construct a component.\n By default, `init` internally calls `cls(*args, **kwargs)`, but you may override it to provide more functionalities.\n Notes:\n `Registry` inherits from `NestedDict`.\n Therefore, `Registry` comes in a nested structure by nature.\n You could create a sub-registry by simply calling `registry.sub_registry = Registry`,\n and access through `registry.sub_registry.register()`.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... @registry.register(\"Module1\")\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> module = registry.register(Module, \"Module2\")\n >>> registry\n Registry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n )\n >>> module = registry.register(Module, \"Module\")\n Traceback (most recent call last):\n ValueError: Component with name Module already registered.\n >>> registry.lookup(\"Module\")\n <class 'chanfig.registry.Module'>\n >>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n >>> # registry.register(Module)\n >>> module = registry.build(config[\"module\"])\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n \"\"\"\noverride: bool = False\ndef __init__(self, override: bool | None = None, fallback: bool | None = None):\nsuper().__init__(fallback=fallback)\nif override is not None:\nself.setattr(\"override\", override)\ndef register(self, component: Any = None, name: Any | None = None) -> Callable:\nr\"\"\"\n Register a new component.\n Args:\n component: The component to register.\n name: The name of the component.\n Returns:\n component: The registered component.\n Registered component are expected to be `Callable`.\n Raises:\n ValueError: If the component with the same name already registered and `Registry.override=False`.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... @registry.register(\"Module1\")\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> module = registry.register(Module, \"Module2\")\n >>> registry\n Registry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n )\n \"\"\"\nif name in self and not self.override:\nraise ValueError(f\"Component with name {name} already registered.\")\n# Registry.register()\nif name is not None:\nself.set(name, component)\nreturn component # type: ignore\n# @Registry.register\nif callable(component) and name is None:\nself.set(component.__name__, component)\nreturn component\n# @Registry.register()\ndef decorator(name: Any | None = None):\n@wraps(self.register)\ndef wrapper(component):\nif name is None:\nself.set(component.__name__, component)\nelse:\nself.set(name, component)\nreturn component\nreturn wrapper\nreturn decorator(component)\ndef lookup(self, name: str) -> Any:\nr\"\"\"\n Lookup for a component.\n Args:\n name:\n Returns:\n (Any): The component.\n Raises:\n KeyError: If the component is not registered.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> registry.lookup(\"Module\")\n <class 'chanfig.registry.Module'>\n \"\"\"\nreturn self[name]\n@staticmethod\ndef init(cls: Callable, *args: Any, **kwargs: Any) -> Any: # type: ignore # pylint: disable=W0211\nr\"\"\"\n Constructor of component.\n Args:\n cls: The component to construct.\n *args: The arguments to pass to the component.\n **kwargs: The keyword arguments to pass to the component.\n Returns:\n (Any):\n Examples:\n >>> class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> kwargs = {\"a\": 1, \"b\": 2}\n >>> module = Registry.init(Module, **kwargs)\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n \"\"\"\nreturn cls(*args, **kwargs)\ndef build(self, name: str | Mapping, *args: Any, **kwargs: Any) -> Any:\nr\"\"\"\n Build a component.\n Args:\n name (str | Mapping):\n If its a `Mapping`, it must contain `\"name\"` as a member, the rest will be treated as `**kwargs`.\n Note that values in `kwargs` will override values in `name` if its a `Mapping`.\n *args: The arguments to pass to the component.\n **kwargs: The keyword arguments to pass to the component.\n Returns:\n (Any):\n Raises:\n KeyError: If the component is not registered.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n >>> # registry.register(Module)\n >>> module = registry.build(**config[\"module\"])\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n >>> module = registry.build(config[\"module\"], a=2)\n >>> module.a\n 2\n \"\"\"\nif isinstance(name, Mapping):\nname = deepcopy(name)\nname, kwargs = name.pop(\"name\"), dict(name, **kwargs) # type: ignore\nreturn self.init(self.lookup(name), *args, **kwargs) # type: ignore\n
"},{"location":"registry/#chanfig.Registry.build","title":"build(name, *args, **kwargs)
","text":"Build a component.
Parameters:
Name Type Description Defaultname
str | Mapping
If its a Mapping
, it must contain \"name\"
as a member, the rest will be treated as **kwargs
. Note that values in kwargs
will override values in name
if its a Mapping
.
*args
Any
The arguments to pass to the component.
()
**kwargs
Any
The keyword arguments to pass to the component.
{}
Returns:
Type DescriptionAny
Raises:
Type DescriptionKeyError
If the component is not registered.
Examples:
Python Console Session>>> registry = Registry()\n>>> @registry.register\n... class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n>>> # registry.register(Module)\n>>> module = registry.build(**config[\"module\"])\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a\n1\n>>> module.b\n2\n>>> module = registry.build(config[\"module\"], a=2)\n>>> module.a\n2\n
Source code in chanfig/registry.py
Pythondef build(self, name: str | Mapping, *args: Any, **kwargs: Any) -> Any:\nr\"\"\"\n Build a component.\n Args:\n name (str | Mapping):\n If its a `Mapping`, it must contain `\"name\"` as a member, the rest will be treated as `**kwargs`.\n Note that values in `kwargs` will override values in `name` if its a `Mapping`.\n *args: The arguments to pass to the component.\n **kwargs: The keyword arguments to pass to the component.\n Returns:\n (Any):\n Raises:\n KeyError: If the component is not registered.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> config = {\"module\": {\"name\": \"Module\", \"a\": 1, \"b\": 2}}\n >>> # registry.register(Module)\n >>> module = registry.build(**config[\"module\"])\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n >>> module = registry.build(config[\"module\"], a=2)\n >>> module.a\n 2\n \"\"\"\nif isinstance(name, Mapping):\nname = deepcopy(name)\nname, kwargs = name.pop(\"name\"), dict(name, **kwargs) # type: ignore\nreturn self.init(self.lookup(name), *args, **kwargs) # type: ignore\n
"},{"location":"registry/#chanfig.Registry.init","title":"init(*args, **kwargs)
staticmethod
","text":"Constructor of component.
Parameters:
Name Type Description Defaultcls
Callable
The component to construct.
required*args
Any
The arguments to pass to the component.
()
**kwargs
Any
The keyword arguments to pass to the component.
{}
Returns:
Type DescriptionAny
Examples:
Python Console Session>>> class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> kwargs = {\"a\": 1, \"b\": 2}\n>>> module = Registry.init(Module, **kwargs)\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a\n1\n>>> module.b\n2\n
Source code in chanfig/registry.py
Python@staticmethod\ndef init(cls: Callable, *args: Any, **kwargs: Any) -> Any: # type: ignore # pylint: disable=W0211\nr\"\"\"\n Constructor of component.\n Args:\n cls: The component to construct.\n *args: The arguments to pass to the component.\n **kwargs: The keyword arguments to pass to the component.\n Returns:\n (Any):\n Examples:\n >>> class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> kwargs = {\"a\": 1, \"b\": 2}\n >>> module = Registry.init(Module, **kwargs)\n >>> type(module)\n <class 'chanfig.registry.Module'>\n >>> module.a\n 1\n >>> module.b\n 2\n \"\"\"\nreturn cls(*args, **kwargs)\n
"},{"location":"registry/#chanfig.Registry.lookup","title":"lookup(name)
","text":"Lookup for a component.
Parameters:
Name Type Description Defaultname
str
required Returns:
Type DescriptionAny
The component.
Raises:
Type DescriptionKeyError
If the component is not registered.
Examples:
Python Console Session>>> registry = Registry()\n>>> @registry.register\n... class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> registry.lookup(\"Module\")\n<class 'chanfig.registry.Module'>\n
Source code in chanfig/registry.py
Pythondef lookup(self, name: str) -> Any:\nr\"\"\"\n Lookup for a component.\n Args:\n name:\n Returns:\n (Any): The component.\n Raises:\n KeyError: If the component is not registered.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> registry.lookup(\"Module\")\n <class 'chanfig.registry.Module'>\n \"\"\"\nreturn self[name]\n
"},{"location":"registry/#chanfig.Registry.register","title":"register(component=None, name=None)
","text":"Register a new component.
Parameters:
Name Type Description Defaultcomponent
Any
The component to register.
None
name
Any | None
The name of the component.
None
Returns:
Name Type Descriptioncomponent
Callable
The registered component. Registered component are expected to be Callable
.
Raises:
Type DescriptionValueError
If the component with the same name already registered and Registry.override=False
.
Examples:
Python Console Session>>> registry = Registry()\n>>> @registry.register\n... @registry.register(\"Module1\")\n... class Module:\n... def __init__(self, a, b):\n... self.a = a\n... self.b = b\n>>> module = registry.register(Module, \"Module2\")\n>>> registry\nRegistry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n)\n
Source code in chanfig/registry.py
Pythondef register(self, component: Any = None, name: Any | None = None) -> Callable:\nr\"\"\"\n Register a new component.\n Args:\n component: The component to register.\n name: The name of the component.\n Returns:\n component: The registered component.\n Registered component are expected to be `Callable`.\n Raises:\n ValueError: If the component with the same name already registered and `Registry.override=False`.\n Examples:\n >>> registry = Registry()\n >>> @registry.register\n ... @registry.register(\"Module1\")\n ... class Module:\n ... def __init__(self, a, b):\n ... self.a = a\n ... self.b = b\n >>> module = registry.register(Module, \"Module2\")\n >>> registry\n Registry(\n ('Module1'): <class 'chanfig.registry.Module'>\n ('Module'): <class 'chanfig.registry.Module'>\n ('Module2'): <class 'chanfig.registry.Module'>\n )\n \"\"\"\nif name in self and not self.override:\nraise ValueError(f\"Component with name {name} already registered.\")\n# Registry.register()\nif name is not None:\nself.set(name, component)\nreturn component # type: ignore\n# @Registry.register\nif callable(component) and name is None:\nself.set(component.__name__, component)\nreturn component\n# @Registry.register()\ndef decorator(name: Any | None = None):\n@wraps(self.register)\ndef wrapper(component):\nif name is None:\nself.set(component.__name__, component)\nelse:\nself.set(name, component)\nreturn component\nreturn wrapper\nreturn decorator(component)\n
"},{"location":"utils/","title":"Utilities","text":" Bases: type
Metaclass for Singleton Classes.
Source code inchanfig/utils.py
Pythonclass Singleton(type):\nr\"\"\"\n Metaclass for Singleton Classes.\n \"\"\"\n__instances__: Mapping[type, object] = {}\ndef __call__(cls, *args: Any, **kwargs: Any):\nif cls not in cls.__instances__:\ncls.__instances__[cls] = super().__call__(*args, **kwargs) # type: ignore\nreturn cls.__instances__[cls]\n
options: heading_level: 0
"},{"location":"utils/#chanfigutilsnull","title":"chanfig.utils.Null","text":"Null
is an instance of NULL
.
Since the metaclass of NULL
is Singleton
, it is advised to use obj is Null
to determine if obj
is Null
.
NULL class.
get
method in CHANfiG may accept None
or Ellipse
(...
) as value of default
. Therefore, it is mandatory to have a different default value for default
.
Null
is an instance of NULL
and is recommended to be used as obj is Null
.
chanfig/utils.py
Pythonclass NULL(metaclass=Singleton):\nr\"\"\"\n NULL class.\n `get` method in CHANfiG may accept `None` or `Ellipse`(`...`) as value of `default`.\n Therefore, it is mandatory to have a different default value for `default`.\n `Null` is an instance of `NULL` and is recommended to be used as `obj is Null`.\n \"\"\"\ndef __repr__(self):\nreturn \"Null\"\ndef __nonzero__(self):\nreturn False\ndef __len__(self):\nreturn 0\ndef __call__(self, *args: Any, **kwargs: Any):\nreturn self\ndef __contains__(self, name):\nreturn False\ndef __iter__(self):\nreturn self\ndef __next__(self):\nraise StopIteration\ndef __getattr__(self, name):\nreturn self\ndef __getitem__(self, index):\nreturn self\n
options: heading_level: 0
Bases: JSONEncoder
JSON encoder for Config.
Source code inchanfig/utils.py
Pythonclass JsonEncoder(JSONEncoder):\nr\"\"\"\n JSON encoder for Config.\n \"\"\"\ndef default(self, o: Any) -> Any:\nif hasattr(o, \"__json__\"):\nreturn o.__json__()\nreturn super().default(o)\n
options: heading_level: 0
Bases: SafeDumper
YAML Dumper for Config.
Source code inchanfig/utils.py
Pythonclass YamlDumper(SafeDumper): # pylint: disable=R0903\nr\"\"\"\n YAML Dumper for Config.\n \"\"\"\ndef increase_indent(self, flow: bool = False, indentless: bool = False): # pylint: disable=W0235\nreturn super().increase_indent(flow, indentless)\n
options: heading_level: 0
Bases: SafeLoader
YAML Loader for Config.
Source code inchanfig/utils.py
Pythonclass YamlLoader(SafeLoader): # pylint: disable=R0901,R0903\nr\"\"\"\n YAML Loader for Config.\n \"\"\"\n
options: heading_level: 0
"},{"location":"variable/","title":"Variable","text":" Bases: Generic[V]
Mutable wrapper for immutable objects.
Parameters:
Name Type Description Defaultvalue
Any
The value to wrap.
Null
type
type | None
Desired type of the value.
None
choices
list | None
Possible values of the value.
None
validator
Callable | None
Callable
that validates the value.
None
required
bool
Whether the value is required.
False
help
str | None
Help message of the value.
None
Raises:
Type DescriptionRuntimeError
If required
is True
and value
is Null
.
TypeError
If type
is specified and value
is not an instance of type
.
ValueError
| If choices
is specified and value
is not in choices
. If validator
is specified and validator
returns False
.
Attributes:
Name Type Descriptionvalue
Any
The wrapped value.
dtype
type
The type of the wrapped value.
NotesVariable
by default wrap the instance type to type of the wrapped object. Therefore, isinstance(Variable(1), int)
will return True
.
To temporarily disable this behaviour, you can call context manager with Variable.unwrapped()
.
To permanently disable this behaviour, you can call Variable.unwrap()
.
Examples:
Python Console Session>>> v = Variable(1)\n>>> n = v\n>>> v, n\n(1, 1)\n>>> v += 1\n>>> v, n\n(2, 2)\n>>> v.value = 3\n>>> v, n\n(3, 3)\n>>> n.set(4)\n>>> v, n\n(4, 4)\n>>> n = 5\n>>> v, n\n(4, 5)\n>>> f'{v} < {n}'\n'4 < 5'\n>>> isinstance(v, int)\nTrue\n>>> type(v)\n<class 'chanfig.variable.Variable'>\n>>> v.dtype\n<class 'int'>\n>>> with v.unwrapped():\n... isinstance(v, int)\nFalse\n>>> v = Variable('hello')\n>>> f'{v}, world!'\n'hello, world!'\n>>> v += ', world!'\n>>> v\n'hello, world!'\n>>> \"hello\" in v\nTrue\n
Source code in chanfig/variable.py
Pythonclass Variable(Generic[V]): # pylint: disable=R0902\nr\"\"\"\n Mutable wrapper for immutable objects.\n Args:\n value: The value to wrap.\n type: Desired type of the value.\n choices: Possible values of the value.\n validator: `Callable` that validates the value.\n required: Whether the value is required.\n help: Help message of the value.\n Raises:\n RuntimeError: If `required` is `True` and `value` is `Null`.\n TypeError: If `type` is specified and `value` is not an instance of `type`.\n ValueError: |\n If `choices` is specified and `value` is not in `choices`.\n If `validator` is specified and `validator` returns `False`.\n Attributes:\n value: The wrapped value.\n dtype: The type of the wrapped value.\n Notes:\n `Variable` by default wrap the instance type to type of the wrapped object.\n Therefore, `isinstance(Variable(1), int)` will return `True`.\n To temporarily disable this behaviour, you can call context manager `with Variable.unwrapped()`.\n To permanently disable this behaviour, you can call `Variable.unwrap()`.\n Examples:\n >>> v = Variable(1)\n >>> n = v\n >>> v, n\n (1, 1)\n >>> v += 1\n >>> v, n\n (2, 2)\n >>> v.value = 3\n >>> v, n\n (3, 3)\n >>> n.set(4)\n >>> v, n\n (4, 4)\n >>> n = 5\n >>> v, n\n (4, 5)\n >>> f'{v} < {n}'\n '4 < 5'\n >>> isinstance(v, int)\n True\n >>> type(v)\n <class 'chanfig.variable.Variable'>\n >>> v.dtype\n <class 'int'>\n >>> with v.unwrapped():\n ... isinstance(v, int)\n False\n >>> v = Variable('hello')\n >>> f'{v}, world!'\n 'hello, world!'\n >>> v += ', world!'\n >>> v\n 'hello, world!'\n >>> \"hello\" in v\n True\n \"\"\"\nwrap_type: bool = True\n_storage: List[Any]\n_type: Optional[type] = None\n_choices: Optional[list] = None\n_validator: Optional[Callable] = None\n_required: bool = False\n_help: Optional[str] = None\ndef __init__( # pylint: disable=R0913\nself,\nvalue: Any = Null,\ntype: type | None = None, # pylint: disable=W0622\nchoices: list | None = None,\nvalidator: Callable | None = None,\nrequired: bool = False,\nhelp: str | None = None, # pylint: disable=W0622\n) -> None:\nself._storage = [value]\nself._type = type\nself._choices = choices\nself._validator = validator\nself._required = required\nself._help = help\n@property # type: ignore\ndef __class__(self) -> type:\nreturn self.value.__class__ if self.wrap_type else type(self)\n@property\ndef value(self) -> Any:\nr\"\"\"\n Fetch the object wrapped in `Variable`.\n \"\"\"\nreturn self._storage[0]\n@value.setter\ndef value(self, value) -> None:\nr\"\"\"\n Assign value to the object wrapped in `Variable`.\n \"\"\"\nself.validate(value)\nself._storage[0] = self._get_value(value)\n@property\ndef dtype(self) -> type:\nr\"\"\"\n Data type of the object wrapped in `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> type(id)\n <class 'chanfig.variable.Variable'>\n >>> id.dtype\n <class 'int'>\n >>> issubclass(id.dtype, int)\n True\n \"\"\"\nreturn self.value.__class__\n@property\ndef storage(self) -> list[Any]:\nr\"\"\"\n Storage of `Variable`.\n \"\"\"\nreturn self._storage\n@property\ndef type(self) -> type | None:\nreturn self._type\n@property\ndef choices(self) -> list | None:\nreturn self._choices\n@property\ndef validator(self) -> Callable | None:\nreturn self._validator\n@property\ndef required(self) -> bool:\nreturn self._required\n@property\ndef help(self) -> str:\nreturn self._help or \"\"\ndef validate(self, *args) -> None:\nr\"\"\"\n Validate if the value is valid.\n \"\"\"\nif len(args) == 0:\nvalue = self.value\nelif len(args) == 1:\nvalue = args[0]\nelse:\nraise ValueError(\"Too many arguments.\")\nif self._required and value is Null:\nraise RuntimeError(\"Value is required.\")\nif self._type is not None and not isinstance(value, self._type):\nraise TypeError(f\"Value {value} is not of type {self._type}.\")\nif self._choices is not None and value not in self._choices:\nraise ValueError(f\"Value {value} is not in choices {self._choices}.\")\nif self._validator is not None and not self._validator(value):\nraise ValueError(f\"Value {value} is not valid.\")\ndef get(self) -> Any:\nr\"\"\"\n Fetch the object wrapped in `Variable`.\n \"\"\"\nreturn self.value\ndef set(self, value) -> None:\nr\"\"\"\n Assign value to the object wrapped in `Variable`.\n `Variable.set` is extremely useful when you want to change the value without changing the reference.\n In `FlatDict.set`, all assignments of `Variable` calls `Variable.set` Internally.\n \"\"\"\nself.value = value\ndef __get__(self, obj, objtype=None):\nreturn self\ndef __set__(self, obj, value):\nself.value = value\ndef to(self, cls: Callable) -> Any: # pylint: disable=C0103\nr\"\"\"\n Convert the object wrapped in `Variable` to target `cls`.\n Args:\n cls: The type to convert to.\n Examples:\n >>> id = Variable(1013)\n >>> id.to(float)\n 1013.0\n >>> id.to(str)\n '1013.0'\n \"\"\"\nself.value = cls(self.value)\nreturn self\ndef int(self) -> int:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `int`.\n Examples:\n >>> id = Variable(1013.0)\n >>> id.int()\n 1013\n \"\"\"\nreturn self.to(int)\ndef float(self) -> float:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `float`.\n Examples:\n >>> id = Variable(1013)\n >>> id.float()\n 1013.0\n \"\"\"\nreturn self.to(float)\ndef str(self) -> str:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `float`.\n Examples:\n >>> id = Variable(1013)\n >>> id.str()\n '1013'\n \"\"\"\nreturn self.to(str)\ndef wrap(self) -> None:\nr\"\"\"\n Wrap the type of `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> id.unwrap()\n >>> isinstance(id, int)\n False\n >>> id.wrap()\n >>> isinstance(id, int)\n True\n \"\"\"\nself.wrap_type = True\ndef unwrap(self) -> None:\nr\"\"\"\n Unwrap the type of `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> id.unwrap()\n >>> isinstance(id, int)\n False\n \"\"\"\nself.wrap_type = False\n@contextmanager\ndef unwrapped(self):\nr\"\"\"\n Context manager which temporarily unwrap the `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> isinstance(id, int)\n True\n >>> with id.unwrapped():\n ... isinstance(id, int)\n False\n \"\"\"\nwrap_type = self.wrap_type\nself.wrap_type = False\ntry:\nyield self\nfinally:\nself.wrap_type = wrap_type\n@staticmethod\ndef _get_value(obj) -> Any:\nif isinstance(obj, Variable):\nreturn obj.value\nreturn obj\ndef __getattr__(self, attr) -> Any:\nreturn getattr(self.value, attr)\ndef __lt__(self, other) -> bool:\nreturn self.value < self._get_value(other)\ndef __le__(self, other) -> bool:\nreturn self.value <= self._get_value(other)\ndef __eq__(self, other) -> bool:\nreturn self.value == self._get_value(other)\ndef __ne__(self, other) -> bool:\nreturn self.value != self._get_value(other)\ndef __ge__(self, other) -> bool:\nreturn self.value >= self._get_value(other)\ndef __gt__(self, other) -> bool:\nreturn self.value > self._get_value(other)\ndef __index__(self):\nreturn self.value.__index__()\ndef __invert__(self):\nreturn ~self.value\ndef __abs__(self):\nreturn abs(self.value)\ndef __add__(self, other):\nreturn Variable(self.value + self._get_value(other))\ndef __radd__(self, other):\nreturn Variable(self._get_value(other) + self.value)\ndef __iadd__(self, other):\nself.value += self._get_value(other)\nreturn self\ndef __and__(self, other):\nreturn Variable(self.value & self._get_value(other))\ndef __rand__(self, other):\nreturn Variable(self._get_value(other) & self.value)\ndef __iand__(self, other):\nself.value &= self._get_value(other)\nreturn self\ndef __floordiv__(self, other):\nreturn Variable(self.value // self._get_value(other))\ndef __rfloordiv__(self, other):\nreturn Variable(self._get_value(other) // self.value)\ndef __ifloordiv__(self, other):\nself.value //= self._get_value(other)\nreturn self\ndef __mod__(self, other):\nreturn Variable(self.value % self._get_value(other))\ndef __rmod__(self, other):\nreturn Variable(self._get_value(other) % self.value)\ndef __imod__(self, other):\nself.value %= self._get_value(other)\nreturn self\ndef __mul__(self, other):\nreturn Variable(self.value * self._get_value(other))\ndef __rmul__(self, other):\nreturn Variable(self._get_value(other) * self.value)\ndef __imul__(self, other):\nself.value *= self._get_value(other)\nreturn self\ndef __matmul__(self, other):\nreturn Variable(self.value @ self._get_value(other))\ndef __rmatmul__(self, other):\nreturn Variable(self._get_value(other) @ self.value)\ndef __imatmul__(self, other):\nself.value @= self._get_value(other)\nreturn self\ndef __pow__(self, other):\nreturn Variable(self.value ** self._get_value(other))\ndef __rpow__(self, other):\nreturn Variable(self._get_value(other) ** self.value)\ndef __ipow__(self, other):\nself.value **= self._get_value(other)\nreturn self\ndef __truediv__(self, other):\nreturn Variable(self.value / self._get_value(other))\ndef __rtruediv__(self, other):\nreturn Variable(self._get_value(other) / self.value)\ndef __itruediv__(self, other):\nself.value /= self._get_value(other)\nreturn self\ndef __sub__(self, other):\nreturn Variable(self.value - self._get_value(other))\ndef __rsub__(self, other):\nreturn Variable(self._get_value(other) - self.value)\ndef __isub__(self, other):\nself.value -= self._get_value(other)\nreturn self\ndef __copy__(self):\nreturn Variable(self.value)\ndef __deepcopy__(self, memo: Mapping | None = None):\nreturn Variable(copy(self.value))\ndef __format__(self, format_spec):\nreturn self.value if isinstance(self, str) else format(self.value, format_spec)\ndef __iter__(self):\nreturn iter(self.value)\ndef __next__(self):\nreturn next(self.value)\ndef __hash__(self):\nreturn hash(self.value)\ndef __repr__(self):\nreturn repr(self.value)\ndef __str__(self):\nreturn self.value if isinstance(self, str) else str(self.value)\ndef __json__(self):\nreturn self.value\ndef __contains__(self, name):\nreturn name in self.value\n
"},{"location":"variable/#chanfig.Variable.dtype","title":"dtype: type
property
","text":"Data type of the object wrapped in Variable
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> type(id)\n<class 'chanfig.variable.Variable'>\n>>> id.dtype\n<class 'int'>\n>>> issubclass(id.dtype, int)\nTrue\n
"},{"location":"variable/#chanfig.Variable.storage","title":"storage: list[Any]
property
","text":"Storage of Variable
.
value: Any
property
writable
","text":"Fetch the object wrapped in Variable
.
float()
","text":"Convert the object wrapped in Variable
to python float
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> id.float()\n1013.0\n
Source code in chanfig/variable.py
Pythondef float(self) -> float:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `float`.\n Examples:\n >>> id = Variable(1013)\n >>> id.float()\n 1013.0\n \"\"\"\nreturn self.to(float)\n
"},{"location":"variable/#chanfig.Variable.get","title":"get()
","text":"Fetch the object wrapped in Variable
.
chanfig/variable.py
Pythondef get(self) -> Any:\nr\"\"\"\n Fetch the object wrapped in `Variable`.\n \"\"\"\nreturn self.value\n
"},{"location":"variable/#chanfig.Variable.int","title":"int()
","text":"Convert the object wrapped in Variable
to python int
.
Examples:
Python Console Session>>> id = Variable(1013.0)\n>>> id.int()\n1013\n
Source code in chanfig/variable.py
Pythondef int(self) -> int:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `int`.\n Examples:\n >>> id = Variable(1013.0)\n >>> id.int()\n 1013\n \"\"\"\nreturn self.to(int)\n
"},{"location":"variable/#chanfig.Variable.set","title":"set(value)
","text":"Assign value to the object wrapped in Variable
.
Variable.set
is extremely useful when you want to change the value without changing the reference.
In FlatDict.set
, all assignments of Variable
calls Variable.set
Internally.
chanfig/variable.py
Pythondef set(self, value) -> None:\nr\"\"\"\n Assign value to the object wrapped in `Variable`.\n `Variable.set` is extremely useful when you want to change the value without changing the reference.\n In `FlatDict.set`, all assignments of `Variable` calls `Variable.set` Internally.\n \"\"\"\nself.value = value\n
"},{"location":"variable/#chanfig.Variable.str","title":"str()
","text":"Convert the object wrapped in Variable
to python float
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> id.str()\n'1013'\n
Source code in chanfig/variable.py
Pythondef str(self) -> str:\nr\"\"\"\n Convert the object wrapped in `Variable` to python `float`.\n Examples:\n >>> id = Variable(1013)\n >>> id.str()\n '1013'\n \"\"\"\nreturn self.to(str)\n
"},{"location":"variable/#chanfig.Variable.to","title":"to(cls)
","text":"Convert the object wrapped in Variable
to target cls
.
Parameters:
Name Type Description Defaultcls
Callable
The type to convert to.
requiredExamples:
Python Console Session>>> id = Variable(1013)\n>>> id.to(float)\n1013.0\n>>> id.to(str)\n'1013.0'\n
Source code in chanfig/variable.py
Pythondef to(self, cls: Callable) -> Any: # pylint: disable=C0103\nr\"\"\"\n Convert the object wrapped in `Variable` to target `cls`.\n Args:\n cls: The type to convert to.\n Examples:\n >>> id = Variable(1013)\n >>> id.to(float)\n 1013.0\n >>> id.to(str)\n '1013.0'\n \"\"\"\nself.value = cls(self.value)\nreturn self\n
"},{"location":"variable/#chanfig.Variable.unwrap","title":"unwrap()
","text":"Unwrap the type of Variable
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> id.unwrap()\n>>> isinstance(id, int)\nFalse\n
Source code in chanfig/variable.py
Pythondef unwrap(self) -> None:\nr\"\"\"\n Unwrap the type of `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> id.unwrap()\n >>> isinstance(id, int)\n False\n \"\"\"\nself.wrap_type = False\n
"},{"location":"variable/#chanfig.Variable.unwrapped","title":"unwrapped()
","text":"Context manager which temporarily unwrap the Variable
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> isinstance(id, int)\nTrue\n>>> with id.unwrapped():\n... isinstance(id, int)\nFalse\n
Source code in chanfig/variable.py
Python@contextmanager\ndef unwrapped(self):\nr\"\"\"\n Context manager which temporarily unwrap the `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> isinstance(id, int)\n True\n >>> with id.unwrapped():\n ... isinstance(id, int)\n False\n \"\"\"\nwrap_type = self.wrap_type\nself.wrap_type = False\ntry:\nyield self\nfinally:\nself.wrap_type = wrap_type\n
"},{"location":"variable/#chanfig.Variable.validate","title":"validate(*args)
","text":"Validate if the value is valid.
Source code inchanfig/variable.py
Pythondef validate(self, *args) -> None:\nr\"\"\"\n Validate if the value is valid.\n \"\"\"\nif len(args) == 0:\nvalue = self.value\nelif len(args) == 1:\nvalue = args[0]\nelse:\nraise ValueError(\"Too many arguments.\")\nif self._required and value is Null:\nraise RuntimeError(\"Value is required.\")\nif self._type is not None and not isinstance(value, self._type):\nraise TypeError(f\"Value {value} is not of type {self._type}.\")\nif self._choices is not None and value not in self._choices:\nraise ValueError(f\"Value {value} is not in choices {self._choices}.\")\nif self._validator is not None and not self._validator(value):\nraise ValueError(f\"Value {value} is not valid.\")\n
"},{"location":"variable/#chanfig.Variable.wrap","title":"wrap()
","text":"Wrap the type of Variable
.
Examples:
Python Console Session>>> id = Variable(1013)\n>>> id.unwrap()\n>>> isinstance(id, int)\nFalse\n>>> id.wrap()\n>>> isinstance(id, int)\nTrue\n
Source code in chanfig/variable.py
Pythondef wrap(self) -> None:\nr\"\"\"\n Wrap the type of `Variable`.\n Examples:\n >>> id = Variable(1013)\n >>> id.unwrap()\n >>> isinstance(id, int)\n False\n >>> id.wrap()\n >>> isinstance(id, int)\n True\n \"\"\"\nself.wrap_type = True\n
"},{"location":"blog/","title":"CHANfiG","text":""},{"location":"zh/#_1","title":"\u4ecb\u7ecd","text":"CHANfiG \u200b\u5e0c\u671b\u200b\u80fd\u200b\u8ba9\u200b\u4f60\u200b\u7684\u200b\u914d\u7f6e\u200b\u66f4\u52a0\u200b\u7b80\u5355\u200b\u3002
\u200b\u8bad\u7ec3\u200b\u4e00\u4e2a\u200b\u673a\u5668\u200b\u5b66\u4e60\u200b\u6a21\u578b\u200b\u6709\u200b\u65e0\u6570\u4e2a\u200b\u53ef\u200b\u8c03\u8282\u200b\u7684\u200b\u53c2\u6570\u200b\u3002 \u200b\u4e3a\u4e86\u200b\u8c03\u8282\u200b\u6240\u6709\u200b\u53c2\u6570\u200b\uff0c\u200b\u7814\u7a76\u5458\u200b\u4eec\u200b\u5e38\u5e38\u200b\u9700\u8981\u200b\u64b0\u5199\u200b\u5de8\u5927\u200b\u7684\u200b\u914d\u7f6e\u6587\u4ef6\u200b\uff0c\u200b\u6709\u65f6\u200b\u751a\u81f3\u200b\u957f\u200b\u8fbe\u200b\u6570\u5343\u200b\u884c\u200b\u3002 \u200b\u5927\u591a\u6570\u200b\u53c2\u6570\u200b\u53ea\u662f\u200b\u65b9\u6cd5\u200b\u9ed8\u8ba4\u200b\u53c2\u6570\u200b\u7684\u200b\u7b80\u5355\u200b\u91cd\u590d\u200b\uff0c\u200b\u8fd9\u200b\u5bfc\u81f4\u200b\u4e86\u200b\u5f88\u591a\u200b\u4e0d\u5fc5\u8981\u200b\u7684\u200b\u58f0\u660e\u200b\u3002 \u200b\u6b64\u5916\u200b\uff0c\u200b\u8c03\u8282\u200b\u8fd9\u4e9b\u200b\u53c2\u6570\u200b\u540c\u6837\u200b\u5f88\u200b\u7e41\u7410\u200b\uff0c\u200b\u9700\u8981\u200b\u5b9a\u4f4d\u200b\u5e76\u200b\u6253\u5f00\u200b\u914d\u7f6e\u6587\u4ef6\u200b\uff0c\u200b\u4f5c\u51fa\u200b\u4fee\u6539\u200b\uff0c\u200b\u7136\u540e\u200b\u4fdd\u5b58\u200b\u5173\u95ed\u200b\u3002 \u200b\u8fd9\u4e2a\u200b\u8fc7\u7a0b\u200b\u6d6a\u8d39\u200b\u4e86\u200b\u65e0\u6570\u200b\u7684\u200b\u5b9d\u8d35\u65f6\u95f4\u200b \u200b\u8fd9\u200b\u65e0\u7591\u200b\u662f\u200b\u4e00\u79cd\u200b\u72af\u7f6a\u200b \u3002 \u200b\u4f7f\u7528\u200bargparse
\u200b\u53ef\u4ee5\u200b\u5728\u200b\u4e00\u5b9a\u200b\u7a0b\u5ea6\u200b\u4e0a\u200b\u7f13\u89e3\u200b\u8c03\u53c2\u200b\u7684\u200b\u4e0d\u53d8\u200b\uff0c\u200b\u4f46\u662f\u200b\uff0c\u200b\u8981\u200b\u8ba9\u200b\u4ed6\u200b\u548c\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u9002\u914d\u200b\u4f9d\u7136\u200b\u9700\u8981\u200b\u5f88\u591a\u200b\u5de5\u4f5c\u200b\uff0c\u200b\u5e76\u4e14\u200b\u7f3a\u4e4f\u200b\u5d4c\u5957\u200b\u4e5f\u200b\u9650\u5236\u200b\u4e86\u200b\u4ed6\u200b\u7684\u200b\u6f5c\u529b\u200b\u3002
CHANfiG \u200b\u65e8\u5728\u200b\u5e26\u6765\u200b\u6539\u53d8\u200b\u3002
\u200b\u4f60\u200b\u53ea\u200b\u9700\u8981\u200b\u5728\u200b\u547d\u4ee4\u884c\u200b\u4e2d\u200b\u8f93\u5165\u200b\u4f60\u200b\u7684\u200b\u4fee\u6539\u200b\uff0c\u200b\u7136\u540e\u200b\u628a\u200b\u5269\u4e0b\u200b\u7684\u200b\u4ea4\u7ed9\u200b CHANfiG\u3002
CHANfiG \u200b\u5f88\u5927\u200b\u7a0b\u5ea6\u200b\u4e0a\u200b\u542f\u53d1\u200b\u81ea\u200bYACS\u3002 \u200b\u4e0d\u540c\u4e8e\u200b YACS \u200b\u7684\u200b\u8303\u5f0f\u200b\uff08\u200b\u4ee3\u7801\u200b + \u200b\u5b9e\u9a8c\u200bE\u200b\u7684\u200bYACS\u200b\u914d\u7f6e\u6587\u4ef6\u200b (+ \u200b\u5916\u90e8\u200b\u4f9d\u8d56\u200b + \u200b\u786c\u4ef6\u200b + \u200b\u5176\u4ed6\u200b\u4ee4\u4eba\u8ba8\u538c\u200b\u7684\u200b\u672f\u8bed\u200b ...) = \u200b\u53ef\u200b\u91cd\u590d\u200b\u7684\u200b\u5b9e\u9a8c\u200bE
\uff09\uff0c CHANfiG \u200b\u7684\u200b\u8303\u5f0f\u200b\u662f\u200b\uff1a
\u200b\u4ee3\u7801\u200b + \u200b\u547d\u4ee4\u884c\u200b\u53c2\u6570\u200b (+ \u200b\u53ef\u9009\u200b\u7684\u200bCHANfiG\u200b\u914d\u7f6e\u6587\u4ef6\u200b + \u200b\u5916\u90e8\u200b\u4f9d\u8d56\u200b + \u200b\u786c\u4ef6\u200b + \u200b\u5176\u4ed6\u200b\u4ee4\u4eba\u8ba8\u538c\u200b\u7684\u200b\u672f\u8bed\u200b ...) = \u200b\u53ef\u200b\u91cd\u590d\u200b\u7684\u200b\u5b9e\u9a8c\u200bE (+ \u200b\u53ef\u9009\u200b\u7684\u200bCHANfiG\u200b\u914d\u7f6e\u6587\u4ef6\u200b)
\u200b\u4e00\u4e2a\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u53ef\u4ee5\u200b\u88ab\u200b\u770b\u4f5c\u200b\u4e00\u4e2a\u200b\u5d4c\u5957\u200b\u7684\u200b\u5b57\u5178\u200b\u7ed3\u6784\u200b\u3002
\u200b\u4f46\u662f\u200b\uff0c\u200b\u9ed8\u8ba4\u200b\u7684\u200b Python \u200b\u5b57\u5178\u200b\u5341\u5206\u200b\u96be\u4ee5\u200b\u64cd\u4f5c\u200b\u3002
\u200b\u8bbf\u95ee\u200b\u5b57\u5178\u200b\u6210\u5458\u200b\u7684\u200b\u552f\u4e00\u200b\u65b9\u5f0f\u200b\u662f\u200bdict['name']
\uff0c\u200b\u8fd9\u200b\u65e0\u7591\u200b\u662f\u200b\u6781\u5176\u200b\u7e41\u7410\u200b\u7684\u200b\u3002 \u200b\u66f4\u200b\u7cdf\u7cd5\u200b\u7684\u200b\uff0c\u200b\u5982\u679c\u200b\u8fd9\u4e2a\u200b\u5b57\u5178\u200b\u548c\u200b\u914d\u7f6e\u200b\u4e00\u6837\u200b\u662f\u200b\u4e00\u4e2a\u200b\u5d4c\u5957\u200b\u7ed3\u6784\u200b\uff0c\u200b\u8bbf\u95ee\u200b\u6210\u5458\u200b\u5c06\u4f1a\u200b\u53d8\u6210\u200b\u7c7b\u4f3c\u200b\u4e8e\u200bdict['parent']['children']['name']
\u200b\u7684\u200b\u6837\u5b50\u200b\u3002
\u200b\u591f\u200b\u4e86\u200b\u5c31\u662f\u200b\u591f\u200b\u4e86\u200b\uff0c\u200b\u662f\u200b\u65f6\u5019\u200b\u505a\u51fa\u200b\u6539\u53d8\u200b\u4e86\u200b\u3002
\u200b\u6211\u4eec\u200b\u9700\u8981\u200b\u5c5e\u6027\u200b\u65b9\u5f0f\u200b\u7684\u200b\u8bbf\u95ee\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6211\u4eec\u200b\u73b0\u5728\u200b\u5c31\u200b\u9700\u8981\u200b\u3002 dict.name
\u200b\u548c\u200bdict.parent.children.name
\u200b\u662f\u200b\u6240\u6709\u200b\u4f60\u200b\u9700\u8981\u200b\u7684\u200b\u3002
\u200b\u5c3d\u7ba1\u200b\u6b64\u524d\u200b\u5df2\u7ecf\u200b\u6709\u200b\u5de5\u4f5c\u200b\u6765\u200b\u5b9e\u73b0\u200b\u7c7b\u4f3c\u200b\u7684\u200b\u5bf9\u200b\u5b57\u5178\u200b\u6210\u5458\u200b\u7684\u200b\u5c5e\u6027\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u3002\u200b\u4f46\u662f\u200b\u4ed6\u4eec\u200b\u7684\u200b Config \u200b\u5bf9\u8c61\u200b\u8981\u4e48\u200b\u4f7f\u7528\u200b\u4e00\u4e2a\u200b\u72ec\u7acb\u200b\u7684\u200b\u5b57\u5178\u200b\u6765\u200b\u5b58\u50a8\u200b\u5c5e\u6027\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u7684\u200b\u4fe1\u606f\u200b\uff08EasyDict\uff09\uff0c\u200b\u800c\u200b\u8fd9\u200b\u53ef\u80fd\u200b\u5bfc\u81f4\u200b\u5c5e\u6027\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u548c\u200b\u5b57\u5178\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u7684\u200b\u4e0d\u200b\u4e00\u81f4\u200b\uff1b\u200b\u8981\u4e48\u200b\u91cd\u65b0\u200b\u4f7f\u7528\u200b\u65e2\u6709\u200b\u7684\u200b__dict__
\u200b\u7136\u540e\u200b\u5bf9\u200b\u5b57\u5178\u200b\u65b9\u5f0f\u200b\u8bbf\u95ee\u200b\u8fdb\u884c\u200b\u91cd\u5b9a\u5411\u200b\uff08ml_collections\uff09\uff0c\u200b\u800c\u200b\u8fd9\u200b\u53ef\u80fd\u200b\u5bfc\u81f4\u200b\u5c5e\u6027\u200b\u548c\u200b\u5b57\u5178\u200b\u6210\u5458\u200b\u5b58\u5728\u200b\u51b2\u7a81\u200b\u3002
\u200b\u4e3a\u4e86\u200b\u89e3\u51b3\u200b\u4e0a\u8ff0\u200b\u9650\u5236\u200b\uff0c\u200b\u6211\u4eec\u200b\u7ee7\u627f\u200b\u4e86\u200b Python \u200b\u5185\u7f6e\u200b\u7684\u200bdict
\u200b\u6765\u200b\u521b\u5efa\u200bFlatDict
\u3001DefaultDict
\u3001NestedDict
\u3001Config
\u200b\u548c\u200bRegistry
\u3002 \u200b\u6211\u4eec\u200b\u540c\u65f6\u200b\u4ecb\u7ecd\u200b\u4e86\u200bVariable
\u200b\u6765\u200b\u5728\u200b\u591a\u4e2a\u200b\u4f4d\u7f6e\u200b\u5171\u4eab\u200b\u503c\u200b\uff0c\u200b\u548c\u200bConfigParser
\u200b\u6765\u200b\u89e3\u6790\u200b\u547d\u4ee4\u884c\u200b\u53c2\u6570\u200b\u3002
FlatDict
\u200b\u5728\u200b\u4e09\u4e2a\u200b\u65b9\u9762\u200b\u5bf9\u200b\u9ed8\u8ba4\u200b\u7684\u200bdict
\u200b\u505a\u51fa\u200b\u6539\u8fdb\u200b\u3002
FlatDict
\u200b\u652f\u6301\u200b\u53d8\u91cf\u200b\u63d2\u503c\u200b\u3002 \u200b\u5c06\u200b\u4e00\u4e2a\u200b\u6210\u5458\u200b\u7684\u200b\u503c\u200b\u8bbe\u7f6e\u200b\u4e3a\u200b${}
\u200b\u5305\u88f9\u200b\u7684\u200b\u53e6\u200b\u4e00\u4e2a\u200b\u6210\u5458\u200b\u540d\u200b\uff0c\u200b\u7136\u540e\u200b\u8c03\u7528\u200binterpolate
\u200b\u65b9\u6cd5\u200b\u3002 \u200b\u8fd9\u4e2a\u200b\u6210\u5458\u200b\u7684\u200b\u503c\u200b\u5c06\u200b\u4f1a\u200b\u81ea\u52a8\u200b\u66ff\u6362\u200b\u4e3a\u200b\u53e6\u200b\u4e00\u4e2a\u200b\u6210\u5458\u200b\u7684\u200b\u503c\u200b\u3002
Python \u200b\u7684\u200bdict
\u200b\u81ea\u200b Python 3.7 \u200b\u4e4b\u540e\u200b\u5c31\u662f\u200b\u6709\u5e8f\u200b\u7684\u200b\uff0c\u200b\u4f46\u662f\u200b\u5e76\u200b\u6ca1\u6709\u200b\u4e00\u4e2a\u200b\u5185\u7f6e\u200b\u7684\u200b\u65b9\u6cd5\u200b\u6765\u200b\u5e2e\u52a9\u200b\u4f60\u200b\u5bf9\u200b\u4e00\u4e2a\u200bdict
\u200b\u8fdb\u884c\u200b\u6392\u5e8f\u200b\u3002FlatDict
\u200b\u652f\u6301\u200bsort
\u200b\u6765\u200b\u5e2e\u52a9\u200b\u4f60\u200b\u7ba1\u7406\u200b\u4f60\u200b\u7684\u200b\u5b57\u5178\u200b\u3002
FlatDict
\u200b\u5305\u62ec\u200b\u4e86\u200b\u4e00\u4e2a\u200bmerge
\u200b\u65b9\u6cd5\u200b\uff0c\u200b\u4ed6\u200b\u4f7f\u200b\u4f60\u200b\u80fd\u200b\u5c06\u200b\u4e00\u4e2a\u200bMapping
\u3001Iterable
\u200b\u6216\u8005\u200b\u4e00\u4e2a\u200b\u8def\u5f84\u200b\u5408\u5e76\u200b\u8fdb\u5165\u200b\u4e00\u4e2a\u200bFlatDict
\u3002 \u200b\u4e0e\u200bupdate
\u200b\u65b9\u6cd5\u200b\u4e0d\u540c\u200b\uff0cmerge
\u200b\u65b9\u6cd5\u200b\u662f\u200b\u8d4b\u503c\u200b\u800c\u200b\u4e0d\u662f\u200b\u66ff\u6362\u200b\uff0c\u200b\u8fd9\u200b\u4f7f\u5f97\u200b\u4ed6\u200b\u80fd\u200b\u66f4\u597d\u200b\u7684\u200b\u4e0e\u200bDefaultDict
\u200b\u914d\u5408\u200b\u4f7f\u7528\u200b\u3002
\u200b\u6b64\u5916\u200b\uff0cFlatDict
\u200b\u5f15\u5165\u200b\u4e86\u200bdifference
\u200b\u548c\u200bintersect
\uff0c\u200b\u8fd9\u4e9b\u200b\u4f7f\u200b\u5176\u200b\u53ef\u4ee5\u200b\u975e\u5e38\u7b80\u5355\u200b\u7684\u200b\u5c06\u200bFlatDict
\u200b\u548c\u200b\u5176\u4ed6\u200bMapping
\u3001Iterable
\u200b\u6216\u8005\u200b\u4e00\u4e2a\u200b\u8def\u5f84\u200b\u8fdb\u884c\u200b\u5bf9\u6bd4\u200b\u3002
FlatDict
\u200b\u652f\u6301\u200b\u4e0e\u200b Pytorch Tensor \u200b\u7c7b\u4f3c\u200b\u7684\u200bto
\u200b\u65b9\u6cd5\u200b\u3002 \u200b\u4f60\u200b\u53ef\u4ee5\u200b\u5f88\u200b\u7b80\u5355\u200b\u7684\u200b\u901a\u8fc7\u200b\u76f8\u540c\u200b\u7684\u200b\u65b9\u5f0f\u200b\u5c06\u200b\u6240\u6709\u200bFlatDict
\u200b\u7684\u200b\u6210\u5458\u200b\u503c\u200b\u8f6c\u6362\u200b\u4e3a\u200b\u67d0\u79cd\u200b\u7c7b\u578b\u200b\u6216\u8005\u200b\u8f6c\u79fb\u200b\u5230\u200b\u67d0\u4e2a\u200b\u8bbe\u5907\u200b\u4e0a\u200b\u3002
FlatDict
\u200b\u540c\u65f6\u200b\u96c6\u6210\u200b\u4e86\u200bcpu
\u3001gpu
(cuda
)\u3001tpu
(xla
)\u200b\u65b9\u6cd5\u200b\u6765\u200b\u63d0\u4f9b\u200b\u66f4\u200b\u4fbf\u6377\u200b\u7684\u200b\u8bbf\u95ee\u200b\u3002
FlatDict
\u200b\u652f\u6301\u200bjson
\u3001jsons
\u3001yaml
\u200b\u548c\u200byamls
\u200b\u65b9\u6cd5\u200b\u6765\u200b\u5c06\u200bFlatDict
\u200b\u5b58\u50a8\u200b\u5230\u200b\u6587\u4ef6\u200b\u6216\u8005\u200b\u8f6c\u6362\u6210\u200b\u5b57\u7b26\u4e32\u200b\u3002 \u200b\u5b83\u200b\u8fd8\u200b\u63d0\u4f9b\u200b\u4e86\u200bfrom_json
\u3001from_jsons
\u3001from_yaml
\u200b\u548c\u200bfrom_yamls
\u200b\u6765\u200b\u4ece\u200b\u4e00\u4e2a\u200b\u5b57\u7b26\u4e32\u200b\u6216\u8005\u200b\u6587\u4ef6\u200b\u4e2d\u200b\u6784\u5efa\u200bFlatDict
\u3002
FlatDict
\u200b\u4e5f\u200b\u5305\u62ec\u200b\u4e86\u200bdump
\u200b\u548c\u200bload
\u200b\u65b9\u6cd5\u200b\uff0c\u200b\u4ed6\u4eec\u200b\u53ef\u4ee5\u200b\u4ece\u6587\u4ef6\u200b\u6269\u5c55\u540d\u200b\u4e2d\u200b\u81ea\u52a8\u200b\u63a8\u65ad\u200b\u7c7b\u578b\u200b\u7136\u540e\u200b\u5c06\u200bFlatDict
\u200b\u5b58\u50a8\u200b\u5230\u200b\u6587\u4ef6\u200b\u4e2d\u200b/\u200b\u4ece\u6587\u4ef6\u200b\u4e2d\u200b\u52a0\u8f7d\u200bFlatDict
\u3002
\u200b\u4e3a\u4e86\u200b\u6ee1\u8db3\u200b\u9ed8\u8ba4\u503c\u200b\u7684\u200b\u9700\u8981\u200b\uff0c\u200b\u6211\u4eec\u200b\u5305\u62ec\u200b\u4e86\u200b\u4e00\u4e2a\u200bDefaultDict
\uff0c\u200b\u4ed6\u200b\u63a5\u53d7\u200bdefault_factory
\u200b\u53c2\u6570\u200b\uff0c\u200b\u5e76\u200b\u548c\u200bcollections.defaultdict
\u200b\u4e00\u6837\u200b\u5de5\u4f5c\u200b\u3002
\u200b\u7531\u4e8e\u200b\u5927\u591a\u6570\u200b\u914d\u7f6e\u200b\u90fd\u200b\u662f\u200b\u4e00\u4e2a\u200b\u5d4c\u5957\u200b\u7684\u200b\u7ed3\u6784\u200b\uff0c\u200b\u6211\u4eec\u200b\u8fdb\u4e00\u6b65\u200b\u63d0\u51fa\u200b\u4e86\u200bNestedDict
\u3002
\u200b\u57fa\u4e8e\u200bDefaultDict
\uff0cNestedDict
\u200b\u63d0\u4f9b\u200b\u4e86\u200ball_keys
\u3001all_values
\u3001all_items
\u200b\u65b9\u6cd5\u200b\u6765\u200b\u5141\u8bb8\u200b\u4e00\u6b21\u6027\u200b\u904d\u5386\u200b\u6574\u4e2a\u200b\u5d4c\u5957\u200b\u7ed3\u6784\u200b\u3002
NestedDict
\u200b\u540c\u65f6\u200b\u63d0\u4f9b\u200b\u4e86\u200bapply
\u200b\u548c\u200bapply_
\u200b\u65b9\u6cd5\u200b\uff0c\u200b\u5b83\u200b\u53ef\u4ee5\u200b\u4f7f\u200b\u64cd\u7eb5\u200b\u5d4c\u5957\u200b\u7ed3\u6784\u200b\u66f4\u52a0\u200b\u5bb9\u6613\u200b\u3002
Config
\u200b\u901a\u8fc7\u200b\u4e24\u4e2a\u200b\u65b9\u9762\u200b\u6765\u200b\u8fdb\u4e00\u6b65\u200b\u63d0\u5347\u200b\u529f\u80fd\u6027\u200b\uff1a \u200b\u652f\u6301\u200bfreeze
\u200b\u6765\u200b\u51bb\u7ed3\u200b\u548c\u200bdefrost
\u200b\u89e3\u51bb\u200b\u5b57\u5178\u200b\u548c\u200b \u200b\u52a0\u5165\u200b\u5185\u7f6e\u200b\u7684\u200bConfigParser
\u200b\u6765\u200b\u89e3\u6790\u200b\u547d\u4ee4\u884c\u200b\u8bed\u53e5\u200b\u3002
\u200b\u6ce8\u610f\u200bConfig
\u200b\u9ed8\u8ba4\u8bbe\u7f6e\u200bdefault_factory=Config()
\u200b\u6765\u200b\u63d0\u4f9b\u200b\u4fbf\u5229\u200b\u3002
Registry
\u200b\u7ee7\u627f\u200b\u81ea\u200bNestedDict
\uff0c\u200b\u5e76\u4e14\u200b\u63d0\u4f9b\u200bregister
\u3001lookup
\u200b\u548c\u200bbuild
\u200b\u6765\u200b\u5e2e\u52a9\u200b\u4f60\u200b\u6ce8\u518c\u200b\u6784\u9020\u51fd\u6570\u200b\u5e76\u200b\u4ece\u200bConfig
\u200b\u6765\u200b\u521b\u5efa\u5bf9\u8c61\u200b\u3002
\u200b\u6709\u200b\u4e00\u4e2a\u200b\u503c\u200b\u5728\u200b\u591a\u4e2a\u200b\u5730\u65b9\u200b\u4ee5\u200b\u591a\u4e2a\u200b\u540d\u5b57\u200b\u51fa\u73b0\u200b\uff1f\u200b\u6211\u4eec\u200b\u7ed9\u200b\u4f60\u200b\u63d0\u4f9b\u200b\u63a9\u62a4\u200b\u3002
\u200b\u53ea\u8981\u200b\u5c06\u503c\u200b\u4ee5\u200bVariable
\u200b\u5305\u88c5\u200b\uff0c\u200b\u7136\u540e\u200b\u6bcf\u5904\u200b\u66f4\u6539\u200b\u90fd\u200b\u4f1a\u200b\u5728\u200b\u5904\u5904\u200b\u4f53\u73b0\u200b\u3002
Variable
\u200b\u540c\u65f6\u200b\u652f\u6301\u200btype
\u3001choices
\u3001validator
\u3001required
\u200b\u6765\u200b\u786e\u4fdd\u200b\u503c\u200b\u7684\u200b\u6b63\u786e\u6027\u200b\u3002
\u200b\u4e3a\u4e86\u200b\u66f4\u52a0\u200b\u7b80\u5355\u200b\uff0cVariable
\u200b\u8fd8\u200b\u652f\u6301\u200bhelp
\u200b\u6765\u200b\u5728\u200b\u4f7f\u7528\u200bConfigParser
\u200b\u65f6\u200b\u63d0\u4f9b\u200b\u63cf\u8ff0\u200b\u3002
ConfigParser
\u200b\u5728\u200bArgumentParser
\u200b\u7684\u200b\u57fa\u7840\u200b\u4e4b\u4e0a\u200b\uff0c\u200b\u63d0\u4f9b\u200b\u4e86\u200bparse
\u200b\u548c\u200bparse_config
\u200b\u6765\u200b\u89e3\u6790\u200b\u547d\u4ee4\u884c\u200b\u53c2\u6570\u200b\u5e76\u200b\u521b\u5efa\u200b/\u200b\u66f4\u65b0\u200bConfig
\u3002
CHANfiG \u200b\u6709\u7740\u200b\u5f3a\u5927\u200b\u7684\u200b\u524d\u200b\u5411\u200b\u517c\u5bb9\u200b\u80fd\u529b\u200b\uff0c\u200b\u80fd\u591f\u200b\u826f\u597d\u200b\u7684\u200b\u517c\u5bb9\u200b\u4ee5\u5f80\u200b\u57fa\u4e8e\u200b yaml \u200b\u548c\u200b json \u200b\u7684\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u3002
\u200b\u5982\u679c\u200b\u4f60\u200b\u6b64\u524d\u200b\u4f7f\u7528\u200b yacs\uff0c\u200b\u53ea\u200b\u9700\u200b\u7b80\u5355\u200b\u5c06\u200bCfgNode
\u200b\u66ff\u6362\u200b\u4e3a\u200bConfig
\u200b\u4fbf\u200b\u53ef\u4ee5\u200b\u4eab\u53d7\u200b\u6240\u6709\u200b CHANfiG \u200b\u6240\u200b\u63d0\u4f9b\u200b\u7684\u200b\u4fbf\u5229\u200b\u3002
\u200b\u66f4\u8fdb\u4e00\u6b65\u200b\u7684\u200b\uff0c\u200b\u5982\u679c\u200b\u4f60\u200b\u53d1\u73b0\u200bConfig
\u200b\u4e2d\u200b\u7684\u200b\u540d\u5b57\u200b\u5bf9\u4e8e\u200b\u547d\u4ee4\u884c\u200b\u6765\u8bf4\u200b\u8fc7\u957f\u200b\uff0c\u200b\u4f60\u200b\u53ef\u4ee5\u200b\u7b80\u5355\u200b\u7684\u200b\u8c03\u7528\u200bself.add_argument
\u200b\u5e76\u200b\u8bbe\u7f6e\u200b\u6070\u5f53\u200b\u7684\u200bdest
\u200b\u6765\u200b\u5728\u200b\u547d\u4ee4\u884c\u200b\u4e2d\u200b\u4f7f\u7528\u200b\u66f4\u200b\u77ed\u200b\u7684\u200b\u540d\u5b57\u200b\uff0c\u200b\u6b63\u5982\u200bargparse
\u200b\u6240\u200b\u505a\u200b\u7684\u200b\u90a3\u6837\u200b\u3002
from chanfig import Config, Variable\nclass Model:\ndef __init__(self, encoder, dropout=0.1, activation='ReLU'):\nself.encoder = Encoder(**encoder)\nself.dropout = Dropout(dropout)\nself.activation = getattr(Activation, activation)\ndef main(config):\nmodel = Model(**config.model)\noptimizer = Optimizer(**config.optimizer)\nscheduler = Scheduler(**config.scheduler)\ndataset = Dataset(**config.dataset)\ndataloader = Dataloader(**config.dataloader)\nclass TestConfig(Config):\ndef __init__(self):\nsuper().__init__()\ndropout = Variable(0.1)\nself.name = \"CHANfiG\"\nself.seed = 1013\nself.data.batch_size = 64\nself.model.encoder.num_layers = 6\nself.model.decoder.num_layers = 6\nself.model.dropout = dropout\nself.model.encoder.dropout = dropout\nself.model.decoder.dropout = dropout\nself.activation = \"GELU\"\nself.optim.lr = 1e-3\nself.add_argument(\"--batch_size\", dest=\"data.batch_size\")\nself.add_argument(\"--lr\", dest=\"optim.lr\")\ndef post(self):\nself.id = f\"{self.name}_{self.seed}\"\nif __name__ == '__main__':\n# config = Config.load('config.yaml') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u8bfb\u53d6\u200b\u4e00\u4e2a\u200b yaml\n# config = Config.load('config.json') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u8bfb\u53d6\u200b\u4e00\u4e2a\u200b json\n# existing_configs = {'data.batch_size': 64, 'model.encoder.num_layers': 8}\n# config = Config(**existing_configs) # \u200b\u5982\u679c\u200b\u4f60\u200b\u6709\u4e9b\u200bconfig\u200b\u9700\u8981\u200b\u8bfb\u53d6\u200b\nconfig = TestConfig()\nconfig = config.parse()\n# config.merge('dataset.yaml') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u5408\u5e76\u200b\u4e00\u4e2a\u200b yaml\n# config.merge('dataset.json') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u5408\u5e76\u200b\u4e00\u4e2a\u200b json\n# \u200b\u6ce8\u610f\u200b\u88ab\u200b\u5408\u5e76\u200b\u7684\u200b\u503c\u200b\u5177\u6709\u200b\u66f4\u200b\u9ad8\u200b\u7684\u200b\u4f18\u5148\u7ea7\u200b\nconfig.model.decoder.num_layers = 8\nconfig.freeze()\nprint(config)\n# main(config)\n# config.yaml('config.yaml') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u4fdd\u5b58\u200b\u4e00\u4e2a\u200b yaml\n# config.json('config.json') # \u200b\u5982\u679c\u200b\u4f60\u200b\u60f3\u200b\u4fdd\u5b58\u200b\u4e00\u4e2a\u200b json\n
\u200b\u6240\u6709\u200b\u4f60\u200b\u9700\u8981\u200b\u505a\u200b\u7684\u200b\u4ec5\u4ec5\u200b\u662f\u200b\u8fd0\u884c\u200b\u4e00\u884c\u200b\uff1a
Bashpython main.py --model.encoder.num_layers 8 --model.dropout=0.2 --lr 5e-3\n
\u200b\u5f53\u7136\u200b\uff0c\u200b\u4f60\u200b\u4e5f\u200b\u53ef\u4ee5\u200b\u8bfb\u53d6\u200b\u4e00\u4e2a\u200b\u9ed8\u8ba4\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u7136\u540e\u200b\u5728\u200b\u4ed6\u200b\u57fa\u7840\u200b\u4e0a\u200b\u4fee\u6539\u200b\uff1a
\u200b\u6ce8\u610f\u200b\uff0c\u200b\u4f60\u200b\u5fc5\u987b\u200b\u6307\u5b9a\u200bconfig.parse(default_config='config')
\u200b\u6765\u200b\u6b63\u786e\u200b\u8bfb\u53d6\u200b\u9ed8\u8ba4\u200b\u914d\u7f6e\u6587\u4ef6\u200b\u3002
python main.py --config meow.yaml --model.encoder.num_layers 8 --model.dropout=0.2 --lr 5e-3\n
\u200b\u5982\u679c\u200b\u4f60\u200b\u4fdd\u5b58\u200b\u4e86\u200b\u914d\u7f6e\u6587\u4ef6\u200b\uff0c\u200b\u90a3\u200b\u4ed6\u200b\u5e94\u8be5\u200b\u770b\u8d77\u6765\u200b\u50cf\u200b\u8fd9\u6837\u200b\uff1a
YAMLactivation: GELU\ndata:\nbatch_size: 64\nid: CHANfiG_1013\nmodel:\ndecoder:\ndropout: 0.1\nnum_layers: 6\ndropout: 0.1\nencoder:\ndropout: 0.1\nnum_layers: 6\nname: CHANfiG\noptim:\nlr: 0.005\nseed: 1013\n
JSON{\n\"name\": \"CHANfiG\",\n\"seed\": 1013,\n\"data\": {\n\"batch_size\": 64\n},\n\"model\": {\n\"encoder\": {\n\"num_layers\": 6,\n\"dropout\": 0.1\n},\n\"decoder\": {\n\"num_layers\": 6,\n\"dropout\": 0.1\n},\n\"dropout\": 0.1\n},\n\"activation\": \"GELU\",\n\"optim\": {\n\"lr\": 0.005\n},\n\"id\": \"CHANfiG_1013\"\n}\n
\u200b\u5728\u200b\u65b9\u6cd5\u200b\u4e2d\u200b\u5b9a\u4e49\u200b\u9ed8\u8ba4\u200b\u53c2\u6570\u200b\uff0c\u200b\u5728\u200b\u547d\u4ee4\u884c\u200b\u4e2d\u200b\u4fee\u6539\u200b\uff0c\u200b\u7136\u540e\u200b\u5c06\u200b\u5269\u4e0b\u200b\u7684\u200b\u4ea4\u7ed9\u200b CHANfiG\u3002
"},{"location":"zh/#_6","title":"\u5b89\u88c5","text":"\u200b\u5b89\u88c5\u200b pypi \u200b\u4e0a\u200b\u6700\u8fd1\u200b\u7684\u200b\u7a33\u5b9a\u200b\u7248\u672c\u200b\uff1a
Bashpip install chanfig\n
\u200b\u4ece\u200b\u6e90\u7801\u200b\u5b89\u88c5\u200b\u6700\u65b0\u200b\u7684\u200b\u7248\u672c\u200b\uff1a
Bashpip install git+https://github.com/ZhiyuanChen/CHANfiG\n
\u200b\u4ed6\u200b\u672c\u8be5\u5982\u6b64\u200b\u5de5\u4f5c\u200b\u3002
"},{"location":"zh/#_7","title":"\u6388\u6743","text":"CHANfiG \u200b\u4f9d\u636e\u200b\u4e0b\u5217\u200b\u8bb8\u53ef\u8bc1\u200b\u8fdb\u884c\u200b\u591a\u91cd\u200b\u6388\u6743\u200b\uff1a
\u200b\u5982\u679c\u200b\u4f60\u200b\u4f7f\u7528\u200b\u672c\u200b\u5de5\u4f5c\u200b\uff0c\u200b\u4f60\u200b\u53ef\u4ee5\u200b\u4ece\u4e2d\u200b\u4efb\u9009\u200b\uff08\u200b\u4e00\u4e2a\u200b\u6216\u8005\u200b\u591a\u4e2a\u200b\uff09\u200b\u8bb8\u53ef\u8bc1\u200b\u3002
SPDX-License-Identifier: Unlicense OR AGPL-3.0-or-later OR GPL-2.0-or-later OR BSD-4-Clause OR MIT OR Apache-2.0