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 @@ - CHANfiGEasier Configurationhttps://chanfig.danling.org/CHANfiG Contributorshttps://github.com/ZhiyuanChen/CHANfiGen-None Thu, 14 Sep 2023 05:16:34 -0000 Thu, 14 Sep 2023 05:16:34 -0000 1440 MkDocs RSS plugin - v1.8.0 CHANfiG <h1>CHANfiG</h1>https://chanfig.danling.org/blog/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/blog/ CHANfiG <h1>CHANfiG</h1>https://chanfig.danling.org/zh/blog/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/blog/ CHANfiG Zhiyuan Chen <p>--8&lt;-- "README.md"</p>https://chanfig.danling.org/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/ Config Zhiyuan Chen <h1>Config</h1><p>::: chanfig.config</p>https://chanfig.danling.org/config/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/config/ DefaultDict Zhiyuan Chen <h1>DefaultDict</h1><p>::: chanfig.DefaultDict</p>https://chanfig.danling.org/default_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/default_dict/ FlatDict Zhiyuan Chen <h1>FlatDict</h1><p>::: chanfig.FlatDict</p>https://chanfig.danling.org/flat_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/flat_dict/ Functional Zhiyuan Chen <h1>Functional</h1><p>::: chanfig.to_dictoptions:heading_level: 0</p><p>::: chanfig.saveoptions:heading_level: 0</p><p>::: chanfig.loadoptions:heading_level: 0</p><p>::: chan...</p>https://chanfig.danling.org/functional/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/functional/ NestedDict Zhiyuan Chen <h1>NestedDict</h1><p>::: chanfig.NestedDict</p>https://chanfig.danling.org/nested_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/nested_dict/ ConfigParser Zhiyuan Chen <h1>ConfigParser</h1><p>::: chanfig.ConfigParser</p>https://chanfig.danling.org/parser/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/parser/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.Registry</p>https://chanfig.danling.org/registry/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/registry/ Utilities Zhiyuan Chen <h1>Utilities</h1><p>::: chanfig.utils.Singletonoptions:heading_level: 0</p><h2>chanfig.utils.Null</h2><p><code>Null</code> is an instance of [<code>NULL</code>][chanfig.utils.NULL].</p><p>Since the ...</p>https://chanfig.danling.org/utils/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/utils/ Variable Zhiyuan Chen <h1>Variable</h1><p>::: chanfig.Variable</p>https://chanfig.danling.org/variable/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/variable/ CHANfiG Zhiyuan Chen <p>--8&lt;-- "README.zh.md"</p>https://chanfig.danling.org/zh/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/ Config Zhiyuan Chen <h1>Config</h1><p>::: chanfig.config</p>https://chanfig.danling.org/zh/config/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/config/ DefaultDict Zhiyuan Chen <h1>DefaultDict</h1><p>::: chanfig.DefaultDict</p>https://chanfig.danling.org/zh/default_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/default_dict/ FlatDict Zhiyuan Chen <h1>FlatDict</h1><p>::: chanfig.FlatDict</p>https://chanfig.danling.org/zh/flat_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/flat_dict/ Functional Zhiyuan Chen <h1>Functional</h1><p>::: chanfig.to_dictoptions:heading_level: 0</p><p>::: chanfig.saveoptions:heading_level: 0</p><p>::: chanfig.loadoptions:heading_level: 0</p><p>::: chan...</p>https://chanfig.danling.org/zh/functional/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/functional/ NestedDict Zhiyuan Chen <h1>NestedDict</h1><p>::: chanfig.NestedDict</p>https://chanfig.danling.org/zh/nested_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/nested_dict/ ConfigParser Zhiyuan Chen <h1>ConfigParser</h1><p>::: chanfig.ConfigParser</p>https://chanfig.danling.org/zh/parser/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/parser/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.Registry</p>https://chanfig.danling.org/zh/registry/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/registry/ \ No newline at end of file + CHANfiGEasier Configurationhttps://chanfig.danling.org/CHANfiG Contributorshttps://github.com/ZhiyuanChen/CHANfiGen-None Tue, 19 Sep 2023 12:00:52 -0000 Tue, 19 Sep 2023 12:00:52 -0000 1440 MkDocs RSS plugin - v1.8.0 CHANfiG <h1>CHANfiG</h1>https://chanfig.danling.org/blog/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/blog/ CHANfiG <h1>CHANfiG</h1>https://chanfig.danling.org/zh/blog/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/blog/ CHANfiG Zhiyuan Chen <p>--8&lt;-- "README.md"</p>https://chanfig.danling.org/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/ Config Zhiyuan Chen <h1>Config</h1><p>::: chanfig.config</p>https://chanfig.danling.org/config/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/config/ DefaultDict Zhiyuan Chen <h1>DefaultDict</h1><p>::: chanfig.DefaultDict</p>https://chanfig.danling.org/default_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/default_dict/ FlatDict Zhiyuan Chen <h1>FlatDict</h1><p>::: chanfig.FlatDict</p>https://chanfig.danling.org/flat_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/flat_dict/ Functional Zhiyuan Chen <h1>Functional</h1><p>::: chanfig.to_dictoptions:heading_level: 0</p><p>::: chanfig.saveoptions:heading_level: 0</p><p>::: chanfig.loadoptions:heading_level: 0</p><p>::: chan...</p>https://chanfig.danling.org/functional/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/functional/ NestedDict Zhiyuan Chen <h1>NestedDict</h1><p>::: chanfig.NestedDict</p>https://chanfig.danling.org/nested_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/nested_dict/ ConfigParser Zhiyuan Chen <h1>ConfigParser</h1><p>::: chanfig.ConfigParser</p>https://chanfig.danling.org/parser/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/parser/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.Registry</p>https://chanfig.danling.org/registry/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/registry/ Utilities Zhiyuan Chen <h1>Utilities</h1><p>::: chanfig.utils.Singletonoptions:heading_level: 0</p><h2>chanfig.utils.Null</h2><p><code>Null</code> is an instance of [<code>NULL</code>][chanfig.utils.NULL].</p><p>Since the ...</p>https://chanfig.danling.org/utils/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/utils/ Variable Zhiyuan Chen <h1>Variable</h1><p>::: chanfig.Variable</p>https://chanfig.danling.org/variable/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/variable/ CHANfiG Zhiyuan Chen <p>--8&lt;-- "README.zh.md"</p>https://chanfig.danling.org/zh/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/ Config Zhiyuan Chen <h1>Config</h1><p>::: chanfig.config</p>https://chanfig.danling.org/zh/config/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/config/ DefaultDict Zhiyuan Chen <h1>DefaultDict</h1><p>::: chanfig.DefaultDict</p>https://chanfig.danling.org/zh/default_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/default_dict/ FlatDict Zhiyuan Chen <h1>FlatDict</h1><p>::: chanfig.FlatDict</p>https://chanfig.danling.org/zh/flat_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/flat_dict/ Functional Zhiyuan Chen <h1>Functional</h1><p>::: chanfig.to_dictoptions:heading_level: 0</p><p>::: chanfig.saveoptions:heading_level: 0</p><p>::: chanfig.loadoptions:heading_level: 0</p><p>::: chan...</p>https://chanfig.danling.org/zh/functional/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/functional/ NestedDict Zhiyuan Chen <h1>NestedDict</h1><p>::: chanfig.NestedDict</p>https://chanfig.danling.org/zh/nested_dict/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/nested_dict/ ConfigParser Zhiyuan Chen <h1>ConfigParser</h1><p>::: chanfig.ConfigParser</p>https://chanfig.danling.org/zh/parser/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/parser/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.Registry</p>https://chanfig.danling.org/zh/registry/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/zh/registry/ \ No newline at end of file diff --git a/feed_rss_updated.xml b/feed_rss_updated.xml index 990cefd6..a0090fc4 100644 --- a/feed_rss_updated.xml +++ b/feed_rss_updated.xml @@ -1 +1 @@ - CHANfiGEasier Configurationhttps://chanfig.danling.org/CHANfiG Contributorshttps://github.com/ZhiyuanChen/CHANfiGen-None Thu, 14 Sep 2023 05:16:34 -0000 Thu, 14 Sep 2023 05:16:34 -0000 1440 MkDocs RSS plugin - v1.8.0 Functional Zhiyuan Chen <h1>Functional</h1><p>::: chanfig.to_dictoptions:heading_level: 0</p><p>::: chanfig.saveoptions:heading_level: 0</p><p>::: chanfig.loadoptions:heading_level: 0</p><p>::: chan...</p>https://chanfig.danling.org/functional/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/functional/ ConfigParser Zhiyuan Chen <h1>ConfigParser</h1><p>::: chanfig.ConfigParser</p>https://chanfig.danling.org/parser/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/parser/ Utilities Zhiyuan Chen <h1>Utilities</h1><p>::: chanfig.utils.Singletonoptions:heading_level: 0</p><h2>chanfig.utils.Null</h2><p><code>Null</code> is an instance of [<code>NULL</code>][chanfig.utils.NULL].</p><p>Since the ...</p>https://chanfig.danling.org/utils/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/utils/ Functional Zhiyuan Chen <h1>Functional</h1><p>::: chanfig.to_dictoptions:heading_level: 0</p><p>::: chanfig.saveoptions:heading_level: 0</p><p>::: chanfig.loadoptions:heading_level: 0</p><p>::: chan...</p>https://chanfig.danling.org/zh/functional/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/zh/functional/ ConfigParser Zhiyuan Chen <h1>ConfigParser</h1><p>::: chanfig.ConfigParser</p>https://chanfig.danling.org/zh/parser/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/zh/parser/ Utilities Zhiyuan Chen <h1>Utilities</h1><p>::: chanfig.utils.Singletonoptions:heading_level: 0</p><h2>chanfig.utils.Null</h2><p><code>Null</code> is an instance of [<code>NULL</code>][chanfig.utils.NULL].</p><p>Since the ...</p>https://chanfig.danling.org/zh/utils/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/zh/utils/ CHANfiG Zhiyuan Chen <p>--8&lt;-- "README.md"</p>https://chanfig.danling.org/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/ Config Zhiyuan Chen <h1>Config</h1><p>::: chanfig.config</p>https://chanfig.danling.org/config/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/config/ DefaultDict Zhiyuan Chen <h1>DefaultDict</h1><p>::: chanfig.DefaultDict</p>https://chanfig.danling.org/default_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/default_dict/ FlatDict Zhiyuan Chen <h1>FlatDict</h1><p>::: chanfig.FlatDict</p>https://chanfig.danling.org/flat_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/flat_dict/ NestedDict Zhiyuan Chen <h1>NestedDict</h1><p>::: chanfig.NestedDict</p>https://chanfig.danling.org/nested_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/nested_dict/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.Registry</p>https://chanfig.danling.org/registry/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/registry/ Variable Zhiyuan Chen <h1>Variable</h1><p>::: chanfig.Variable</p>https://chanfig.danling.org/variable/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/variable/ CHANfiG <h1>CHANfiG</h1>https://chanfig.danling.org/blog/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/blog/ CHANfiG Zhiyuan Chen <p>--8&lt;-- "README.zh.md"</p>https://chanfig.danling.org/zh/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/ Config Zhiyuan Chen <h1>Config</h1><p>::: chanfig.config</p>https://chanfig.danling.org/zh/config/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/config/ DefaultDict Zhiyuan Chen <h1>DefaultDict</h1><p>::: chanfig.DefaultDict</p>https://chanfig.danling.org/zh/default_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/default_dict/ FlatDict Zhiyuan Chen <h1>FlatDict</h1><p>::: chanfig.FlatDict</p>https://chanfig.danling.org/zh/flat_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/flat_dict/ NestedDict Zhiyuan Chen <h1>NestedDict</h1><p>::: chanfig.NestedDict</p>https://chanfig.danling.org/zh/nested_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/nested_dict/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.Registry</p>https://chanfig.danling.org/zh/registry/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/registry/ \ No newline at end of file + CHANfiGEasier Configurationhttps://chanfig.danling.org/CHANfiG Contributorshttps://github.com/ZhiyuanChen/CHANfiGen-None Tue, 19 Sep 2023 12:00:52 -0000 Tue, 19 Sep 2023 12:00:52 -0000 1440 MkDocs RSS plugin - v1.8.0 Functional Zhiyuan Chen <h1>Functional</h1><p>::: chanfig.to_dictoptions:heading_level: 0</p><p>::: chanfig.saveoptions:heading_level: 0</p><p>::: chanfig.loadoptions:heading_level: 0</p><p>::: chan...</p>https://chanfig.danling.org/functional/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/functional/ ConfigParser Zhiyuan Chen <h1>ConfigParser</h1><p>::: chanfig.ConfigParser</p>https://chanfig.danling.org/parser/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/parser/ Utilities Zhiyuan Chen <h1>Utilities</h1><p>::: chanfig.utils.Singletonoptions:heading_level: 0</p><h2>chanfig.utils.Null</h2><p><code>Null</code> is an instance of [<code>NULL</code>][chanfig.utils.NULL].</p><p>Since the ...</p>https://chanfig.danling.org/utils/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/utils/ Functional Zhiyuan Chen <h1>Functional</h1><p>::: chanfig.to_dictoptions:heading_level: 0</p><p>::: chanfig.saveoptions:heading_level: 0</p><p>::: chanfig.loadoptions:heading_level: 0</p><p>::: chan...</p>https://chanfig.danling.org/zh/functional/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/zh/functional/ ConfigParser Zhiyuan Chen <h1>ConfigParser</h1><p>::: chanfig.ConfigParser</p>https://chanfig.danling.org/zh/parser/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/zh/parser/ Utilities Zhiyuan Chen <h1>Utilities</h1><p>::: chanfig.utils.Singletonoptions:heading_level: 0</p><h2>chanfig.utils.Null</h2><p><code>Null</code> is an instance of [<code>NULL</code>][chanfig.utils.NULL].</p><p>Since the ...</p>https://chanfig.danling.org/zh/utils/ Wed, 26 Jul 2023 17:37:04 +0000CHANfiGhttps://chanfig.danling.org/zh/utils/ CHANfiG Zhiyuan Chen <p>--8&lt;-- "README.md"</p>https://chanfig.danling.org/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/ Config Zhiyuan Chen <h1>Config</h1><p>::: chanfig.config</p>https://chanfig.danling.org/config/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/config/ DefaultDict Zhiyuan Chen <h1>DefaultDict</h1><p>::: chanfig.DefaultDict</p>https://chanfig.danling.org/default_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/default_dict/ FlatDict Zhiyuan Chen <h1>FlatDict</h1><p>::: chanfig.FlatDict</p>https://chanfig.danling.org/flat_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/flat_dict/ NestedDict Zhiyuan Chen <h1>NestedDict</h1><p>::: chanfig.NestedDict</p>https://chanfig.danling.org/nested_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/nested_dict/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.Registry</p>https://chanfig.danling.org/registry/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/registry/ Variable Zhiyuan Chen <h1>Variable</h1><p>::: chanfig.Variable</p>https://chanfig.danling.org/variable/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/variable/ CHANfiG <h1>CHANfiG</h1>https://chanfig.danling.org/blog/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/blog/ CHANfiG Zhiyuan Chen <p>--8&lt;-- "README.zh.md"</p>https://chanfig.danling.org/zh/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/ Config Zhiyuan Chen <h1>Config</h1><p>::: chanfig.config</p>https://chanfig.danling.org/zh/config/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/config/ DefaultDict Zhiyuan Chen <h1>DefaultDict</h1><p>::: chanfig.DefaultDict</p>https://chanfig.danling.org/zh/default_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/default_dict/ FlatDict Zhiyuan Chen <h1>FlatDict</h1><p>::: chanfig.FlatDict</p>https://chanfig.danling.org/zh/flat_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/flat_dict/ NestedDict Zhiyuan Chen <h1>NestedDict</h1><p>::: chanfig.NestedDict</p>https://chanfig.danling.org/zh/nested_dict/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/nested_dict/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.Registry</p>https://chanfig.danling.org/zh/registry/ Thu, 20 Jul 2023 17:14:16 +0000CHANfiGhttps://chanfig.danling.org/zh/registry/ \ No newline at end of file diff --git a/flat_dict/index.html b/flat_dict/index.html index a9520a56..0c91da33 100644 --- a/flat_dict/index.html +++ b/flat_dict/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/functional/index.html b/functional/index.html index 1b060c18..1cfd3301 100644 --- a/functional/index.html +++ b/functional/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/index.html b/index.html index 7736655a..145f1017 100644 --- a/index.html +++ b/index.html @@ -38,7 +38,7 @@ - + @@ -46,7 +46,7 @@ - + diff --git a/nested_dict/index.html b/nested_dict/index.html index 75fa2dbb..7e915428 100644 --- a/nested_dict/index.html +++ b/nested_dict/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/parser/index.html b/parser/index.html index 3af82615..f1df698b 100644 --- a/parser/index.html +++ b/parser/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/registry/index.html b/registry/index.html index b50d5371..2c529773 100644 --- a/registry/index.html +++ b/registry/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/search/search_index.json b/search/search_index.json index d22a9188..a2621912 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\u200b\\-]","pipeline":["stemmer"]},"docs":[{"location":"","title":"CHANfiG","text":""},{"location":"#introduction","title":"Introduction","text":"

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)

"},{"location":"#components","title":"Components","text":"

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.

"},{"location":"#flatdict","title":"FlatDict","text":"

FlatDict improves the default dict in 3 aspects.

"},{"location":"#dict-operations","title":"Dict Operations","text":"

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. FlatDictsupportssort 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.

"},{"location":"#ml-operations","title":"ML Operations","text":"

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.

"},{"location":"#io-operations","title":"IO Operations","text":"

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.

"},{"location":"#defaultdict","title":"DefaultDict","text":"

To facility the needs of default values, we incorporate DefaultDict which accepts default_factory and works just like a collections.defaultdict.

"},{"location":"#nesteddict","title":"NestedDict","text":"

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.

"},{"location":"#config","title":"Config","text":"

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.

"},{"location":"#registry","title":"Registry","text":"

Registry extends the NestedDict and supports register, lookup, and build to help you register constructors and build objects from a Config.

"},{"location":"#variable","title":"Variable","text":"

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.

"},{"location":"#configparser","title":"ConfigParser","text":"

ConfigParser extends ArgumentParser and provides parse and parse_config to parse command line arguments.

"},{"location":"#usage","title":"Usage","text":"

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.

Python
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:

Bash
python 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.

Bash
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:

YAML
activation: 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:

Bash
pip install chanfig\n

Install the latest version from source:

Bash
pip 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

  1. fun fact: time is always uncountable.\u00a0\u21a9

"},{"location":"config/","title":"Config","text":""},{"location":"config/#chanfig.config.Config","title":"Config","text":"

Bases: NestedDict

Config is an extension of NestedDict.

The differences between Config and NestedDict lies in 3 aspects:

  1. Config has default_factory set to Config and convert_mapping set to True by default.
  2. Config has a frozen attribute, which can be toggled with freeze(lock) & defrost(unlock) or temporarily changed with locked & unlocked.
  3. 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.

Notes

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 Description parser 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 Python
class 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 Python
def 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 Description self 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 Python
def 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 Default recursive bool True

Alias:

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 Python
def 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 Default name 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 Default recursive bool True

Alias:

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 Python
def 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 Default name Any required default Any None

Returns:

Name Type Description value Any

If Config does not contain name, return default. If default is not specified, return default_factory().

Raises:

Type Description KeyError

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 Python
def 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.

Source code in chanfig/config.py Python
def 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 Default args 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 Python
def 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 Default args 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 Python
def 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 Default name Any required default Any Null

Returns:

Name Type Description value 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 Description self 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 Python
def 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 Default name Any required value Any required convert_mapping bool | None

Whether to convert Mapping to NestedDict. Defaults to self.convert_mapping.

None

Raises:

Type Description ValueError

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.

Source code in chanfig/config.py Python
def 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 in chanfig/config.py Python
def 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 Description default_factory Optional[Callable]

Default factory for defaultdict behaviour.

Raises:

Type Description TypeError

If default_factory is not callable.

Notes

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 Python
class 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 Default name Any required

Raises:

Type Description ValueError

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 Python
def 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 Description indent int

Indentation level in printing and dumping to json or yaml.

Notes

FlatDict 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 Python
class 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.

See Also

all_items

Source code in chanfig/flat_dict.py Python
def 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.

See Also

all_keys

Source code in chanfig/flat_dict.py Python
def 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.

See Also

all_values

Source code in chanfig/flat_dict.py Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description FlatDict

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 Python
def 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 Description self 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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description FlatDict

Alias:

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 Python
def 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 Default name 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 Python
def 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 Default name 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 Python
def 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 Default cls Callable

Target class to be converted to.

dict

Returns:

Type Description Mapping 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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Default other Mapping | Iterable | PathStr required

Returns:

Type Description FlatDict

Alias:

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description FlatDict

Alias:

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description FlatDict 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, propertys are saved in __dict__, they will keep their original reference after calling this method.

Returns:

Type Description FlatDict 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 Python
def 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 Description FlatDict

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 Description FlatDict

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 Description FlatDict

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 Description FlatDict

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 Default name Any required default Any None

Returns:

Name Type Description value Any

If FlatDict does not contain name, return default.

Raises:

Type Description KeyError

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 Python
def 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 Default name str required default Any Null

Returns:

Name Type Description value Any

If FlatDict does not contain name, return default.

Raises:

Type Description AttributeError

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 Python
def 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 Description self FlatDict

Alias:

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 Python
def 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 Default name str required

Returns:

Type Description bool

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Default use_variable bool

Whether to convert values to Variable objects.

True interpolators MutableMapping | None

Mapping contains values for interpolation. Defaults to self.

None

Raises:

Type Description ValueError

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 Python
def 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 Default other Mapping | Iterable | PathStr required

Returns:

Type Description FlatDict

Alias:

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 Python
def 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 Python
def 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 Description str

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 Python
def 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 Default file File

File to load from.

required method str | None

File type, should be in JSON or YAML.

None

Returns:

Type Description FlatDict

Raises:

Type Description ValueError

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 Description self FlatDict

Alias:

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 Python
def 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 Default file File required *args Any

Passed to load.

() **kwargs Any

Passed to load.

{}

Returns:

Name Type Description self 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 Python
def 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 Default file 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 Description Generator[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 Description ValueError

If save to IO and method is not specified.

TypeError

If save to unsupported extension.

Alias:

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 Python
def 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 Default name 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 Python
def 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 Default name str required value Any required

Warns:

Type Description RuntimeWarning

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 Python
def 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 Description FlatDict

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 Python
def 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 Default cls str | device | dtype required

Returns:

Name Type Description self 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 Python
def 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 Description self FlatDict

Alias:

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description TypeError

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Python
def 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 Description str

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 Python
def 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 Default obj Any

Object to be converted.

required

Returns:

Type Description Mapping[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 Python
def 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 Description ValueError

If save to IO and method is not specified.

TypeError

If save to unsupported extension.

Alias:

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 Python
def 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 Default file 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 Python
def 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 Default obj Any

Object to apply function.

required func 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 Description Any

Return value of func.

See Also

apply_: Apply an in-place operation.

Source code in chanfig/nested_dict.py Python
def 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 Default obj Any

Object to apply function.

required func 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 Description Any

Return value of func.

See Also

apply_: Apply a non-in-place operation.

Source code in chanfig/nested_dict.py Python
def 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 Description convert_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.

Notes

When 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 Python
class 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 Description Generator

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 Python
def 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 Description Generator

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 Python
def 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 Description Generator

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 Python
def 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.

Note

This method is meant for non-in-place modification of obj, for example, to.

Parameters:

Name Type Description Default func Callable required See Also

apply_: Apply an in-place operation. apply: Implementation of apply.

tionples

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 in chanfig/nested_dict.py Python
def 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.

Note

This method is meant for in-place modification of obj, for example, freeze.

Parameters:

Name Type Description Default func 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 Python
def 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 Default name 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 Python
def 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 Default other 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 Python
def 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 Default name Any required default Any None

Returns:

Name Type Description value Any

If NestedDict does not contain name, return default. If default is not specified, return default_factory().

Raises:

Type Description KeyError

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 Python
def 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 Default other 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 Python
def 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 Default name Any required default Any Null

Returns:

Name Type Description value 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 Python
def 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 Default name 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 Python
def 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 Default recursive bool

Whether to apply sort recursively.

True

Returns:

Type Description NestedDict

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 Python
def 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 Description TypeError

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 Python
def 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.

Source code in chanfig/parser.py Python
class 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:

  1. The base Config parsed into this method,
  2. The base config file located at the path of default_config (if specified),
  3. The config specified in arguments.

Higher levels override lower levels (i.e. 3 > 2 > 1).

Parameters:

Name Type Description Default args 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 Description config Config

The parsed Config.

Raises:

Type Description ValueError

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.

See Also

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.

Python Console Session
>>> 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.

Python Console Session
>>> 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.

Python Console Session
>>> 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.

Python Console Session
>>> 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.

Python Console Session
>>> 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 Python
def 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:

  1. The base Config parsed into this method,
  2. The base config file located at the path of default_config (if specified),
  3. The config specified in arguments.

Higher levels override lower levels (i.e. 3 > 2 > 1).

Parameters:

Name Type Description Default args 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 Description config Config

The parsed Config.

Raises:

Type Description ValueError

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.

See Also

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

Python Console Session
>>> 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 Python
def 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.

Notes

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 Python
class 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 Default name 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.

required *args Any

The arguments to pass to the component.

() **kwargs Any

The keyword arguments to pass to the component.

{}

Returns:

Type Description Any

Raises:

Type Description KeyError

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 Python
def 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 Default cls 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 Description Any

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 Default name str required

Returns:

Type Description Any

The component.

Raises:

Type Description KeyError

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 Python
def 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 Default component Any

The component to register.

None name Any | None

The name of the component.

None

Returns:

Name Type Description component Callable

The registered component. Registered component are expected to be Callable.

Raises:

Type Description ValueError

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 Python
def 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 in chanfig/utils.py Python
class 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.

Source code in chanfig/utils.py Python
class 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 in chanfig/utils.py Python
class 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 in chanfig/utils.py Python
class 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 in chanfig/utils.py Python
class 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 Default value 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 Description RuntimeError

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 Description value Any

The wrapped value.

dtype type

The type of the wrapped value.

Notes

Variable 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 Python
class 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.

"},{"location":"variable/#chanfig.Variable.value","title":"value: Any property writable","text":"

Fetch the object wrapped in Variable.

"},{"location":"variable/#chanfig.Variable.float","title":"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 Python
def 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.

Source code in chanfig/variable.py Python
def 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 Python
def 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.

Source code in chanfig/variable.py Python
def 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 Python
def 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 Default cls Callable

The type to convert to.

required

Examples:

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 Python
def 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 Python
def 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 in chanfig/variable.py Python
def 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 Python
def 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)

"},{"location":"zh/#_2","title":"\u7ec4\u4ef6","text":"

\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

"},{"location":"zh/#flatdict","title":"FlatDict","text":"

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

"},{"location":"zh/#_3","title":"\u5b57\u5178\u200b\u64cd\u4f5c","text":"

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

"},{"location":"zh/#_4","title":"\u673a\u5668\u200b\u5b66\u4e60\u200b\u64cd\u4f5c","text":"

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

"},{"location":"zh/#io","title":"IO \u200b\u64cd\u4f5c","text":"

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

"},{"location":"zh/#defaultdict","title":"DefaultDict","text":"

\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

"},{"location":"zh/#nesteddict","title":"NestedDict","text":"

\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

"},{"location":"zh/#config","title":"Config","text":"

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

"},{"location":"zh/#registry","title":"Registry","text":"

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

"},{"location":"zh/#variable","title":"Variable","text":"

\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

"},{"location":"zh/#configparser","title":"ConfigParser","text":"

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

"},{"location":"zh/#_5","title":"\u4f7f\u7528","text":"

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

Python
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

Bash
python 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

Bash
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

YAML
activation: 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

Bash
pip install chanfig\n

\u200b\u4ece\u200b\u6e90\u7801\u200b\u5b89\u88c5\u200b\u6700\u65b0\u200b\u7684\u200b\u7248\u672c\u200b\uff1a

Bash
pip 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

"}]} \ No newline at end of file +{"config":{"lang":["en","zh"],"separator":"[\\s\\u200b\\-]","pipeline":["stemmer"]},"docs":[{"location":"","title":"CHANfiG","text":""},{"location":"#introduction","title":"Introduction","text":"

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)

"},{"location":"#components","title":"Components","text":"

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.

"},{"location":"#flatdict","title":"FlatDict","text":"

FlatDict improves the default dict in 3 aspects.

"},{"location":"#dict-operations","title":"Dict Operations","text":"

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. FlatDictsupportssort 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.

"},{"location":"#ml-operations","title":"ML Operations","text":"

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.

"},{"location":"#io-operations","title":"IO Operations","text":"

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.

"},{"location":"#defaultdict","title":"DefaultDict","text":"

To facility the needs of default values, we incorporate DefaultDict which accepts default_factory and works just like a collections.defaultdict.

"},{"location":"#nesteddict","title":"NestedDict","text":"

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.

"},{"location":"#config","title":"Config","text":"

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.

"},{"location":"#registry","title":"Registry","text":"

Registry extends the NestedDict and supports register, lookup, and build to help you register constructors and build objects from a Config.

"},{"location":"#variable","title":"Variable","text":"

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.

"},{"location":"#configparser","title":"ConfigParser","text":"

ConfigParser extends ArgumentParser and provides parse and parse_config to parse command line arguments.

"},{"location":"#usage","title":"Usage","text":"

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.

Python
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:

Bash
python 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.

Bash
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:

YAML
activation: 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:

Bash
pip install chanfig\n

Install the latest version from source:

Bash
pip 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

  1. fun fact: time is always uncountable.\u00a0\u21a9

"},{"location":"config/","title":"Config","text":""},{"location":"config/#chanfig.config.Config","title":"Config","text":"

Bases: NestedDict

Config is an extension of NestedDict.

The differences between Config and NestedDict lies in 3 aspects:

  1. Config has default_factory set to Config and convert_mapping set to True by default.
  2. Config has a frozen attribute, which can be toggled with freeze(lock) & defrost(unlock) or temporarily changed with locked & unlocked.
  3. 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.

Notes

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 Description parser 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 Python
class 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 Python
def 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 Description self 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 Python
def 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 Default recursive bool True

Alias:

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 Python
def 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 Default name 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 Default recursive bool True

Alias:

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 Python
def 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 Default name Any required default Any None

Returns:

Name Type Description value Any

If Config does not contain name, return default. If default is not specified, return default_factory().

Raises:

Type Description KeyError

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 Python
def 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.

Source code in chanfig/config.py Python
def 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 Default args 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 Python
def 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 Default args 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 Python
def 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 Default name Any required default Any Null

Returns:

Name Type Description value 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 Description self 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 Python
def 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 Default name Any required value Any required convert_mapping bool | None

Whether to convert Mapping to NestedDict. Defaults to self.convert_mapping.

None

Raises:

Type Description ValueError

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.

Source code in chanfig/config.py Python
def 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 in chanfig/config.py Python
def 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 Description default_factory Optional[Callable]

Default factory for defaultdict behaviour.

Raises:

Type Description TypeError

If default_factory is not callable.

Notes

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 Python
class 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 Default name Any required

Raises:

Type Description ValueError

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 Python
def 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 Description indent int

Indentation level in printing and dumping to json or yaml.

Notes

FlatDict 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 Python
class 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.

See Also

all_items

Source code in chanfig/flat_dict.py Python
def 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.

See Also

all_keys

Source code in chanfig/flat_dict.py Python
def 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.

See Also

all_values

Source code in chanfig/flat_dict.py Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description FlatDict

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 Python
def 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 Description self 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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description FlatDict

Alias:

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 Python
def 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 Default name 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 Python
def 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 Default name 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 Python
def 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 Default cls Callable

Target class to be converted to.

dict

Returns:

Type Description Mapping 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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Default other Mapping | Iterable | PathStr required

Returns:

Type Description FlatDict

Alias:

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description FlatDict

Alias:

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description FlatDict 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, propertys are saved in __dict__, they will keep their original reference after calling this method.

Returns:

Type Description FlatDict 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 Python
def 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 Description FlatDict

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 Description FlatDict

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 Description FlatDict

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 Description FlatDict

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 Default name Any required default Any None

Returns:

Name Type Description value Any

If FlatDict does not contain name, return default.

Raises:

Type Description KeyError

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 Python
def 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 Default name str required default Any Null

Returns:

Name Type Description value Any

If FlatDict does not contain name, return default.

Raises:

Type Description AttributeError

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 Python
def 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 Description self FlatDict

Alias:

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 Python
def 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 Default name str required

Returns:

Type Description bool

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Default use_variable bool

Whether to convert values to Variable objects.

True interpolators MutableMapping | None

Mapping contains values for interpolation. Defaults to self.

None

Raises:

Type Description ValueError

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 Python
def 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 Default other Mapping | Iterable | PathStr required

Returns:

Type Description FlatDict

Alias:

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 Python
def 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 Python
def 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 Description str

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 Python
def 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 Default file File

File to load from.

required method str | None

File type, should be in JSON or YAML.

None

Returns:

Type Description FlatDict

Raises:

Type Description ValueError

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 Description self FlatDict

Alias:

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 Python
def 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 Default file File required *args Any

Passed to load.

() **kwargs Any

Passed to load.

{}

Returns:

Name Type Description self 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 Python
def 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 Default file 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 Description Generator[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 Description ValueError

If save to IO and method is not specified.

TypeError

If save to unsupported extension.

Alias:

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 Python
def 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 Default name 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 Python
def 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 Default name str required value Any required

Warns:

Type Description RuntimeWarning

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 Python
def 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 Description FlatDict

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 Python
def 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 Default cls str | device | dtype required

Returns:

Name Type Description self 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 Python
def 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 Description self FlatDict

Alias:

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Description TypeError

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 Python
def 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.

Source code in chanfig/flat_dict.py Python
def 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 Python
def 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 Description str

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 Python
def 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 Default obj Any

Object to be converted.

required

Returns:

Type Description Mapping[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 Python
def 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 Description ValueError

If save to IO and method is not specified.

TypeError

If save to unsupported extension.

Alias:

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 Python
def 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 Default file 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 Python
def 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 Default obj Any

Object to apply function.

required func 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 Description Any

Return value of func.

See Also

apply_: Apply an in-place operation.

Source code in chanfig/nested_dict.py Python
def 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 Default obj Any

Object to apply function.

required func 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 Description Any

Return value of func.

See Also

apply_: Apply a non-in-place operation.

Source code in chanfig/nested_dict.py Python
def 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 Description convert_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.

Notes

When 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 Python
class 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 Description Generator

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 Python
def 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 Description Generator

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 Python
def 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 Description Generator

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 Python
def 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.

Note

This method is meant for non-in-place modification of obj, for example, to.

Parameters:

Name Type Description Default func Callable required See Also

apply_: Apply an in-place operation. apply: Implementation of apply.

tionples

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 in chanfig/nested_dict.py Python
def 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.

Note

This method is meant for in-place modification of obj, for example, freeze.

Parameters:

Name Type Description Default func 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 Python
def 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 Default name 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 Python
def 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 Default other 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 Python
def 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 Default name Any required default Any None

Returns:

Name Type Description value Any

If NestedDict does not contain name, return default. If default is not specified, return default_factory().

Raises:

Type Description KeyError

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 Python
def 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 Default other 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 Python
def 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 Default name Any required default Any Null

Returns:

Name Type Description value 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 Python
def 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 Default name 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 Python
def 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 Default recursive bool

Whether to apply sort recursively.

True

Returns:

Type Description NestedDict

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 Python
def 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 Description TypeError

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 Python
def 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.

Source code in chanfig/parser.py Python
class 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:

  1. The base Config parsed into this method,
  2. The base config file located at the path of default_config (if specified),
  3. The config specified in arguments.

Higher levels override lower levels (i.e. 3 > 2 > 1).

Parameters:

Name Type Description Default args 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 Description config Config

The parsed Config.

Raises:

Type Description ValueError

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.

See Also

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.

Python Console Session
>>> 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.

Python Console Session
>>> 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.

Python Console Session
>>> 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.

Python Console Session
>>> 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.

Python Console Session
>>> 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 Python
def 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:

  1. The base Config parsed into this method,
  2. The base config file located at the path of default_config (if specified),
  3. The config specified in arguments.

Higher levels override lower levels (i.e. 3 > 2 > 1).

Parameters:

Name Type Description Default args 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 Description config Config

The parsed Config.

Raises:

Type Description ValueError

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.

See Also

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

Python Console Session
>>> 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 Python
def 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.

Notes

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 Python
class 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 Default name 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.

required *args Any

The arguments to pass to the component.

() **kwargs Any

The keyword arguments to pass to the component.

{}

Returns:

Type Description Any

Raises:

Type Description KeyError

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 Python
def 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 Default cls 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 Description Any

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 Default name str required

Returns:

Type Description Any

The component.

Raises:

Type Description KeyError

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 Python
def 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 Default component Any

The component to register.

None name Any | None

The name of the component.

None

Returns:

Name Type Description component Callable

The registered component. Registered component are expected to be Callable.

Raises:

Type Description ValueError

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 Python
def 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 in chanfig/utils.py Python
class 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.

Source code in chanfig/utils.py Python
class 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 in chanfig/utils.py Python
class 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 in chanfig/utils.py Python
class 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 in chanfig/utils.py Python
class 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 Default value 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 Description RuntimeError

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 Description value Any

The wrapped value.

dtype type

The type of the wrapped value.

Notes

Variable 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 Python
class 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.

"},{"location":"variable/#chanfig.Variable.value","title":"value: Any property writable","text":"

Fetch the object wrapped in Variable.

"},{"location":"variable/#chanfig.Variable.float","title":"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 Python
def 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.

Source code in chanfig/variable.py Python
def 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 Python
def 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.

Source code in chanfig/variable.py Python
def 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 Python
def 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 Default cls Callable

The type to convert to.

required

Examples:

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 Python
def 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 Python
def 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 in chanfig/variable.py Python
def 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 Python
def 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)

"},{"location":"zh/#_2","title":"\u7ec4\u4ef6","text":"

\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

"},{"location":"zh/#flatdict","title":"FlatDict","text":"

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

"},{"location":"zh/#_3","title":"\u5b57\u5178\u200b\u64cd\u4f5c","text":"

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

"},{"location":"zh/#_4","title":"\u673a\u5668\u200b\u5b66\u4e60\u200b\u64cd\u4f5c","text":"

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

"},{"location":"zh/#io","title":"IO \u200b\u64cd\u4f5c","text":"

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

"},{"location":"zh/#defaultdict","title":"DefaultDict","text":"

\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

"},{"location":"zh/#nesteddict","title":"NestedDict","text":"

\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

"},{"location":"zh/#config","title":"Config","text":"

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

"},{"location":"zh/#registry","title":"Registry","text":"

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

"},{"location":"zh/#variable","title":"Variable","text":"

\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

"},{"location":"zh/#configparser","title":"ConfigParser","text":"

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

"},{"location":"zh/#_5","title":"\u4f7f\u7528","text":"

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

Python
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

Bash
python 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

Bash
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

YAML
activation: 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

Bash
pip install chanfig\n

\u200b\u4ece\u200b\u6e90\u7801\u200b\u5b89\u88c5\u200b\u6700\u65b0\u200b\u7684\u200b\u7248\u672c\u200b\uff1a

Bash
pip 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

"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 57e99a92..b255f7f9 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,154 +2,154 @@ https://chanfig.danling.org/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/config/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/default_dict/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/flat_dict/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/functional/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/nested_dict/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/parser/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/registry/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/utils/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/variable/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/blog/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/config/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/default_dict/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/flat_dict/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/functional/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/nested_dict/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/parser/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/registry/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/utils/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/variable/ - 2023-09-14 + 2023-09-19 daily https://chanfig.danling.org/zh/blog/ - 2023-09-14 + 2023-09-19 daily diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 1a1ead77..f163236f 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ diff --git a/utils/index.html b/utils/index.html index 891d27c2..7c2ef1d0 100644 --- a/utils/index.html +++ b/utils/index.html @@ -38,7 +38,7 @@ - + @@ -46,7 +46,7 @@ - + diff --git a/variable/index.html b/variable/index.html index 3b667890..f2f4d137 100644 --- a/variable/index.html +++ b/variable/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/zh/blog/index.html b/zh/blog/index.html index 472e3a07..867c2d54 100644 --- a/zh/blog/index.html +++ b/zh/blog/index.html @@ -36,7 +36,7 @@ - + @@ -44,7 +44,7 @@ - + diff --git a/zh/config/index.html b/zh/config/index.html index a12ae75f..0c87305a 100644 --- a/zh/config/index.html +++ b/zh/config/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/zh/default_dict/index.html b/zh/default_dict/index.html index bb593c1f..0f801f1a 100644 --- a/zh/default_dict/index.html +++ b/zh/default_dict/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/zh/flat_dict/index.html b/zh/flat_dict/index.html index 5d710655..83c7a0cd 100644 --- a/zh/flat_dict/index.html +++ b/zh/flat_dict/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/zh/functional/index.html b/zh/functional/index.html index c76daaf8..a5ca801f 100644 --- a/zh/functional/index.html +++ b/zh/functional/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/zh/index.html b/zh/index.html index 70a26539..85f4ca54 100644 --- a/zh/index.html +++ b/zh/index.html @@ -38,7 +38,7 @@ - + @@ -46,7 +46,7 @@ - + diff --git a/zh/nested_dict/index.html b/zh/nested_dict/index.html index 3b0e0e6f..80f30a99 100644 --- a/zh/nested_dict/index.html +++ b/zh/nested_dict/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/zh/parser/index.html b/zh/parser/index.html index a88ed142..20280f69 100644 --- a/zh/parser/index.html +++ b/zh/parser/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/zh/registry/index.html b/zh/registry/index.html index d35e10d1..2bba17cb 100644 --- a/zh/registry/index.html +++ b/zh/registry/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/zh/utils/index.html b/zh/utils/index.html index 5f5fc416..b81e4196 100644 --- a/zh/utils/index.html +++ b/zh/utils/index.html @@ -38,7 +38,7 @@ - + @@ -46,7 +46,7 @@ - + diff --git a/zh/variable/index.html b/zh/variable/index.html index 42d1f90d..0f51994b 100644 --- a/zh/variable/index.html +++ b/zh/variable/index.html @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - +